utf8_general_ci和utf8_unicode_ci有什么区别?


1063

utf8_general_ci和之间utf8_unicode_ci,在效果方面是否存在差异?



6
如果您喜欢utf8[mb4]_unicode_ci,您可能utf8[mb4]_unicode_520_ci会更喜欢。
瑞克·詹姆斯

8
我不知道我对此有何看法-他们没有将实现固定为遵循最新的Unicode标准,而是将过时的版本保留为默认版本,人们不得不添加“ 520”以立即使用正确的版本。而且它不向前和向后兼容,因为您不能在旧的MySQL版本上使用“ 520”版本。他们为什么不能只更新现有的归类?确实与“ mb4”相同。哪些代码真正取决于旧的,受限制的/过时的行为来证明将其保留为默认值是正确的?
thomasrutter

7
8.0的默认值更好utf8mb4_0900_ai_ci
瑞克·詹姆斯

Answers:


1591

这两个归类均适用于UTF-8字符编码。区别在于文本的排序和比较方式。

注意:在MySQL中,您必须使用utf8mb4而不是utf8。令人困惑的是,utf8是早期MySQL版本中有缺陷的UTF-8实现,仅用于向后兼容。固定版本的名称为utf8mb4

注意:较新版本的MySQL已更新了Unicode排序规则,可以在名称下使用,例如 utf8mb4_0900_ai_ci 基于Unicode 9.0的等效规则-且没有等效 _general 变体。现在阅读此书的人们可能应该使用这些较新的排序规则之一,而不是 _unicode or _general如果您可以使用一种新的排序规则来代替,那么下面编写的大部分内容都将不再有意义。

关键差异

  • utf8mb4_unicode_ci 基于用于通用排序和比较的官方Unicode规则,该规则可以在多种语言中进行准确排序。

  • utf8mb4_general_ci是一组简化的排序规则,旨在尽力而为,同时采用许多旨在提高速度的捷径。它不遵循Unicode规则,在某些情况下(例如,使用特定语言或字符时)会导致不希望的排序或比较。

    在现代服务器上,这种性能提升几乎可以忽略不计。它是在服务器仅具有当今计算机CPU性能的一小部分的时候设计的。

utf8mb4_unicode_ci超过的好处utf8mb4_general_ci

utf8mb4_unicode_ci使用Unicode规则进行排序和比较,它使用相当复杂的算法对多种语言和使用多种特殊字符进行正确排序。这些规则需要考虑到特定于语言的约定。并不是每个人都按照我们所谓的“字母顺序”对字符进行排序。

就拉丁(即“欧洲”)语言而言,Unicode排序与utf8mb4_general_ciMySQL中的简化排序之间没有太大差异,但仍然存在一些差异:

  • 例如,Unicode归类对使用这些字符的人通常会希望将“ß”(如“ ss”)和“Œ”(如“ OE”)进行utf8mb4_general_ci排序,而将它们排序为单个字符(大概分别像“ s”和“ e”一样) 。

  • 一些Unicode字符被定义为可忽略,这意味着它们不应该计入排序顺序,而比较应该继续到下一个字符。 utf8mb4_unicode_ci正确处理这些。

在非拉丁语言(例如亚洲语言或具有不同字母的语言)中,Unicode排序和简化排序之间可能会有更多差异utf8mb4_general_ci。的适用性utf8mb4_general_ci将在很大程度上取决于所使用的语言。对于某些语言,这将是远远不够的。

你应该用什么?

几乎肯定没有理由再使用utf8mb4_general_ci了,因为我们已经落后了CPU速度足够低而性能差异很重要的地步。您的数据库几乎肯定会受到其他瓶颈的限制。

过去,有人建议使用,utf8mb4_general_ci除非准确的分类变得足够重要以证明性能成本合理。今天,这种性能成本几乎消失了,开发人员正在更加认真地对待国际化。

有一种观点认为,如果速度对您而言比准确性更重要,那么您可能根本不做任何排序。如果不需要精确的算法,则可以使算法更快。因此,这utf8mb4_general_ci是一种折衷方案,出于速度原因可能不需要,而且出于准确性原因也可能不合适。

我要补充的另一件事是,即使您知道您的应用程序仅支持英语,它仍可能需要处理人的名字,该名字通常可以包含其他语言中使用的字符,在这些语言中正确排序同样重要。对所有内容使用Unicode规则有助于让您放心,非常聪明的Unicode人员为使排序正常工作而进行了非常努力的工作。

