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
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;
由于我们可以读取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
9条答案
按热度按时间oxf4rvwz1#
对于那些在2020年或之后仍然面临这个问题的人来说,有一些更新的选项可能比这两个选项都好。例如,
utf8_unicode_520_ci
。所有这些排序规则都是针对UTF-8字符编码的。区别在于文本的排序和比较方式。
_unicode_ci
和_general_ci
是两组不同的规则,用于按照我们期望的方式对文本进行排序和比较,MySQL的较新版本也引入了新的规则集,例如_unicode_520_ci
是基于Unicode 5.2的等效规则,或MySQL 8.x特定的_0900_ai_ci
,以获得基于Unicode 9.0的等效规则(而且没有等效的_general_ci
变体)。现在阅读本文的人们可能应该使用这些较新的排序规则之一,而不是_unicode_ci
或_general_ci
。下面对这些旧归类的描述仅供参考。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
排序没有太大区别,但仍然有一些区别: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
(区分大小写),用于大小写很重要的文本数据;以及X1 M25 N1 X,用于编码需要匹配的地方,逐位,其适用于实际上编码为二进制数据的字段区分大小写的排序会导致一些奇怪的结果,区分大小写的比较会导致重复的值,只是字母大小写不同。所以区分大小写的排序规则不再适用于文本数据-如果大小写对您来说很重要,那么可以忽略的标点符号等可能也很重要,二进制排序规则可能更合适。接下来,
unicode
或general
指的是特定的排序和比较规则--特别是规范化或比较文本的方式。utf8 mb 4字符编码有许多不同的规则集。其中unicode
和general
是试图在所有可能的语言而不是一种特定语言中良好工作的两个。这两组规则之间的差异是本答案的主题。请注意,unicode
使用Unicode 4.0中的规则。MySQL和MariaDB的最新版本添加了使用Unicode 5.2中的规则的规则集unicode_520
。MySQL 8.x使用Unicode 9.0的规则添加了0900
(去掉了“unicode_”部分)。最后,
utf8mb4
当然是内部使用的字符编码,在这个答案中,我只讨论基于Unicode的编码。hgb9j2n62#
我想知道使用
utf8_general_ci
和utf8_unicode_ci
之间的性能差异是什么,但我没有在互联网上找到任何列出的基准,所以我决定自己创建基准。我创建了一个包含500,000行的非常简单的表:
然后,我通过运行以下存储过程用随机数据填充它:
然后,我创建了以下存储过程来对简单的
SELECT
、SELECT
与LIKE
以及排序(SELECT
与ORDER BY
)进行基准测试:在上面的存储过程中,使用了
utf8_general_ci
排序规则,但当然在测试期间,我同时使用了utf8_general_ci
和utf8_unicode_ci
。对于每个排序规则,我调用每个存储过程5次(
utf8_general_ci
调用5次,utf8_unicode_ci
调用5次),然后计算平均值。我的结果是:
utf8_general_ci
:9,957毫秒utf8_unicode_ci
:10,271毫秒在此基准测试中,使用
utf8_unicode_ci
比使用utf8_general_ci
慢3.2%。utf8_general_ci
:11,441毫秒utf8_unicode_ci
:12,811毫秒在此基准测试中,使用
utf8_unicode_ci
比使用utf8_general_ci
慢12%。utf8_general_ci
:11,944毫秒utf8_unicode_ci
:12,887毫秒在此基准测试中,使用
utf8_unicode_ci
比使用utf8_general_ci
慢7.9%。nwlls2ji3#
这篇文章描述得很好。
简而言之:
utf8_unicode_ci
使用Unicode标准中定义的Unicode排序规则算法,而utf8_general_ci
是一种更简单的排序顺序,导致排序结果“不太准确”。qlzsbp2j4#
参见mysql手册,Unicode Character Sets部分:
对于任何Unicode字符集,使用_general_ci归类执行的操作都比使用_unicode_ci归类执行的操作快。例如,utf8_general_ci归类的比较比utf8_unicode_ci归类的比较快,但准确性稍差。这是因为utf8_unicode_ci支持扩展等Map;即,当一个字符与其他字符的组合进行比较时。例如,在德语和某些其他语言中,“ß”等于“ss”。utf8_unicode_ci还支持缩写和可忽略字符。utf8_general_ci是不支持扩展、缩写或可忽略字符的旧归类。它只能在字符之间进行一对一比较。
综上所述,utf_general_ci使用的比较集比utf_unicode_ci更小,正确性更低(根据标准),而utf_unicode_ci * 应该 * 实现整个标准。general_ci集合将更快,因为要做的计算更少。
0g0grzrc5#
简言之:
如果您需要更好的排序顺序-使用
utf8_unicode_ci
(这是首选方法),但是如果你对性能完全感兴趣-使用X1 M1 N1 X,但是要知道它有点过时了。
性能方面的差异非常小。
gc0ot86w6#
排序和字符匹配有两大区别:
排序:
utf8mb4_general_ci
删除所有重音符号并逐个排序,这可能会创建不正确的排序结果。utf8mb4_unicode_ci
排序准确。字符匹配
它们匹配字符的方式不同。
例如,在
utf8mb4_unicode_ci
中有i != ı
,但在utf8mb4_general_ci
中有ı=i
。例如,假设您有一行
name="Yılmaz"
。如果配置为
utf8mb4_general_ci
,则将返回该行,但如果与utf8mb4_unicode_ci
配置,则将不返回该行!另一方面,我们有
a=ª
和ß=ss
在utf8mb4_unicode_ci
中,而在utf8mb4_general_ci
中不是这样,所以假设有一行name="ªßi"
,那么如果配置为
utf8mb4_unicode_ci
,则将返回行,但如果配置设置为utf8mb4_general_ci
,则不返回行。每个搭配的匹配的完整列表可以在here中找到。
fjaof16o7#
一些细节(PL)
由于我们可以读取here(Peter Gulutzan),因此在排序/比较波兰文字母“”(带笔划的L- html esc:
Ł
)(小写:“”- html esc:ł
)-我们有以下假设:在波兰语中,字母
Ł
在字母L
之后,在M
之前。这些编码没有一个更好或更差--这取决于你的需要。goqiplq28#
根据这篇文章,当使用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/
wlwcrazw9#
上面的评论表明没有理由使用
utf8_general*
,然而,对于日语来说,这可能不是真的。在MariaDB中
utf8mb4_ja_0900_as_cs
不可用,因此您必须使用unicode或常规选项之一。然而,unicode
将浊音和清音等视为相同。例如,(美容师)被视为等同于(医院)。这显然是不正确的行为。而一般情况下
换句话说,unicode对浊音和清音假名一视同仁。Imo,这是不可取的。
编辑:使用较新版本的MariaDB上提供的
uca1400_ai_cs
可能会更好,并使上述排序规则正确。