关于utf8_bin与latin1_general_cs的区别及英文大小写敏感存储差异的问询
咱们从几个关键维度来拆解这俩排序规则的差异:
依赖的字符集不同
utf8_bin是基于MySQL的utf8字符集(注意:这里的utf8实际是utf8mb3,最多支持3字节的Unicode字符),能覆盖绝大多数主流语言的字符;而latin1_general_cs是基于latin1(也就是ISO-8859-1)字符集,仅支持256个字符,主要覆盖西欧拉丁语系。比较/排序的逻辑本质不同
utf8_bin是二进制逐字节对比,完全不考虑语言规则——简单说就是看字符的二进制编码值大小,比如'A'的utf8编码是0x41,'a'是0x61,所以'A'会被判定为小于'a';而latin1_general_cs是语言规则驱动的大小写敏感排序,它会按照拉丁语系的排序逻辑来处理字符,比如对于带重音的字符(像é、è),会按照语言习惯排在e的附近,而不是单纯看二进制值。支持的字符范围差异
因为依赖的字符集不同,utf8_bin可以处理中文、日文、阿拉伯文等非拉丁语系字符,而latin1_general_cs遇到超出latin1范围的字符时,要么被截断成乱码,要么直接报错。存储与性能的隐性差异
latin1字符集每个字符占1字节,所以用latin1_general_cs的表存储西欧字符时更紧凑,比较速度也略快;而utf8字符集的字符占1-3字节,存储体积更大,二进制对比的性能虽然也不差,但整体比latin1稍逊一筹(不过这个差异在小数据量下几乎感知不到)。
这个得分两种情况看:
如果是纯ASCII范围内的英文(仅a-z、A-Z、数字、常见标点)
这时候两者几乎没有差异。因为ASCII字符在utf8和latin1中的二进制编码是完全一致的,utf8_bin的二进制对比和latin1_general_cs的语言规则对比结果完全相同——比如判断'A'和'a'的大小,两者都会认为'A' < 'a';排序时,大小写字母的顺序也完全一致。而且存储体积也一样,因为ASCII字符在utf8中也是占1字节。
如果涉及latin1扩展字符(比如带重音的外来词、£这类符号)
这时候差异就显现了:
- 存储上:latin1中的扩展字符(比如é,latin1编码是
0xE9)在utf8中会被存成2字节(0xC3A9),占用更多空间; - 比较上:utf8_bin会直接对比这两个字节的二进制值,而latin1_general_cs会按照拉丁语系的规则把é和e归为相近的字符(但大小写敏感的前提下,还是会区分é和É),对比逻辑和二进制对比可能产生不同结果。
不过如果你的业务里只用到纯英文(无扩展字符),那选哪一个都能满足大小写敏感的需求,差别可以忽略。
内容的提问来源于stack exchange,提问作者Sugawara