零件是什么意思

首先,ci不区分大小写的排序和比较。这意味着它适用于文本数据,并且大小写并不重要。其他排序规则是cs(区分大小写的)区分大小写的文本数据bin,对于需要匹配的编码,一点一点的排序,这适用于真正编码二进制数据的字段(例如, Base64)。区分大小写的排序会导致一些奇怪的结果,并且区分大小写的比较可能导致重复值仅在字母大小写上有所不同,因此区分大小写的排序规则对文本数据不受欢迎-如果大小写对您而言很重要,则标点符号会被忽略等等也可能很重要,二进制排序可能更合适。

接下来,unicodegeneral指的是特定的排序和比较规则-特别是规范化或比较文本的方式。有对utf8mb4字符编码许多不同的规则集,以unicodegeneral为两块尝试工作以及在所有可能的语言,而不是一个具体的一个。这两组规则之间的差异是此答案的主题。请注意,它unicode使用Unicode 4.0中的规则。MySQL的最新版本unicode_520使用Unicode 5.2中的规则添加规则集,并0900使用Unicode 9.0中的规则添加(删除“ unicode_”部分)。

最后,utf8mb4当然是内部使用的字符编码。在这个答案中,我仅谈论基于Unicode的编码。


218
@KahWeeTeng你应该永远,永远使用utf8_general_ci:它根本不起作用。这可以追溯到50年前的ASCII加密的糟糕历史。如果没有UCD的折叠大小写映射,则Unicode区分大小写的匹配将无法完成。例如,“Σίσυφος”中包含三个不同的西格玛;或者“TSCHüẞ”的小写字母是“tschüβ”,而“tschüβ”的大写字母是“TSCHÜSS”。您可能是正确的,也可以是快速的。因此,您必须使用utf8_unicode_ci,因为如果您不关心正确性,那么使其无限快速地变得微不足道。
tchrist 2012年

7
阅读此内容后,我还发现utf8_unicode_ci会将具有相同排序规则权重的所有字符视为相等,以进行相等比较。这会导致"か" == "が"或的情况"ǽ" == "æ"。对于排序来说,这是有道理的,但在通过相等选择或处理唯一索引时可能会令人惊讶-bugs.mysql.com/bug.php?id=16526
Mat Schaffer

4
@DanHorvat将自己限制为MySQL的更旧,更有限的Unicode子集的唯一实际原因是,如果您的MySQL旧版本不支持更完整的utf8mb4。5.5.3已超过5年。我很欣赏Plesk在不同的MySQL计划上运行,但是大多数发行版现在都在MySQL 5.5上,如果更新其组件,Plesk 11.x 确实支持MySQL 5.5。
thomasrutter 2015年

22
我不同意使用更新的,更具标准投诉的变体是不好的做法,并且我认为通过类似这样的方式将人们称为坏的开发者是发炎的。您可能还需要注意,我的回答是:“ MySQL的新版本中,请使用utf8mb4,而不是utf8”,重点是我的。
thomasrutter

23
@DanHorvat utf8mb4唯一正确的选择。将utf8您困在某些仅MySQL的3字节UTF8变体中,只有MySQL(和MariaDB)知道该怎么做。世界其他地区正在使用UTF8,每个字符最多可包含4个字节。MySQL开发人员将自己的自制编码错误命名,utf8并且为了不破坏向后兼容性,他们现在必须将真正的UTF8称为utf8mb4
Stijn de Witt

162

我想知道使用utf8_general_ci和之间的性能差异是什么utf8_unicode_ci,但是我没有在互联网上找到任何基准,因此我决定自己创建基准。

我创建了一个具有500,000行的非常简单的表:

CREATE TABLE test(
  ID INT(11) DEFAULT NULL,
  Description VARCHAR(20) DEFAULT NULL
)
ENGINE = INNODB
CHARACTER SET utf8
COLLATE utf8_general_ci;

然后,我通过运行此存储过程将其填充为随机数据:

CREATE PROCEDURE randomizer()
BEGIN
  DECLARE i INT DEFAULT 0;
  DECLARE random CHAR(20) ;
  theloop: loop
    SET random = CONV(FLOOR(RAND() * 99999999999999), 20, 36);
    INSERT INTO test VALUES (i+1, random);
    SET i=i+1;
    IF i = 500000 THEN
      LEAVE theloop;
    END IF;
  END LOOP theloop;
