【MySQL】我必须得告诉大家的MySQL优化原理2(下)字符集

字符集:

关于字符集大多数人的第一印象可能就是:数据库字符集尽量使用UTF8,因为UTF8字符集是目前最适合于实现多种不同字符集之间的转换的字符集,可以最大程度上避免乱码问题,也可以方便以后的数据迁移。But why?

字符集是指一种从二进制编码到某类字符符号的映射,可以参考如何使用一个字节来表示英文字母。校对规则是指一组用于某个字符集的排序规则,即采用何种规则对某类字符进行排序。MySQL每一类编码字符都有其对应的字符集和校对规则。MySQL对各种字符集的支持都非常完善,但同时也带来一些复杂性,某些场景下甚至会有一些性能牺牲。

 

一种字符集可能对应多种校对规则,且都有一个默认校对规则,那在MySQL中是如何使用字符集的?在MySQL中可以通过两种方式设置字符集:创建对象时设置默认值、客户端与服务器通信时显式设置。

MySQL采用“阶梯”式的方式来设定字符集默认值,每个数据库,每张表都有自己的默认值,它们逐层继承,最终靠底层的默认设置影响创建的对象。比如,创建数据库时,将根据服务器上的character_set_server来设置数据库的默认字符集,同样的道理,根据database的字符集来指定库中所有表的字符集......不管是对数据库,还是表和列,只有当它们没有显式指定字符集时,默认字符集才会起作用。

 

当客户端与服务器通信时,它们可以使用不同的字符集,这时候服务器将进行必要的转换工作。当客户端向服务器发送请求时,数据以character_set_client设置的字符集进行编码;而当服务器收到客户端的SQL或者数据时,会按照character_set_connection设置的字符集进行转换;当服务器将要进行增删改查等操作前会再次将数据转换成character_set_database(数据库采用的字符集,没有单独配置即使用默认配置,具体参考上文),最后当服务器返回数据或者错误信息时,则将数据按character_set_result设置的字符集进行编码。服务器端可以使用SET CHARACTER SET改变上面的配置,客户端也可以根据对应的API来改变字符集配置。客户端和服务器端都使用正确的字符集才能避免在通信中出现问题。

 

那如何选择字符集?

最主要的衡量因素是存储的内容,在能够满足存储内容的前提下,尽量使用较小的字符集。因为更小的字符集意味着更少空间占用、以及更高的网络传输效率,也间接提高了系统的性能。

如果存储的内容是英文字符等拉丁语系字符的话,那么使用默认的latin1字符集完全没有问题,如果需要存储汉字、俄文、阿拉伯语等非拉丁语系字符,则建议使用UTF8字符集。当然不同字符在使用UTF8字符集所占用的空间是不同的,比如英文字符在UTF8字符集中只使用一个字节,而一个汉字则占用3个字节。

 

除了字符集,校对规则也是我们需要考虑的问题。对于校对规则,一般来说只需要考虑是否以大小写敏感的方式比较字符串或者是否用字符串编码的二进制来比较大小,其对应的校对规则的后缀分别是_cs_ci_bin

大小写敏感和二进制校对规则的不同之处在于

1、二进制校对规则直接使用字符的字节进行比较

2、大小写敏感的校对规则在多字节字符集时,如德语,有更复杂的比较规则。

举个简单的例子,UTF8字符集对应校对规则有三种:

  • utf8_bin将字符串中的每一个字符用二进制数据存储,区分大小写
  • utf8_general_ci不区分大小写,cicase insensitive的缩写,即大小写不敏感
  • utf8_general_cs区分大小写,cscase sensitive的缩写,即大小写敏感

比如,创建一张表,使用UTF8编码,且大小写敏感时,可以使用如下语句:

CREATE TABLE sales (
    order_no VARCHAR(32) NOT NULL PRIMARY KEY,
    order_amount INT NOT NULL DEFAULT 0,
    ......
) ENGINE=InnoDB COLLATE=utf8_general_cs;

因此,在项目中直接使用UTF8字符集是完全没有问题的,但需要记住的是不要在一个数据库中使用多个不同的字符集,不同字符集之间的不兼容问题很难缠。有时候,看起来一切正常,但是当某个特殊字符出现时,一切操作都会出错,而且你很难发现错误的原因。

字符集对数据库的性能有影响吗?

某些字符集和校对规则可能会需要多个的CPU操作,可能会消耗更多的内存和存储空间,这点在前文已经说过。特别是在同一个数据库中使用不同的字符集,造成的影响可能会更大。

不同字符集和校对规则之间的转换可能会带来额外的系统开销,比如,数据表salesbuyer字段上有索引,则可以加速下面的ORDER BY操作:

SELECT order_no,order_amount FROM sales ORDER BY buyer;

只有当SQL查询中排序要求的字符集与服务器数据的字符集相同时,才能使用索引进行排序。你可能会说,这不是废话吗?其实不然,MySQL是可以单独指定排序时使用的校对规则的,比如:

// 你说,这不是吃饱了撑的吗?我觉得也是,也许会有其适用的场景吧
// 这时候就不能使用索引排序呢,只能使用文件排序
SELECT order_no,order_amount FROM sales ORDER BY buyer COLLATE utf8_bin;

当使用两个字符集不同的列来关联两张表时,MySQL会尝试转换其中一个列的字符集。这和在数据列外面封装一个函数一样,会让MySQL无法使用这个列上的索引。关于MySQL字符集还有一些坑,但在实际应用场景中遇到的字符集问题,其实不是特别的多,所以就此打住。

 

结语

MySQL还有一些其他高级特性,但在大多数场景下我们很少会使用,因此这里也没有讨论,但多了解一些总是好的,至少在需要的时候,你知道有这样一个东西。我们非常多的人,总是会认为自己所学的知识就像碎片一样不成体系,又找不到解决办法,那你有没有想过也许是碎片不够多的缘故?点太少,自然不能连接成线,线太少,自然不能结成网。因而,没有其他办法,保持好奇心、多学习、多积累,量变总有一天会质变,写在这儿,与大家共勉吧。

前面我写的一些文章里面会有提到过,架构设计是一种平衡的艺术,其实质应该是一种妥协,是对现有资源的一种妥协。有时候我们会不自觉的陷入某一个点,比如,为了追求数据的扩展性,很多人一上来就开始分库分表,然后把应用搞得非常复杂,到最后表里还没有装满数据,项目就已经死了。所以在资源有限或者未来还不可知的情况下,尽量使用数据库、语言本身的特性来完成相应的工作,是不是会更好一点。解决大数据问题,也不只是分库分表,你还应该还可以想到分区;有些业务即使在分布式环境下也不一定非要在业务层完成,合理使用存储过程和触发器,也许会让你更轻松......

最后,本文所讨论的知识点均出自《高性能MySQL》,强烈建议大家读一读这本书。

参考资料

[1] Baron Scbwartz 等著;宁海元 周振兴等译;高性能MySQL(第三版); 电子工业出版社, 2013

小结:

作者的结语写的很好,不过我还再写一些鸡肋吧:

具体问题具体分析:存储过程、触发器不抱偏见

我还是不写了,拿作者的话吧:

不费事了,结语加粗的字、自动移动到此处……

字符集:二进制编码到某类字符符号的映射,有着校验规则,选择时基于内容选较小

CHEN川 大佬这个价值观传播的不错:赞一个 32个

保持好奇心、多学习、多积累,相信量变总有一天会质变,加油 파이팅!



作者:CHEN川
链接:https://www.jianshu.com/p/01b9f028d9c7
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

猜你喜欢

转载自blog.csdn.net/ma15732625261/article/details/81435589