utf8_general_ci和utf8_unicode_ci在性能上有什么区别吗?
- 另请参见stackoverflow.com/questions/1036454/…
- 如果你喜欢utf8[mb4]_unicode_ci,你可能更喜欢utf8[mb4]_unicode_520_ci。
- 我不知道我对此有何看法——他们不按照最新的Unicode标准来修改实现,而是将过时的版本作为默认版本,人们现在必须添加"520"才能使用正确的版本。它不能向前和向后兼容,因为你不能在老版本的MySQL上使用"520"版本。为什么他们不能更新现有的排序规则?和"MB4"一样,真的。什么代码真正依赖于旧的、有限的/过时的行为来证明将其作为默认行为是正确的?
- 8.0违约的utf8mb4_0900_ai_ci更好。
这两个排序规则都适用于UTF-8字符编码。不同之处在于文本的排序和比较方式。
注:由于MySQL5.5.3应该使用utf8mb4,而不是utf8。它们都指的是UTF-8编码,但是旧的utf8有一个特定于mysql的限制,防止使用超过0xfffd的字符。
准确度
utf8mb4_unicode_ci是基于Unicode排序和比较标准的,该标准在非常广泛的语言范围内精确排序。
utf8mb4_general_ci未能实现所有的unicode排序规则,这将导致在某些情况下(例如使用特定语言或字符时)出现不希望的排序。
性能
utf8mb4_general_ci在比较和排序方面速度更快,因为它采用了一系列与性能相关的快捷方式。
在现代服务器上,这种性能提升几乎可以忽略不计。它是在服务器的CPU性能只占当今计算机的一小部分的时候设计的。
utf8mb4_unicode_ci使用Unicode规则进行排序和比较,它使用一种相当复杂的算法在各种语言中以及在使用各种特殊字符时进行正确排序。这些规则需要考虑到特定于语言的约定;并不是每个人都按照我们称之为"字母顺序"的方式对字符进行排序。
就拉丁语(即"欧洲")而言,在MySQL中,Unicode排序和简化的utf8mb4_general_ci排序没有太大的区别,但仍然存在一些差异:
在非拉丁语言中,例如亚洲语言或具有不同字母的语言,Unicode排序和简化的utf8mb4_general_ci排序之间可能存在更多的差异。utf8mb4_general_ci的适用性在很大程度上取决于所使用的语言。对于某些语言来说,这是远远不够的。
你应该用什么?
几乎没有理由再使用utf8mb4_general_ci,因为我们留下了CPU速度足够低的点,性能差异非常重要。您的数据库几乎肯定会受到其他瓶颈的限制。
绩效的差异只会在非常专业的情况下被衡量,如果是你,你可能已经知道了。如果您遇到排序缓慢的问题,那么在几乎所有情况下,索引/查询计划都会出现问题。更改排序规则功能不应排在要解决的问题列表的最前面。
在过去,有些人建议使用utf8mb4_general_ci,除非准确的排序非常重要,足以证明性能成本的合理性。如今,性能成本几乎消失了,开发人员正在更加认真地对待国际化。
我要补充的另一件事是,即使你知道你的应用程序只支持英语,它可能仍然需要处理人们的名字,这通常可以包含在其他语言中使用的字符,在其他语言中,正确排序同样重要。对所有内容使用Unicode规则有助于让人安心,因为非常聪明的Unicode用户非常努力地使排序工作正常进行。
- @卡维滕,你不应该,永远不要使用utf8_general_ci:它根本不起作用。这是对50年前美国信息交换协会(ASCII)糟糕的旧时代的倒退。如果没有来自UCD的FoldCase映射,则无法进行Unicode不区分大小写的匹配。例如,"?"西格玛?"里面有三个不同的符号;或者"tsch&252;"的小写字母是怎样的?"是"tsch&252;β",但是"tsch&252;β"的大写字母是"tsch&220;ss"。你可以是对的,也可以很快。因此,您必须使用utf8_unicode_ci,因为如果您不关心正确性,那么让它无限快是微不足道的。
- "在许多语言中,utf8-general-ci非常接近于正确的unicode排序,但在某些语言中有许多不准确的地方。":我的意思是,在实践中,它对字符类也有影响吗,比如LTRIM/RTRIM?
- 这是一个很好的问题,我无法给出一个明确的答案。我假设LTRIM和RTRIM的工作原理是相同的,就像纯粹在字符类上进行的任何标识一样。这两个排序规则具有相同的字符和字符范围。不过,我将重复我的一般观点,即确实没有理由再使用utf-general-ci了,因为它的创建原因不再与所有的用例相关,而是与最模糊的用例相关。
- @托马斯鲁特出于好奇,你是怎么学到这么多关于SQL的知识的?
- 我对SQL不太了解,尽管我已经使用MySQL很多年了,并且喜欢花时间了解事情的内部。
- 拯救我的一天…我想知道为什么我使用的ORM总是用Unicode生成模式,现在我知道了。谢谢你的解释
- 阅读本文后,我还发现,为了进行相等比较,utf8 _unicode _ci将考虑具有相同排序规则权重的任何字符。这导致了"か" =="が"或"?" =="æ"的情况。对于排序来说,这是有意义的,但当通过均等选择或处理唯一索引时,可能会令人惊讶-bugs.mysql.com/bug.php?ID=16526
- 我将您的更新移到了顶部,因为这是处理tl;dr用例的信息中最新和最相关的部分。
- 我很欣赏这个想法,但主要的问题是他们之间的区别是什么,他们被你的编辑压了下来。我对使用什么的建议只是一个后续行动。我会想出一个更好的编辑,希望你会更喜欢。
- 如mysql~5.5.3中介绍的,更新后将字符编码称为utf8mb4而不是utf8。它们是相同的编码,但utf8mb4消除了MySQL限制,将其限制为BMP(仅前65536个码位)。
- utf8mb4是一个创造性的选择,也许我甚至可以说是一个荒谬的选择。作为一个开发人员,你应该选择兼容的东西并解决问题,而不是创建它们。也就是说,utfmb4不能在许多PLESK服务器上工作,因为它们没有更好或等于5.5.3的预装MySQL版本。这里应用了"足够好"的原则,所以请使用utf8_unicode_ci作为排序规则。
- @Danhorvat将自己限制在MySQL更旧、更有限的Unicode子集的唯一实际原因是,如果您有一个旧版本的MySQL,它不支持更完整的utf8mb4。5.5.3岁以上。我很感激Plesk运行在不同的MySQL时间表上,但是大多数发行版现在都在MySQL5.5上,如果您更新它的组件,Plesk11.x支持MySQL5.5。
- @托马斯鲁特是的,我就是这么说的。除非有实际的理由使用utf8mb4(额外的东西对您的项目非常重要-您是一个语言学家或其他什么),否则您应该坚持在大多数环境下都能工作。其他一切,例如使用utf8mb4,因为它是"最好的",实际上是不好的做法。无论是谁做的都是一个坏的dev.ovh-plesk服务器,CentOS 6.6使用MySQL5.1.73。"大多数人"也不使用旧版本的IE,但是优秀的开发人员在设计网站时会考虑旧的IE。不管怎样,我对这个答案投了反对票。
- 我不同意使用新的、更标准的投诉变体是一种糟糕的做法,我认为将人们称为不好的开发人员是一种煽动性的做法。您可能还需要注意,我的回答是"在新版本的MySQL中使用UTF8MB4,而不是UTF8",强调我的。
- 感谢@jlonora的帮助编辑。
- @丹霍瓦特埃多克斯1〔2〕是唯一正确的选择。使用utf8,您只能使用一些mysql,只有mysql(和mariadb)知道如何处理的utf8的3字节变体。世界上其他地方使用的是utf8,每个字符最多可以包含4个字节。MySQL开发人员错误地将自己的自制编码命名为utf8,为了不破坏向后兼容性,他们现在必须将真正的utf8称为utf8mb4。
- @史蒂恩德维特,我不同意。但是尝试做一件简单的事情——将一个用最流行的CMS(WordPress)站点构建的站点迁移到最流行的主机提供商(OVH)上第二个最流行的面板(Plesk),然后看看会发生什么。我不认为一个工程解决方案不适合最流行的产品是正确的选择。不管教科书怎么说。工程师的工作是解决问题,而不是像我上面所说的那样创造问题。
- YyCube?不,0xFFFD。旧版本的MySQL不支持基本多语言平面(BMP)之外的字符。
- 请看一下你的答案——它已经被编辑过,所以对utf8和utf8mb4的原始引用被混淆了。
- @Rath我故意将"utf8"的所有实例更改为"utf8mb4"。由于MySQL5.5.3(8年前的现在)没有理由使用错误的"utf8"变体,"utf8"和"utf8mb4"不是这里讨论的内容,这是关于"utf8mb4_-general_-ci"和"utf8mb4_-unicode_-ci"的。我诅咒任何选择提出一个令人困惑的名字"utf8mb4"的人,不仅解决了这个问题,而且,好吧,我有8年的时间来接受它。
- @我甚至没有意识到。谢谢你的澄清,你是对的,这些名字一开始很混乱
我想知道使用utf8_general_ci和utf8_unicode_ci之间的性能差异是什么,但是我没有在互联网上找到任何基准,所以我决定自己创建基准。
我创建了一个包含500000行的非常简单的表:
然后我通过运行以下存储过程将其填充为随机数据:
然后,我创建了以下存储过程,对简单的SELECT、SELECT和LIKE进行基准测试,并对(SELECT和ORDER BY进行排序):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| |
在上面的存储过程中,使用了utf8_general_ci排序,但在测试期间,我当然使用了utf8_general_ci和utf8_unicode_ci。
我为每个排序规则调用每个存储过程5次(对于utf8_general_ci调用5次,对于utf8_unicode_ci调用5次),然后计算平均值。
我的研究结果是:
benchmark_simple_select()
- 带utf8_general_ci时:9957 ms
- 带utf8_unicode_ci时:10271 ms
在这个基准中,使用utf8_unicode_ci比使用utf8_general_ci慢3.2%。
benchmark_select_like()
- 带utf8_general_ci时:11441 ms
- 带utf8_unicode_ci时:12811 ms
在这个基准中,使用utf8_unicode_ci比使用utf8_general_ci慢12%。
benchmark_order_by()
- 使用utf8_general_ci时:11944ms
- 带utf8_unicode_ci时:12887 ms
在这个基准中,使用utf8_unicode_ci比使用utf8_general_ci慢7.9%。
- 很好的基准,谢谢分享。我得到了明显相似的数据(Windows上的MySQLv5.6.12):10%,4%,8%。我同意:utf8_general_ci的性能增益太小,不值得使用。
- 1)但是这个基准不应该为两个排序规则生成类似的结果吗?我的意思是,CONV(FLOOR(RAND() * 99999999999999), 20, 36)只生成ASCII,没有Unicode字符可由排序算法处理。2)Description = 'test' COLLATE ...和Description LIKE 'test%' COLLATE ...在运行时只处理一个字符串("test"),不是吗?3)在真正的应用程序中,用于排序的列可能会被索引,不同排序规则上的索引速度与真正的非ASCII文本可能不同。
- @Halil&214;ZG&252;R-您的观点部分错误。我想这不是因为码位值在ASCII之外(一般的_Ci可以正确处理),而是关于特定的特性,比如将编写的umlaut视为"umleaute"或一些这样的微妙之处。
这篇文章描述得很好。
简而言之:utf8_unicode_ci使用unicode标准中定义的unicode排序算法,而utf8_general_ci是一种更简单的排序顺序,导致"不准确"的排序结果。
- 谢谢。那是我的印象。我将接受性能命中:)
- 如果你不关心正确性,那么让任何算法无限快都是微不足道的。只需使用utf8_unicode_ci,假装另一个不存在。
- @但是如果你关心正确性和速度之间的某种平衡,utf8_general_ci可能适合你。
- @奇瑞斯特从不成为游戏程序员;)
- @Onassar-MySQL8.0声称显著提高了所有排序的性能。
简而言之:
如果需要更好的排序顺序,请使用utf8_unicode_ci(这是首选方法)。
但是,如果你对性能非常感兴趣——使用utf8_general_ci,但是要知道它有点过时。
性能方面的差异很小。
参见mysql手册的unicode字符集部分:
For any Unicode character set,
operations performed using the
_general_ci collation are faster than those for the _unicode_ci collation.
For example, comparisons for the
utf8_general_ci collation are faster,
but slightly less correct, than
comparisons for utf8_unicode_ci. The
reason for this is that
utf8_unicode_ci supports mappings such
as expansions; that is, when one
character compares as equal to
combinations of other characters. For
example, in German and some other
languages"?" is equal to"ss".
utf8_unicode_ci also supports
contractions and ignorable characters.
utf8_general_ci is a legacy collation
that does not support expansions,
contractions, or ignorable characters.
It can make only one-to-one
comparisons between characters.
总而言之,utf-unicode-ci比utf-unicode-ci(应该实现整个标准)使用更小、更不正确的比较集。一般的_Ci集将更快,因为要做的计算更少。
- 没有"稍微不正确"的事情。正确性是一个布尔特性;它不允许修改度数。只需使用utf8_unicode_ci并假装不存在有缺陷的版本。
- 我在获得5.6.15的排序规则连接设置时遇到了问题,结果是你必须在设置行中传递它,比如"set names utf8mb4 collate utf8mb4_unicode_ci"。值得一提的是,MathiasBynens提供了解决方案,这是他非常有用的指南:mathiasBynens.be/notes/mysql-utf8mb4
- @t重申正确性是布尔值的问题是,它不考虑不依赖绝对正确性的情况。你的基本观点并不是无效的,我也不是在试图支持将军的利益,但是你关于正确性的总体声明很容易被推翻。在我的职业中,我每天都这样做。撇开喜剧不谈,斯图尔特在这里有一个很好的观点。
- 在地理定位或游戏开发中,我们总是以正确性和性能为代价。当然,正确性是介于0和1之间的实数,而不是bool。:)例如,在边界框中选择地理点是"附近点"的近似值,它不如计算点与参考点之间的距离并对其进行过滤。但这两者都是近似值,事实上,完全正确性通常是不可能实现的。见海岸线悖论和IEEE 754
- tl;dr:请提供一个程序,打印1/3的正确结果。
- 如果没有"稍微不正确"的事情,我就不得不不同意。如果我拿着一个桃子,有人问我拿着什么,我可以回答"桃子",这是正确的。我也可以回答"一片水果",这是正确的,但稍微有点不正确。我可以回答"食物",这同样是正确的,但稍微少一点。明白过度肛门是多么令人讨厌吗?我愿意。
一些细节(pl)
正如我们在这里看到的(彼得·古鲁特赞)波兰字母"?"的排序/比较有区别。(L和stroke-html-esc:Ł(小写:"?"-html-esc:ł——我们有以下假设:
1 2 3 4
| 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之前。这种编码中没有一种更好或更糟——这取决于您的需要。