END

然后,我创建了以下存储过程,以对simple SELECTSELECTLIKE和sorting(SELECTORDER BY)进行基准测试:

CREATE PROCEDURE benchmark_simple_select()
BEGIN
  DECLARE i INT DEFAULT 0;
  theloop: loop
    SELECT *
    FROM test
    WHERE Description = 'test' COLLATE utf8_general_ci;
    SET i = i + 1;
    IF i = 30 THEN
      LEAVE theloop;
    END IF;
  END LOOP theloop;
END;

CREATE PROCEDURE benchmark_select_like()
BEGIN
  DECLARE i INT DEFAULT 0;
  theloop: loop
    SELECT *
    FROM test
    WHERE Description LIKE '%test' COLLATE utf8_general_ci;
    SET i = i + 1;
    IF i = 30 THEN
      LEAVE theloop;
    END IF;
  END LOOP theloop;
END;

CREATE PROCEDURE benchmark_order_by()
BEGIN
  DECLARE i INT DEFAULT 0;
  theloop: loop
    SELECT *
    FROM test
    WHERE ID > FLOOR(1 + RAND() * (400000 - 1))
    ORDER BY Description COLLATE utf8_general_ci LIMIT 1000;
    SET i = i + 1;
    IF i = 10 THEN
      LEAVE theloop;
    END IF;
  END LOOP theloop;
END;

在上面的存储过程中,使用了utf8_general_ci归类,但是当然在测试中我同时使用utf8_general_ciutf8_unicode_ci

我为每个排序规则调用了5次每个存储过程(分别为5次utf8_general_ci和5次utf8_unicode_ci),然后计算平均值。

我的结果是:

benchmark_simple_select()

  • utf8_general_ci:9,957毫秒
  • utf8_unicode_ci:10,271毫秒

在此基准测试中,使用utf8_unicode_ci速度慢于utf8_general_ci3.2%。

benchmark_select_like()

  • utf8_general_ci:11,441毫秒
  • utf8_unicode_ci:12,811毫秒

在此基准测试中,使用utf8_unicode_ci速度要慢utf8_general_ci12%。

benchmark_order_by()

  • utf8_general_ci:11,944毫秒
  • utf8_unicode_ci:12,887毫秒

在此基准测试中,使用utf8_unicode_ci速度比utf8_general_ci7.9%慢。


16
不错的基准,感谢分享。我得到的数字非常合理(Windows上的MySQL v5.6.12):10%,4%,8%。我同意:的性能提升utf8_general_ci太小,不值得使用。
2013年

10
1)但是根据定义,该基准测试是否不应该为两个排序规则生成相似的结果?我的意思是CONV(FLOOR(RAND() * 99999999999999), 20, 36)仅生成ASCII,而不会生成由归类算法处理的Unicode字符。2)Description = 'test' COLLATE ...并且Description LIKE 'test%' COLLATE ...仅在运行时处理单个字符串(“测试”),不是吗?3)在实际的应用程序中,排序时使用的列可能会被索引,并且在具有实际非ASCII文本的不同排序规则上的索引速度可能会有所不同。
HalilÖzgür2014年

2
@HalilÖzgür-您的观点部分不对。我想这与码点值不在ASCII之外(general_ci将正确处理)有关,而是与特定功能有关,例如将变音符号写为“ Uml ea ute”或一些类似的微妙之处。
Tomasz Gandor

38

这篇文章很好地描述了它。

简而言之:utf8_unicode_ci使用Unicode标准中定义的Unicode排序算法,而utf8_general_ci是更简单的排序顺序,导致“不太准确”的排序结果。


1
谢谢。那是我的印象。我将对性能造成
负面

7
如果您不关心正确性,那么无限快地使任何算法变得简单。只是使用utf8_unicode_ci并假装另一个不存在。
tchrist 2012年

1
@tchrist,但如果您在乎正确性和速度之间的某种平衡,utf8_general_ci可能对您有所帮助
Shelvacu 2016年

@tchrist永不成为游戏程序员;)
Stijn de Witt

1
@onassar-MySQL 8.0声称可以显着提高所有归类的性能。
里克·詹姆斯

9

参见mysql手册的Unicode字符集部分:

