性能专题 - 数据库

  • 作者:KK

  • 发表日期:2016.10.25

  • 更新日期:2017.7.9

    简化了一些索引的描述,引用了另一篇讲索引的文章来详细描述


其实只要项目中使用了主流的编程语言,则无论是PHP还是.NET还是Python又或是Java什么语言,一旦项目出现性能问题那基本上问题都出在数据库里面,数据量大,查询语句的查询逻辑比较低下

所以其实在代码层做运行效率的优化所得来的“提升感”并不会很明显,大部分程序员还是要花多点时间在数据库优化的工作上(这里默认指的就是实现了SQL标准的关系型数据库)

这其实已经不是PHP领域的专题了,我个人也是少有积累,接触的项目也不大,能谈的不是很多,未来再慢慢整理成MySql优化专题


基本上就是在索引优化的功夫上

在这里我先快速简要地分享一下优化经验


了解索引

我最近也发表了相关的分析文章MySql进阶 - 索引 - 索引快的原理,不算深入,感谢阅读!


索引分4类

主健索引、唯一索引、普通索引、全文索引,其中主健索引也包含唯一性,具体自己上网查MySql索引分类

唯一索引的查询效率会快些

这种索引的值都是唯一的嘛,就像目录中每一章节的标题都是不同的,只要找到这个标题了,就不需要再往后找了,赶紧终止索引查询,跑去具体的页码去抓数据了

而普通索引不唯一,所以就算找到了一个值与where里查询的一样,但后面可能还会有啊,于是别停下来,赶紧往后查查还有没有,最后整个索引目录都被它翻遍了!

提示:除了主键以外,倒是很少有其它字段是唯一的,邮箱吧,手机号吧,账号吧,也就这些了,其它看具体业务场景了


如何知道SQL语句用了什么索引

你上网找这个关键词吧:MySql explain分析,其中用desc也可以,只是显示方式不一样


一般情况下每次查询只使用1个索引

MySQL5.0之前,一个表一次只能使用一个索引,无法同时使用多个索引分别进行条件扫描。但是从5.1开始,引入了 index merge 优化技术,对同一个表可以使用多个索引分别进行条件扫描。(见《MySql进阶 - 索引 - explain详解》)


varchar只能索引前面几个字符

设计数据表时为什么要给这个类型的字段设置长度呢?其中就是用在索引上面的,长度是3那索引就只拿前面3个字符做索引,后面的抛弃,那你如果要like 'abcd'就无法使用索引了,只能like 'abc'


like查询前面有%号不会用索引

like 'ab%'可以用到索引,但like '%bc'不可以

因为假如索引了前三个字母的话,可数据里有个值是ababc怎么办,索引只对aba做了索引,所以%bc是无法用索引查这样的数据的


一般都不会用全文索引

如果你不介意可以用,实际上全文索引无法索引中文,咋办!?

用Sphinx做索引提供搜索功能吧,查询效率比like查询运算符快不知多少倍了


尽量不要用子查询,用join联接查询来取代它

书上教的,道理也不是很懂,看上去join的实现代码可能比子查询的更牛逼轰轰吧?


count(*)无条件统计一个表的数据条数的运行速度是很快的别担心

尽量避免使用MySql函数

在where、order by、group by里用函数(比如max、min、rand等)都会导致MySql放弃使用索引

这些计算工作留给编程语言吧,让数据库做好它的数据存取处理本职


在InnoDb引擎的表里,where语句有or逻辑就不会使用索引

优化方法1:

此时查询还是要or条件的话就用union来解决:

不会用索引:
SELECT * FROM user WHERE age = 1 or type = 2;


会用索引:
SELECT * FROM user WHERE age = 1 UNION ALL SELECT * FROM user WHERE type = 2;

优化方法2:

上面方法1里总会执行UNION左右两边的SQL,当请求量很大的时候可能不是很合适,可以这样:

在编程语言级别先执行SELECT * FROM user WHERE age = 1看看有没有结果,没有再执行SELECT * FROM user WHERE type = 2

如果有就直接拿左边SQL的语句结果去就行了,反正or逻辑的意思就是随便一个结果都能偏爱对吧?

or左右两边的比较字段都要有索引才会使用索引

where a = 1 or b = 1这样a和b两个字段都要做独立索引


用in取代or

where id = 1 or id = 2 or id = 3应该改造成:where id in(1,2,3)


如果MyISAM表比较多,应该多优化配置里的buffer,具体找文章或看《高性能MySQL》,没有定式

这个优化更加不能一劳永逸,是要持续监测运行情况去调整buffer缓冲区的大小的,关键词:MySQL MyISAM优化


数据达1000万时一般都要做分区分表处理了

由于我没做过大量数据的项目,都是练习中实践的,我暂时推荐MySql parttion这个关键词的解决方案,不推荐用编程语言去计算hash来做分表定位这些

应用分区分表方案后,查询时间缩短了80%以上


适当地冗余数据

就是A表与B表有个关联ID,如果要靠关联ID去查B表的其它信息,这个联接查询在难以解决效率的问题时,就要考虑A表也存一份B表的常用字段信息(前提也是B表的这些信息不经常更新或不会更新)

这样多余地存一份就叫做数据冗余,关键词:数据库反泛式设计

如果B表的字段经常被更新,那A表保存的内容也难以做到同步(实际上连大牛发表的文章都言论他们不会搞同步),所以那些常变更的字段就基本无法做冗余了


慢查询优化

开启慢查询日志,将1秒以上的查询记下,安排定时程序获取慢查询记录提醒优化


实施主从读写分离

这个也是烂大街的资料了,上网找“MYSQL读写分离”大把教程

对于常见的项目,通常都是一主多从模式,就是部署N台服务器专门用于运行数据库的,其中1台是主服务器,增、删、改的SQL都会往这个数据库传达,于是它在增加时就有了新数据,删除时就没了数据,更新时……

其它几台都是从服务器,通过MYSQL自带的配置支持,可以实现主服务器有增、删、改变化时,从主服务器复制数据的更新到自己身上,于是从服务器也等于实时同步了主服务器的数据,拥有了一模一样的数据

另外所有查询语句都往从服务器传达,这样从服务器实际上不处理增删改的SQL,只处理查询的SQL

有人会问程序代码是不是要创建很多个数据库的连接?增删改就连接A库,查询就连接B库……

是呀,但现在很多框架在底层都做了这些支持,只要配置哪个主哪个从就行了

另外其实还有一些中间件可以让代码不修改就实现,比如MySql Proxy,可以实现代码依然只连接一个数据库(就是连接这个中间件),往数据库传达SQL语句时,这个中间件就自动判断语句的操作方式再决定分派给后面的哪台服务器