在utf8_general_ci
和utf8_unicode_ci
之间,性能方面有什么不同吗?
这两个排序规则都是UTF-8字符编码。不同之处在于文本的排序和比较方式。
注意:从MySQL 5.5.3开始,你应该使用utf8mb4
而不是utf8
。它们都引用了UTF-8编码,但较旧的utf8
具有MySQL特定的限制,阻止使用编号高于0xFFFD的字符。
关键差异
utf8mb4_unicode_ci
基于官方Unicode规则进行通用排序和比较,可以在各种语言中进行准确排序。utf8mb4_general_ci
是一套简化的排序规则,旨在尽可能地采取措施,同时采取旨在提高速度的许多捷径。它不遵循Unicode规则,并且在某些情况下会导致不合需要的排序或比较,例如在使用特定语言或字符时。
在现代服务器上,这种性能提升几乎可以忽略不计。它是在服务器只占当今计算机CPU性能的一小部分时设计的。注意:现在有一个名为utf8mb4_unicode_ci
的utf8mb4_0900_ai_ci
的更新版本 - 这是基于Unicode版本9.0的更改,并且显然也更快。它采用了一种新的命名方案,其中0900
是Unicode版本,ai
意味着不区分重音 - 就像之前的utf8mb4_unicode_ci
一样,字母中的重音不被认为是重要的。
utf8mb4_unicode_ci
对utf8mb4_general_ci
的好处
utf8mb4_unicode_ci
使用Unicode规则进行排序和比较,使用相当复杂的算法在各种语言中进行正确排序,并且使用各种特殊字符时。这些规则需要考虑特定语言的惯例;不是每个人都按照我们称之为“字母顺序”的方式对他们的角色进行排序。
就拉丁语(即“欧洲”)语言而言,MySQL中的Unicode排序和简化的utf8mb4_general_ci
排序没有太大区别,但仍然存在一些差异:
utf8mb4_general_ci
将它们分类为单个字符(可能分别为“s”和“e”) )。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)。区分大小写的排序会导致一些奇怪的结果,并且区分大小写的比较可能会导致重复值仅在字母大小写中不同,因此区分大小写的排序规则对文本数据不利 - 如果案例对您很重要,那么否则会忽略标点符号等等可能也很重要,二进制整理可能更合适。
接下来,unicode
或general
指的是特定的排序和比较规则 - 特别是文本的标准化或比较方式。 utf8mb4字符编码有许多不同的规则集,其中unicode
和general
是两种,它们试图在所有可能的语言中运行良好,而不是一种特定的语言。这两套规则之间的差异是这个答案的主题。请注意,较新的规则集包括引用Unicode 9.0的0900
和引用Unicode 5.2的unicode_520
。
最后,utf8mb4
当然是内部使用的字符编码。在这个答案中,我只谈论基于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
和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毫秒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%。
This post非常好地描述了它。
简而言之:utf8_unicode_ci使用Unicode标准中定义的Unicode排序算法,而utf8_general_ci是一种更简单的排序顺序,导致“不太准确”的排序结果。
请参阅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集会更快,因为计算量较少。
简而言之:
如果您需要更好的排序顺序 - 使用utf8_unicode_ci
(这是首选方法),
但如果你对性能完全感兴趣 - 使用utf8_general_ci
,但要知道它有点过时了。
性能方面的差异非常小。
我们可以阅读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
之前。这种编码没有一个更好或更差 - 这取决于您的需求。