这两个排序规则都是 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_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
然后我创建了以下存储过程来基于简单的SELECT
, SELECT
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_ci
和utf8_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_ci
比utf8_general_ci
慢 3.2%。
benchmark_select_like()
utf8_general_ci
:11,441 毫秒utf8_unicode_ci
:12,811 毫秒在此基准测试中,使用utf8_unicode_ci
比utf8_general_ci
慢 12%。
benchmark_order_by()
utf8_general_ci
:11,944 毫秒utf8_unicode_ci
:12,887 毫秒在此基准测试中,使用utf8_unicode_ci
比utf8_general_ci
慢 7.9%。
这篇文章很好地描述了它。
简而言之:utf8_unicode_ci 使用 Unicode 标准中定义的 Unicode 排序算法,而 utf8_general_ci 是一种更简单的排序顺序,导致 “不太准确” 的排序结果。