在utf8_general_ci
和utf8_unicode_ci
之间,性能方面是否存在差异?
答案 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_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
和LIKE
进行基准测试,并使用SELECT
对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
中使用了排序规则,但在测试过程中我使用了utf8_general_ci
和utf8_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_ci
比utf8_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)
我们可以读到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
之前。这种编码的好与坏都没有,这取决于您的需求。
答案 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=ª
中有ß=ss
和utf8mb4_unicode_ci
,而在utf8mb4_general_ci
中则不是。因此,假设您有name="ªßi"
,然后
select id from users where name='assi';
如果并置为utf8mb4_unicode_ci
,将返回行,但如果将并置设置为utf8mb4_general_ci
,将不返回行。
可以找到每种搭配的完整匹配列表here。