utf8_general_ci和utf8_unicode_ci之间有什么区别?

时间:2009-04-20 03:43:49

标签: mysql unicode utf-8 collation character-set

utf8_general_ciutf8_unicode_ci之间,性能方面是否存在差异?

8 个答案:

答案 0 :(得分:1429)

这两个排序规则都适用于UTF-8字符编码。不同之处在于如何对文本进行排序和比较。

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

  • <强>精度

    utf8mb4_unicode_ci基于Unicode标准进行排序和比较,可以在各种语言中进行准确排序。

    utf8mb4_general_ci无法实现所有Unicode排序规则,这会在某些情况下导致不合理的排序,例如使用特定语言或字符时。

  • <强>性能

    utf8mb4_general_ci在比较和排序方面更快,因为它需要一堆与性能相关的快捷方式。

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

    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,除非准确的排序非常重要,足以证明性能成本合理。今天,性能成本几乎消失了,开发人员正在更加认真地对待国际化。

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

答案 1 :(得分:145)

我想知道使用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 ms
  • utf8_unicode_ci:10,271 ms

在此基准测试中,使用utf8_unicode_ci的速度比utf8_general_ci慢3.2%。

<强> benchmark_select_like()

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

在此基准测试中,使用utf8_unicode_ci的速度比utf8_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%。

答案 2 :(得分:36)

This post很好地描述了它。

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

答案 3 :(得分:6)

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

  

对于任何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集会更快,因为计算量较少。

答案 4 :(得分:6)

简而言之:

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

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

性能方面的差异很小。

答案 5 :(得分:2)

某些详细信息(PL)

我们可以读到here Peter Gulutzan ),对波兰字母“Ł”(带有笔划的L-html esc:&#321;)进行排序/比较存在差异小写:“ł”-html esc:&#322;)-我们有以下假设:

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之前。这种编码的好与坏都没有,这取决于您的需求。

答案 6 :(得分:0)

根据这篇文章,当使用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/

答案 7 :(得分:0)

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

排序

  • 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并置,则将返回该行!

另一方面,我们在a=ª中有ß=ssutf8mb4_unicode_ci,而在utf8mb4_general_ci中则不是。因此,假设您有name="ªßi",然后

select id from users where name='assi';

如果并置为utf8mb4_unicode_ci,将返回行,但如果将并置设置为utf8mb4_general_ci,将返回行。

可以找到每种搭配的完整匹配列表here