协慌网

登录 贡献 社区

utf8_general_ci 和 utf8_unicode_ci 之间有什么区别?

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

答案

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

就拉丁语(即 “欧洲”)语言utf8mb4_general_ci ,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取决于所使用的语言。对于某些语言来说,这将是非常不合适的。

你应该用什么?

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

性能上的差异只能在非常特殊的情况下才能衡量,如果是你,你可能已经知道了。如果您遇到的排序速度很慢,几乎在所有情况下都会出现索引 / 查询计划的问题。在要排除故障的事项列表中,更改排序规则功能不应该很高。

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

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

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

然后我创建了以下存储过程来基于简单的SELECTSELECT with LIKE和排序( SELECT with ORDER 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 collation,但当然在测试期间我使用了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%。

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

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