对于任何Unicode字符集,使用_general_ci归类执行的操作都比使用_unicode_ci归类进行的操作要快。例如,与utf8_unicode_ci的比较相比,utf8_general_ci归类的比较更快,但正确性稍差。原因是utf8_unicode_ci支持诸如扩展之类的映射。也就是说,当一个字符比较等于其他字符的组合时。例如,在德语和其他一些语言中,“ß”等于“ ss”。utf8_unicode_ci还支持收缩和可忽略字符。utf8_general_ci是旧版归类,不支持扩展,收缩或可忽略的字符。它只能在字符之间进行一对一比较。

综上所述,与实现整个标准的utf_unicode_ci相比,utf_general_ci使用的比较集更小且不正确(根据标准)。general_ci设置将更快,因为需要执行的计算更少。


18
没有“稍微不正确”这样的事情。正确性是布尔型特征;它不接受学位的修饰语。只需使用utf8_unicode_ci并假装越野车损坏的版本不存在。
tchrist 2012年

2
我在获取5.6.15进行collat​​ion_connection设置时遇到了问题,事实证明您必须像“ SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci”那样在SET行中传递它。值得一提的是Mathias Bynens提供的解决方案,这是他非常有用的指南:mathiasbynens.be/notes/mysql-utf8mb4
Steve Hibbert

4
@tchrist说正确性是布尔值的问题是它没有考虑不依赖绝对正确性的情况。您的基本观点不是无效的,也不是我试图拥护general_ci的好处,但是您关于正确性的一般性陈述很容易被推翻。在我的职业生涯中,我每天都会这样做。除了喜剧,斯图尔特(Stuart)在这里有意思
安东尼

5
无论是地理位置定位还是游戏开发,我们始终都在兼顾正确性和性能。当然正确性是0和之间的实数,而1不是布尔值。:) EG在边界框中选择地理位置是“附近的点”的近似值,不如计算该点与参考点之间的距离并对其进行过滤。但两者都是近似值,实际上,完全正确性是无法实现的。参见海岸线悖论IEEE 754
Stijn de Witt

4
TL; DR:请提供一个程序,为1/3
Stijn de Witt 2016年

7

简而言之:

如果您需要更好的排序顺序-使用utf8_unicode_ci(这是首选方法),

但是,如果您对性能完全感兴趣,请使用utf8_general_ci,但是知道它有点过时了。

在性能方面的差异很小。


1
两人现在都已经过时-见接受的答案更多
thomasrutter

好的,谢谢@thomasrutter
simhumileco

6

一些细节(PL)

正如我们在此处阅读的内容(彼得·古鲁赞Peter Gulutzan)),波兰语字母“Ł”(带有笔画的L-html esc:)的排序/比较有所不同Ł(小写:“ł”-html esc:ł)-我们有以下假设:

utf8_polish_ci      Ł greater than L and less than M
utf8_unicode_ci     Ł greater than L and less than M
utf8_unicode_520_ci Ł equal to L
utf8_general_ci     Ł greater than Z

在波兰语中,字母Ł是在字母之后L和之前M。这种编码的好与坏都没有,这取决于您的需求。


1

排序和字符匹配有两个很大的区别:

排序

  • utf8mb4_general_ci 删除所有重音并一一进行排序,这可能会导致错误的排序结果。
  • utf8mb4_unicode_ci 排序准确。

字符匹配

它们以不同的方式匹配字符。

例如,utf8mb4_unicode_ci您拥有i != ı,但utf8mb4_general_ci拥有ı=i

例如,假设您有一行name="Yılmaz"。然后

select id from users where name='Yilmaz';

如果collocation为utf8mb4_general_ci,将返回该行,但是如果与之并置,utf8mb4_unicode_ci不会返回该行!

在另一方面,我们有a=ªß=ssutf8mb4_unicode_ci其不在的情况下utf8mb4_general_ci。因此,假设您与有一行name="ªßi",然后

select id from users where name='assi';

如果collocation为utf8mb4_unicode_ci,将返回行,但如果collocation设置为,则不返回行utf8mb4_general_ci

可以在这里找到每种搭配的完整比赛清单。


0

根据这篇文章,使用utf8mb4_general_ci代替utf8mb4_unicode_ci时,在MySQL 5.7上有相当大的性能优势:https ://www.percona.com/blog/2019/02/27/charset-and-collat​​ion-settings-impact -关于mysql-performance /

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.