utf8_general_ci和utf8_unicode_ci有什么区别?

utf8_general_ciutf8_unicode_ci之间,在性能方面有什么差异吗?

554339 次浏览

请参阅mysql手册,Unicode字符集部分:

对于任何Unicode字符集,使用_general_ci排序规则比_unicode_ci排序规则更快。例如,比较utf8_general_ci整理更快,但不太正确,比utf8_unicode_ci的比较原因是utf8_unicode_ci支持这样的映射作为扩展;也就是说,当一个字符比较等于其他字符的组合。对于例如,在德国和其他一些语言的“β”等于“ss”。utf8_unicode_ci还支持缩写和无知的字符。utf8_general_ci是遗产整理不支持扩展,缩写,或无知的字符。它只能使一对一字符之间的比较。

总而言之,utf_general_ci使用了一个比utf_unicode_ci更小、更不正确的(根据标准)比较集,应该实现了整个标准。general_ci集会更快,因为要做的计算更少。

对于那些在2020年或以后仍在回答这个问题的人来说,有一些更新的选项可能比这两个选项都更好。例如,utf8_unicode_520_ci

所有这些排序规则都是针对UTF-8字符编码的。不同之处在于文本的排序和比较方式。

_unicode_ci_general_ci是根据我们期望的方式对文本进行排序和比较的两套不同的规则。较新版本的MySQL也引入了新的规则集,例如_unicode_520_ci用于基于Unicode 5.2的等效规则,或者MySQL 8. x特定的_0900_ai_ci用于基于Unicode 9.0的等效规则(并且没有等效的_general_ci变体)。现在阅读这篇文章的人可能应该使用这些较新的排序规则之一,而不是_unicode_ci_general_ci。下面对这些旧排序规则的描述仅供参考。

MySQL目前正在从旧的、有缺陷的UTF-8实现过渡。目前,您需要在字符编码部分使用utf8mb4而不是utf8,以确保您获得的是固定版本。有缺陷的版本仍然是向后兼容,尽管它已被弃用。

主要区别

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

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

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

utf8mb4_unicode_ciutf8mb4_general_ci的好处

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

就拉丁(即“欧洲”)语言而言,Unicode排序和MySQL中简化的utf8mb4_general_ci排序之间没有太大区别,但仍有一些区别:

  • 例如,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和MariaDB使用Unicode 5.2中的规则添加规则集unicode_520,MySQL 8. x使用Unicode 9.0中的规则添加0900(删除“unicode_”部分)。

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

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

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

我想知道使用utf8_general_ciutf8_unicode_ci之间的性能差异,但我没有在互联网上找到任何基准,所以我决定自己创建基准。

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

CREATE TABLE test(ID INT(11) DEFAULT NULL,Description VARCHAR(20) DEFAULT NULL)ENGINE = INNODBCHARACTER SET utf8COLLATE utf8_general_ci;

然后我通过运行这个存储过程用随机数据填充它:

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

然后我创建了以下存储过程来基准测试简单的SELECTSELECTLIKE,以及排序(SELECTORDER BY):

CREATE PROCEDURE benchmark_simple_select()BEGINDECLARE i INT DEFAULT 0;theloop: loopSELECT *FROM testWHERE Description = 'test' COLLATE utf8_general_ci;SET i = i + 1;IF i = 30 THENLEAVE theloop;END IF;END LOOP theloop;END;
CREATE PROCEDURE benchmark_select_like()BEGINDECLARE i INT DEFAULT 0;theloop: loopSELECT *FROM testWHERE Description LIKE '%test' COLLATE utf8_general_ci;SET i = i + 1;IF i = 30 THENLEAVE theloop;END IF;END LOOP theloop;END;
CREATE PROCEDURE benchmark_order_by()BEGINDECLARE i INT DEFAULT 0;theloop: loopSELECT *FROM testWHERE ID > FLOOR(1 + RAND() * (400000 - 1))ORDER BY Description COLLATE utf8_general_ci LIMIT 1000;SET i = i + 1;IF i = 10 THENLEAVE theloop;END IF;END LOOP theloop;END;

在上面的存储过程中使用了utf8_general_ci排序规则,但当然在测试过程中我同时使用了utf8_general_ciutf8_unicode_ci

我为每个归类调用每个存储过程5次(utf8_general_ci调用5次,utf8_unicode_ci调用5次),然后计算平均值。

我的成果是:

benchmark_simple_select()

  • utf8_general_ci:9,957 ms
  • utf8_unicode_ci:10,271 ms

在这个基准测试中,使用utf8_unicode_ciutf8_general_ci慢3.2%。

benchmark_select_like()

  • utf8_general_ci:11,441 ms
  • utf8_unicode_ci:12,811 ms

在这个基准测试中,使用utf8_unicode_ciutf8_general_ci慢12%。

benchmark_order_by()

  • utf8_general_ci:11,944 ms
  • utf8_unicode_ci:12,887 ms

在这个基准测试中,使用utf8_unicode_ciutf8_general_ci慢7.9%。

简言之:

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

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

性能方面的差异非常小。

一些细节(PL)

正如我们可以读到的这里彼得·古鲁赞),在排序/比较波兰字母“”(L带笔画-html esc:&彼得·古鲁赞21;)(小写: "ł" - html esc:&彼得·古鲁赞22;)方面存在差异-我们有以下假设:

utf8_polish_ci      Ł greater than L and less than Mutf8_unicode_ci     Ł greater than L and less than Mutf8_unicode_520_ci Ł equal to Lutf8_general_ci     Ł greater than Z

在波兰语中,字母Ł在字母L之后和字母M之前。这种编码没有一个更好或更差-这取决于您的需求。

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

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

排序

  • utf8mb4_general_ci删除所有重音并逐个排序,这可能会产生不正确的排序结果。
  • utf8mb4_unicode_ci排序准确。

字符匹配

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

例如,在utf8mb4_unicode_ci中,你有i != ı,但在utf8mb4_general_ci中,它有ı=i

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

select id from users where name='Yilmaz';

如果并置为utf8mb4_general_ci,将返回该行,但如果它与utf8mb4_unicode_ci并置,则没有将返回该行!

另一方面,我们在utf8mb4_unicode_ci中有a=ªß=ss,而在utf8mb4_general_ci中不是这样。所以假设你有一行name="ªßi",然后

select id from users where name='assi';

如果搭配设置为utf8mb4_unicode_ci,则将返回行,但如果搭配设置为utf8mb4_general_ci,则没有将返回行。

可以找到每个搭配的完整匹配列表这里