utf8_general_ci
和之间utf8_unicode_ci
,在性能方面有什么区别吗?
8 回答
对于那些在 2020 年或之后仍会遇到这个问题的人,有可能比这两个更好的新选项。例如,utf8mb4_0900_ai_ci
。
所有这些排序规则都用于 UTF-8 字符编码。不同之处在于文本的排序和比较方式。
_unicode_ci
并且_general_ci
是根据我们期望的方式对文本进行排序和比较的两组不同的规则。较新版本的 MySQL 也引入了新的规则集,例如_0900_ai_ci
基于 Unicode 9.0 的等效规则 - 并且没有等效_general_ci
变体。现在阅读本文的人可能应该使用这些较新的排序规则之一,而不是使用_unicode_ci
or _general_ci
。下面对那些较旧的排序规则的描述仅供参考。
MySQL 目前正在从旧的、有缺陷的 UTF-8 实现过渡。现在,您需要使用utf8mb4
而不是utf8
字符编码部分,以确保您获得的是固定版本。有缺陷的版本仍然是为了向后兼容,尽管它已被弃用。
主要区别
utf8mb4_unicode_ci
基于官方 Unicode 规则进行通用排序和比较,可在多种语言中准确排序。utf8mb4_general_ci
是一组简化的排序规则,旨在尽其所能,同时采取许多旨在提高速度的捷径。它不遵循 Unicode 规则,并且在某些情况下会导致不希望的排序或比较,例如在使用特定语言或字符时。在现代服务器上,这种性能提升几乎可以忽略不计。它是在服务器的 CPU 性能只有当今计算机的一小部分时设计的。
utf8mb4_unicode_ci
超过的好处utf8mb4_general_ci
utf8mb4_unicode_ci
,它使用 Unicode 规则进行排序和比较,采用相当复杂的算法在各种语言中以及在使用各种特殊字符时正确排序。这些规则需要考虑特定语言的约定;不是每个人都按照我们所说的“字母顺序”对他们的字符进行排序。
就拉丁语(即“欧洲”)语言而言,Unicode 排序和 MySQL 中的简化排序没有太大区别utf8mb4_general_ci
,但仍有一些区别:
例如,Unicode 排序规则将“ß”排序为“ss”,将“Œ”排序为“OE”,因为使用这些字符的人通常需要这些字符,而将
utf8mb4_general_ci
它们排序为单个字符(大概分别像“s”和“e”) .一些 Unicode 字符被定义为可忽略,这意味着它们不应该计入排序顺序,并且比较应该转到下一个字符。
utf8mb4_unicode_ci
妥善处理这些。
在非拉丁语言中,例如亚洲语言或具有不同字母表的语言,Unicode 排序和简化排序之间可能存在更多差异utf8mb4_general_ci
。will的适用性在utf8mb4_general_ci
很大程度上取决于所使用的语言。对于某些语言,这将是非常不充分的。
你应该用什么?
几乎可以肯定没有理由再使用utf8mb4_general_ci
了,因为我们已经忽略了 CPU 速度足够低以至于性能差异很重要的点。您的数据库几乎肯定会受到除此之外的其他瓶颈的限制。
过去,有些人建议使用,utf8mb4_general_ci
除非准确排序足够重要以证明性能成本是合理的。如今,这种性能成本几乎消失了,开发人员正在更加认真地对待国际化。
有一个论点是,如果速度对您来说比准确性更重要,那么您可能根本不进行任何排序。如果您不需要准确的算法,那么使算法更快是微不足道的。因此,utf8mb4_general_ci
出于速度原因可能不需要妥协,也可能出于准确性原因也不适合。
我要补充的另一件事是,即使您知道您的应用程序只支持英语,它可能仍需要处理人名,这些人名通常包含其他语言中使用的字符,在这些语言中正确排序同样重要. 对所有事情都使用 Unicode 规则有助于让您更加安心,因为非常聪明的 Unicode 人员已经非常努力地工作以使排序正常工作。
零件是什么意思
首先,ci
用于不区分大小写的排序和比较。这意味着它适用于文本数据,大小写并不重要。其他类型的排序规则是cs
(区分大小写)对于大小写很重要的文本数据bin
,对于需要匹配编码的情况,逐位匹配,这适用于真正编码二进制数据的字段(包括,例如, Base64)。区分大小写的排序会导致一些奇怪的结果,区分大小写的比较可能会导致重复值仅在字母大小写上有所不同,因此区分大小写的排序规则对文本数据不受欢迎 - 如果大小写对您很重要,那么标点符号就可以忽略等等可能也很重要,二进制排序规则可能更合适。
接下来,unicode
orgeneral
指的是具体的排序和比较规则——特别是文本被规范化或比较的方式。utf8mb4 字符编码有许多不同的规则集,其中有unicode
两个规则general
试图在所有可能的语言中都很好地工作,而不是一种特定的语言。这两组规则之间的差异是此答案的主题。请注意,unicode
使用 Unicode 4.0 中的规则。MySQL 的最新版本unicode_520
使用 Unicode 5.2 中的规则添加规则集,并且0900
(删除“unicode_”部分)使用 Unicode 9.0 中的规则。
最后,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
然后我创建了以下存储过程来对 simple SELECT
、SELECT
withLIKE
和排序(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
中使用了排序规则,但当然在测试期间我同时使用了utf8_general_ci
和utf8_unicode_ci
.
我为每个排序规则调用了每个存储过程 5 次(5 次 forutf8_general_ci
和 5 次 for utf8_unicode_ci
),然后计算平均值。
我的结果是:
benchmark_simple_select()
- 与
utf8_general_ci
:9,957 毫秒 - : 10,271
utf8_unicode_ci
毫秒
在这个基准测试中,使用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()
- : 11,944
utf8_general_ci
毫秒 - 与
utf8_unicode_ci
:12,887 毫秒
在这个基准测试中,使用utf8_unicode_ci
速度比utf8_general_ci
7.9% 慢。
这篇文章很好地描述了它。
简而言之:utf8_unicode_ci
使用 Unicode 标准中定义的 Unicode 排序算法,然而utf8_general_ci
是一种更简单的排序顺序,导致“不太准确”的排序结果。
请参阅 mysql 手册,Unicode 字符集部分:
对于任何 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 集会更快,因为要做的计算更少。
简而言之:
如果您需要更好的排序顺序 - 使用utf8_unicode_ci
(这是首选方法),
但如果你对性能使用完全感兴趣utf8_general_ci
,但知道它有点过时了。
性能方面的差异非常小。
一些细节(PL)
正如我们在这里所读到的(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
。这种编码没有一个更好或更坏 - 这取决于您的需求。
排序和字符匹配有两个很大的区别:
排序:
utf8mb4_general_ci
删除所有重音并一一排序,这可能会产生错误的排序结果。utf8mb4_unicode_ci
排序准确。
字符匹配
它们以不同的方式匹配字符。
例如, in utf8mb4_unicode_ci
you have i != ı
,但 in utf8mb4_general_ci
it 持有ı=i
。
例如,假设您有一排name="Yılmaz"
. 然后
select id from users where name='Yilmaz';
如果 collocation is 将返回该行utf8mb4_general_ci
,但如果它与之并置utf8mb4_unicode_ci
则不会返回该行!
另一方面,我们有那个,a=ª
而ß=ss
在utf8mb4_unicode_ci
其中不是这种情况utf8mb4_general_ci
。所以想象你有一行name="ªßi"
,然后
select id from users where name='assi';
如果 collocation 是 将返回行utf8mb4_unicode_ci
,但如果 collocation 设置为 则不会返回行utf8mb4_general_ci
。
可以在此处找到每个搭配的完整匹配列表。
根据这篇文章,当使用 utf8mb4_general_ci 而不是 utf8mb4_unicode_ci 时,MySQL 5.7 有相当大的性能优势: https ://www.percona.com/blog/2019/02/27/charset-and-collation-settings-impact -关于-mysql-性能/