utf8_general_ci和utf8_unicode_ci之间有什么区别?

Kah*_*eng 998 mysql unicode collation utf-8 character-set

utf8_general_ci和之间utf8_unicode_ci,性能方面有什么不同吗?

tho*_*ter 1514

这两个排序规则都是UTF-8字符编码.不同之处在于文本的排序和比较方式.

注意:从MySQL 5.5.3开始,你应该使用utf8mb4而不是utf8.它们都引用了UTF-8编码,但较旧版本utf8具有MySQL特定的限制,阻止使用编号高于0xFFFD的字符.

  • 准确性

    utf8mb4_0900_ai_ci 基于Unicode标准进行排序和比较,可以在各种语言中进行准确排序.

    general 无法实现所有Unicode排序规则,这将在某些情况下导致不合需要的排序,例如使用特定语言或字符时.

  • 性能

    utf8mb4_unicode_ci 在比较和排序方面更快,因为它需要一堆与性能相关的快捷方式.

    在现代服务器上,这种性能提升几乎可以忽略不计.它是在服务器只占当今计算机CPU性能的一小部分时设计的.

    utf8mb4_general_ci,它使用Unicode规则进行排序和比较,使用相当复杂的算法在各种语言中进行正确排序,并且在使用各种特殊字符时.这些规则需要考虑特定语言的惯例; 不是每个人都按照我们称之为"字母顺序"的方式对他们的角色进行排序.

就拉丁语(即"欧洲")语言而言,utf8mb4_unicode_ciMySQL 排序和MySQL中的简化排序没有太大区别,但仍存在一些差异:

  • 例如,Unicode排序规则将"ß"排序为"ss",将"Œ" utf8mb4_0900_ai_ci排序为"OE",因为使用这些字符的人通常会想要,而将它们排序为单个字符(可能分别称为"s"和"e") .

  • 某些Unicode字符被定义为可忽略,这意味着它们不应计入排序顺序,而比较应转移到下一个字符. 0900正确处理这些.

在非拉丁语言中,例如亚洲语言或具有不同字母表的语言,Unicode排序和简化排序之间可能存在更多差异ai.适用性在utf8mb4_unicode_ci很大程度上取决于所使用的语言.对于某些语言来说,这将是非常不合适的.

你应该用什么?

几乎可以肯定没有理由再使用utf8mb4_unicode_ci,因为我们已经落后于CPU速度足够低以至于性能差异很重要的地步.您的数据库几乎肯定会受到其他瓶颈的限制.

性能上的差异只能在非常特殊的情况下才能衡量,如果是你,你可能已经知道了.如果您遇到的排序速度很慢,几乎在所有情况下都会出现索引/查询计划的问题.在要排除故障的事项列表中,更改排序规则功能不应该很高.

在过去,有些人建议使用,utf8mb4_general_ci除非准确的排序对于确定性能成本是非常重要的.今天,性能成本几乎消失了,开发人员正在更加认真地对待国际化.

