utf8_general_ci和utf8_unicode_ci之间有什么区别?

问题描述 投票:926回答:6

utf8_general_ciutf8_unicode_ci之间,性能方面有什么不同吗?

mysql unicode utf-8 collation character-set
6个回答
1487
投票

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

注意:从MySQL 5.5.3开始,你应该使用utf8mb4而不是utf8。它们都引用了UTF-8编码,但较旧的utf8具有MySQL特定的限制,阻止使用编号高于0xFFFD的字符。

关键差异

  • utf8mb4_unicode_ci基于官方Unicode规则进行通用排序和比较,可以在各种语言中进行准确排序。
  • utf8mb4_general_ci是一套简化的排序规则,旨在尽可能地采取措施,同时采取旨在提高速度的许多捷径。它不遵循Unicode规则,并且在某些情况下会导致不合需要的排序或比较,例如在使用特定语言或字符时。 在现代服务器上,这种性能提升几乎可以忽略不计。它是在服务器只占当今计算机CPU性能的一小部分时设计的。

注意:现在有一个名为utf8mb4_unicode_ciutf8mb4_0900_ai_ci的更新版本 - 这是基于Unicode版本9.0的更改,并且显然也更快。它采用了一种新的命名方案,其中0900是Unicode版本,ai意味着不区分重音 - 就像之前的utf8mb4_unicode_ci一样,字母中的重音不被认为是重要的。

utf8mb4_unicode_ciutf8mb4_general_ci的好处

utf8mb4_unicode_ci使用Unicode规则进行排序和比较,使用相当复杂的算法在各种语言中进行正确排序,并且使用各种特殊字符时。这些规则需要考虑特定语言的惯例;不是每个人都按照我们称之为“字母顺序”的方式对他们的角色进行排序。

就拉丁语(即“欧洲”)语言而言,MySQL中的Unicode排序和简化的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 9.0的0900和引用Unicode 5.2的unicode_520

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


150
投票

我想知道使用utf8_general_ciutf8_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

然后我创建了以下存储过程来基准简单的SELECTSELECTLIKE,以及排序(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次(utf8_general_ci为5次,utf8_unicode_ci为5次),然后计算平均值。

我的结果是:

benchmark_simple_select()

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

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

benchmark_select_like()

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

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

benchmark_order_by()

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

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


36
投票

This post非常好地描述了它。

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


8
投票

请参阅mysql手册,Unicode Character Sets部分:

对于任何Unicode字符集,使用_general_ci排序规则执行的操作比_unicode_ci排序规则更快。例如,与utf8_unicode_ci的比较相比,utf8_general_ci整理的比较更快,但更不正确。原因是utf8_unicode_ci支持扩展等映射;也就是说,当一个字符与其他字符的组合相等时。例如,在德语和其他一些语言中,“ß”等于“ss”。 utf8_unicode_ci还支持收缩和可忽略的字符。 utf8_general_ci是一种遗留的排序规则,不支持扩展,收缩或可忽略的字符。它只能在字符之间进行一对一的比较。

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


6
投票

简而言之:

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

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

性能方面的差异非常小。


2
投票

一些细节(PL)

我们可以阅读here(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之前。这种编码没有一个更好或更差 - 这取决于您的需求。

© www.soinside.com 2019 - 2024. All rights reserved.