mysql utf8_general_ci和utf8_unicode_ci之间的区别是什么?

zqdjd7g9  于 2022-12-26  发布在  Mysql
关注(0)|答案(9)|浏览(140)

utf8_general_ciutf8_unicode_ci在性能方面有什么区别吗?

oxf4rvwz

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。下面对这些旧归类的描述仅供参考。

  • 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排序之间可能有很多 * 更多 * 的区别。utf8mb4_general_ci的适用性在很大程度上取决于所使用的语言。对于一些语言,它是相当不合适的。

您应该使用什么?

几乎可以肯定没有理由再使用utf8mb4_general_ci了,因为我们已经忽略了CPU速度低到足以导致性能差异的问题,您的数据库几乎肯定会受到其他瓶颈的限制。
过去,有些人建议使用utf8mb4_general_ci,除非精确的排序非常重要,足以证明性能成本的合理性,而今天,性能成本几乎消失了,开发人员更加认真地对待国际化。
有一种观点认为,如果速度比准确性更重要,那么您还不如根本不进行排序。如果您不需要一个算法准确,那么使其更快是微不足道的。因此,utf8mb4_general_ci是一种折衷方案,可能出于速度原因不需要,也可能出于准确性原因不适合。
我要补充的另一件事是,即使您知道您的应用程序只支持英语,它可能仍然需要处理人名,人名中经常包含其他语言中使用的字符,在这些语言中正确排序同样重要。对所有内容使用Unicode规则有助于让您安心,因为非常聪明的Unicode人员已经非常努力地工作来使排序正常工作。

这些部分意味着什么

首先,ci用于 * 不区分大小写 * 的排序和比较。这意味着它适用于文本数据,大小写并不重要。其他排序类型是cs(区分大小写),用于大小写很重要的文本数据;以及X1 M25 N1 X,用于编码需要匹配的地方,逐位,其适用于实际上编码为二进制数据的字段区分大小写的排序会导致一些奇怪的结果,区分大小写的比较会导致重复的值,只是字母大小写不同。所以区分大小写的排序规则不再适用于文本数据-如果大小写对您来说很重要,那么可以忽略的标点符号等可能也很重要,二进制排序规则可能更合适。
接下来,unicodegeneral指的是特定的排序和比较规则--特别是规范化或比较文本的方式。utf8 mb 4字符编码有许多不同的规则集。其中unicodegeneral是试图在所有可能的语言而不是一种特定语言中良好工作的两个。这两组规则之间的差异是本答案的主题。请注意,unicode使用Unicode 4.0中的规则。MySQL和MariaDB的最新版本添加了使用Unicode 5.2中的规则的规则集unicode_520。MySQL 8.x使用Unicode 9.0的规则添加了0900(去掉了“unicode_”部分)。
最后,utf8mb4当然是内部使用的字符编码,在这个答案中,我只讨论基于Unicode的编码。

hgb9j2n6

hgb9j2n62#

我想知道使用utf8_general_ciutf8_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

然后,我创建了以下存储过程来对简单的SELECTSELECTLIKE以及排序(SELECTORDER 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_ciutf8_unicode_ci
对于每个排序规则,我调用每个存储过程5次(utf8_general_ci调用5次,utf8_unicode_ci调用5次),然后计算平均值。
我的结果是:

    • 一米十二米一x**
  • 使用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%。

    • 一米二十二分一x**
  • 使用utf8_general_ci:11,944毫秒
  • 使用utf8_unicode_ci:12,887毫秒

在此基准测试中,使用utf8_unicode_ci比使用utf8_general_ci慢7.9%。

nwlls2ji

nwlls2ji3#

这篇文章描述得很好。
简而言之:utf8_unicode_ci使用Unicode标准中定义的Unicode排序规则算法,而utf8_general_ci是一种更简单的排序顺序,导致排序结果“不太准确”。

qlzsbp2j

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集合将更快,因为要做的计算更少。

0g0grzrc

0g0grzrc5#

简言之:

如果您需要更好的排序顺序-使用utf8_unicode_ci(这是首选方法),
但是如果你对性能完全感兴趣-使用X1 M1 N1 X,但是要知道它有点过时了。
性能方面的差异非常小。

gc0ot86w

gc0ot86w6#

排序和字符匹配有两大区别:

排序

  • 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=ªß=ssutf8mb4_unicode_ci中,而在utf8mb4_general_ci中不是这样,所以假设有一行name="ªßi",那么

select id from users where name='assi';

如果配置为utf8mb4_unicode_ci,则将返回行,但如果配置设置为utf8mb4_general_ci,则返回行。
每个搭配的匹配的完整列表可以在here中找到。

fjaof16o

fjaof16o7#

一些细节(PL)

由于我们可以读取herePeter 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之前。这些编码没有一个更好或更差--这取决于你的需要。

goqiplq2

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/

wlwcrazw

wlwcrazw9#

上面的评论表明没有理由使用utf8_general*,然而,对于日语来说,这可能不是真的。
在MariaDB中utf8mb4_ja_0900_as_cs不可用,因此您必须使用unicode或常规选项之一。然而,unicode将浊音和清音等视为相同。例如,(美容师)被视为等同于(医院)。这显然是不正确的行为。

> select strcmp('が', 'か' collate utf8mb4_unicode_ci); #0
> strcmp('びよういん', 'びょういん' collate utf8mb4_unicode_520_ci); #0

而一般情况下

> select strcmp('が', 'か' collate utf8mb4_general_ci); #1

换句话说,unicode对浊音和清音假名一视同仁。Imo,这是不可取的。
编辑:使用较新版本的MariaDB上提供的uca1400_ai_cs可能会更好,并使上述排序规则正确。

相关问题