我要补充的另一件事是,即使您知道您的应用程序仅支持英语,它仍可能需要处理人名,这通常包含其他语言中使用的字符,其中正确排序同样重要.对所有内容使用Unicode规则有助于让非常聪明的Unicode人员非常努力地使排序正常工作.

  • @KahWeeTeng你应该**永远,*永远***使用`utf8_general_ci`:它根本不起作用.这是对五十年前ASCII stooopeeedity过去的糟糕历史的回顾.如果没有UCD的折叠映射,则无法进行Unicode不区分大小写的匹配.例如,"Σίσυφος"中有三个不同的sigma; 或者"TSCHüẞ"的小写字母是如何"tschüβ",但"tschüβ"的大写字母是"TSCHÜSS".你可以是对的,或者你可以快.因此你必须使用`utf8_unicode_ci`,因为如果你不关心正确性,那么让它无限快速是微不足道的. (209认同)
  • 我不同意使用更新,更标准的投诉变体是一种不好的做法,而且我认为将糟糕的开发人员称为这样的事情是很煽情的.您可能还需要注意,我的答案显示"*在MySQL的新版本*中使用utf8mb4而不是utf8",强调我的. (21认同)
  • @DanHorvat`utf8mb4`是[唯一正确的选择](https://stijndewitt.com/2015/06/15/use-mysql-utf8mb4-if-you-want-full-unicode-support/).使用`utf8`,你会陷入一些只有MySQL的3字节UTF8变体,只有MySQL(和MariaDB)知道如何处理.世界其他地方正在使用UTF8,它可以包含[每个字符最多4个字节](https://stijndewitt.com/2014/08/09/max-bytes-in-a-utf-8-char/) .MySQL开发人员错误地称他们的自制编码`utf8`并且不破坏向后兼容性,他们现在必须将真正的UTF8称为`utf8mb4`. (20认同)
  • 阅读本文之后,我还发现utf8_unicode_ci会考虑具有相同排序权重的任何字符,以达到相等比较的目的.这导致"か"=="が"或"ǽ"=="æ"`的情况.对于排序这是有道理的,但在通过平等选择或处理唯一索引时可能会令人惊讶 - https://bugs.mysql.com/bug.php?id=16526 (7认同)
  • @DanHorvat限制自己使用MySQL较老的,更有限的Unicode子集的唯一实际原因是,如果你有一个旧版本的MySQL不支持更完整的utf8mb4.5.5.3已超过5年.我很欣赏Plesk运行不同的MySQL时间表,但是大多数发行版现在都在MySQL 5.5上,如果你更新它的组件,Plesk 11.x*会支持MySQL 5.5. (4认同)
  • @托马斯鲁特谢谢你。我还看到 MariaDB 显然计划[跳过 `utf8mb4_0900_*`](https://jira.mariadb.org/browse/MDEV-20912) 并升级到 [`utf8mb4_1400_*`](https://jira.mariadb.org /浏览/MDEV-27009)。 (3认同)
  • "utf8_general_ci在许多语言中非常接近正确的Unicode排序,但在某些语言中有许多不准确之处.":是否对字符类有影响,我的意思是在实践中,它是否会影响像`LTRIM` /`RTRIM这样的东西`? (2认同)
  • “对于那些在 2020 年或以后仍然遇到这个问题的人,[...] 例如,utf8_unicode_520_ci。” 2023 年你好。鉴于字符集“utf8”已弃用,这不应该是“utf8mb4_unicode_520_ci”吗? (2认同)

nig*_*der 154

我想知道使用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;
Run Code Online (Sandbox Code Playgroud)

然后我通过运行这个存储过程填充随机数据:

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
Run Code Online (Sandbox Code Playgroud)

然后我创建了以下存储过程来对simple SELECT,SELECTwith LIKE和sort(SELECTwith 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;
Run Code Online (Sandbox Code Playgroud)

在上面的存储过程utf8_general_ci中使用了排序规则,但当然在测试期间我使用了utf8_general_ciutf8_unicode_ci.

我为每个排序规则调用了每个存储过程5次(5次utf8_general_ci,5次utf8_unicode_ci),然后计算平均值.

我的结果是:

benchmark_simple_select()

  • utf8_general_ci:9,957毫秒
  • utf8_unicode_ci:10,271毫秒

在这个基准测试中使用utf8_unicode_ci比慢于utf8_general_ci3.2%.

benchmark_select_like()

  • utf8_general_ci:11,441毫秒
  • 用时utf8_unicode_ci:12,811毫秒

在这个基准测试中,使用utf8_unicode_ci速度慢于utf8_general_ci12%.

benchmark_order_by()

  • utf8_general_ci:11,944毫秒
  • utf8_unicode_ci:12,887毫秒

在此基准测试中,使用utf8_unicode_ci速度比utf8_general_ci7.9%慢.

  • 很好的基准,感谢分享.我得到了相似的数字(Windows上的MySQL v5.6.12):10%,4%,8%.我同意:`utf8_general_ci`的性能提升太小而不值得使用. (16认同)
  • 1)但是,根据定义,这个基准测试不应该为两个校对产生类似的结果吗?我的意思是`CONV(FLOOR(RAND()*99999999999999),20,36)`只生成ASCII,并且没有Unicode字符由排序算法处理.2)`Description ='test'COLLATE ...`和`Description LIKE'test%'COLLATE ...`只在运行时处理单个字符串("test"),不是吗?3)在实际应用程序中,排序中使用的列可能会被编入索引,并且不同排序规则与真实非ASCII文本的索引速度可能不同. (10认同)
  • @HalilÖzgür - 您的观点部分错误。我想这不是关于在 ASCII 之外的代码点值(general_ci 会正确处理),而是关于特定的功能,比如将变音符号写成“Uml*ea*ute”或一些这样的微妙之处。 (2认同)
  • 因此,虽然这些性能提升看起来很引人注目,但我想知道这是否适用于现实世界的数据。您使用随机字符填充这些字段,但在现实世界中,数据具有更多的结构,并且该结构与排序相关。我的大多数数据库的绝大多数字符采用基本拉丁编码,少数其他字符通常出现在此处或那里的字段中。目前尚不清楚在这些情况下是否会带来任何性能提升。会有吗?我很好奇在我的一些真实数据上运行这个。 (2认同)

Mic*_*sen 37

这篇文章很好地描述了它.

简而言之:utf8_unicode_ci使用Unicode标准中定义的Unicode排序算法,而utf8_general_ci是一种更简单的排序顺序,导致"不太准确"的排序结果.

  • 如果你不关心正确性,那么使任何算法无限快速都是微不足道的.只需使用`utf8_unicode_ci`并假装另一个不存在. (7认同)
  • @tchrist 永远不要成为游戏程序员;) (5认同)
  • @tchrist 但如果你关心正确性和速度之间的某种平衡,`utf8_general_ci` 可能适合你 (2认同)

Ada*_*dam 12

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

\n\n

排序

\n\n
    \n
  • utf8mb4_general_ci删除所有重音符号并进行一一排序,这可能会产生不正确的排序结果。
  • \n
  • utf8mb4_unicode_ci排序准确。
  • \n
\n\n

字符匹配

\n\n

它们以不同的方式匹配字符。

\n\n

例如,utf8mb4_unicode_ci你有i != \xc4\xb1,但utf8mb4_general_ci它包含\xc4\xb1=i

\n\n

例如,假设您有一行name="Y\xc4\xb1lmaz". 然后

\n\n
select id from users where name=\'Yilmaz\';\n
Run Code Online (Sandbox Code Playgroud)\n\n

如果搭配是 则返回该行,但如果它与它utf8mb4_general_ci搭配则不会utf8mb4_unicode_ci返回该行!

\n\n

另一方面,我们有 thata=\xc2\xaa\xc3\x9f=ssin utf8mb4_unicode_ciwhich ,但情况并非如此utf8mb4_general_ci。所以想象一下你和name="\xc2\xaa\xc3\x9fi",然后

\n\n
select id from users where name=\'assi\';\n
Run Code Online (Sandbox Code Playgroud)\n\n

如果 collocation 为 则返回行utf8mb4_unicode_ci,但如果 collocation 设置为 则不会返回utf8mb4_general_ci

\n\n

可以在此处找到每种搭配的完整匹配列表

\n


Dan*_*ane 9

请参阅mysql手册,Unicode字符集部分:

对于任何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_更小且更不正确(根据标准)的比较集,其应该实现整个标准.general_ci集会更快,因为计算量较少.

  • 没有"稍微不正确"的事情.正确性是一个布尔特征; 它不承认程度的修饰语.只需使用`utf8_unicode_ci`并假装错误的破坏版本不存在. (17认同)
  • 通过地理定位或游戏开发,我们始终以性能交换正确性.当然,正确性是"0"和"1"之间的实数,而不是布尔.:) EG在边界框中选择地理点是"附近点"的近似值,这不如计算点和参考点之间的距离并对其进行过滤那么好.但是***都是近似值,事实上,完全正确性大多是无法实现的.参见[海岸线悖论](https://en.wikipedia.org/wiki/Coastline_paradox)和[IEEE 754](https://en.wikipedia.org/wiki/IEEE_floating_point) (5认同)
  • @tchrist说正确性的问题是布尔值,它没有考虑不依赖于绝对正确性的情况.你的基本观点并非无效,也不是我试图支持general_ci的好处,但你对正确性的一般陈述很容易被证实.我每天都在这个职业上做.除了喜剧之外,斯图尔特有一个很好的观点[这里](https://www.youtube.com/watch?v=F_1zoX5Ax9U). (4认同)
  • **TL; DR**:请提供一个打印'1/3`*正确*结果的程序 (4认同)
  • 我在获取5.6.15进行collat​​ion_connection设置时遇到了问题,事实证明您必须像“ SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci”那样在SET行中传递它。值得一提的是Mathias Bynens的解决方案,这是他非常有用的指南:http://mathiasbynens.be/notes/mysql-utf8mb4 (2认同)

Kam*_*ski 7

一些细节(PL)

正如我们在这里读到的(彼得古鲁赞),在排序/比较波兰语字母“?”方面存在差异。(L withstroke - 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
Run Code Online (Sandbox Code Playgroud)

在波兰语中,字母?在字母之后L和之前M。这些编码没有一个更好或更坏 - 这取决于您的需求。


sim*_*eco 6

简而言之:

如果您需要更好的排序顺序 - 使用utf8_unicode_ci(这是首选方法),

但如果你对性能完全感兴趣 - 使用utf8_general_ci,但要知道它有点过时了.

性能方面的差异非常小.

  • 两者现在都已过时 - 有关更多信息,请参阅已接受的答案 (2认同)