其实只要项目中使用了主流的编程语言,则无论是PHP还是.NET还是Python又或是Java什么语言,一旦项目出现性能问题那基本上问题都出在数据库里面,数据量大,查询语句的查询逻辑比较低下
所以其实在代码层做运行效率的优化所得来的“提升感”并不会很明显,大部分程序员还是要花多点时间在数据库优化的工作上(这里默认指的就是实现了SQL标准的关系型数据库)
这其实已经不是PHP领域的专题了,我个人也是少有积累,接触的项目也不大,能谈的不是很多,未来再慢慢整理成MySql优化专题
基本上就是在索引优化的功夫上
在这里我先快速简要地分享一下优化经验
-
了解索引
稍有经验的程序员在面试时都跟我说,他觉得索引就像一本书的目录一样,把故事的章节标题和页码标上,翻目录就能很快找到章节去读故事,而不是一页一页地查找需要的故事内容
不然如果一本书没有目录,叫你去读你也不大喜欢是吧
没错,逻辑上数据库索引就是这样起到快速查询的作用的
-
索引的实现媒介
实现索引快速查找的技术是一种叫
二叉树
的数据结构,它是计算机学界中出了名的用于搜索的数据结构,又有人称为“搜索树”数据结构叫二叉树,那搜索时也要有一个算法去从这个结构中找到需要的数据才行,它就叫
二分查找法
想涨姿势的同学可以找一下二叉树和二分查找法的资料看看(大部分是C语言的代码演示,可以专门搜索下有没有PHP的二叉树算法演示)
但个人不理解为什么有的大牛在面试高级PHP程序员时,居然会问起红黑树,虽然也是一种树结构,但是在MySql中的应用没听过,在一些内存管理中就用过(比如Nginx),说这个是为免大家探索时陷入了红黑树中,我也粗略了解过一下,不想陷太深
-
索引需要重构
既然书中有很多章节,既然做了个目录列出哪个标题的章节在哪一页开始
可是如果这本书突然加多了一个章节在中间,如果不更新目录,那就无法从目录(索引)中快速查到这一篇新章节了是吧!?
所以,只要书的内容有新增,原来的目录也要更新才可以,侧面映射了数据表的数据有新的插入时,索引也得更新
其实不止,你想啊,书中的文章标题变了,目录其实也得跟着更新标题
书的章节被删除了,目录也得跟着更新,不然目录查到有章节,跳到那一页却又没有那个章节,查询就扑空啦!
小结:数据表的增、删、改 都会引起索引重构
-
索引如何重构的
很不幸地告诉你,索引重构,我听说简直就是删除原来的索引,重新组织一份的,或许不是这样,又或许有某些类型的索引会很巧妙地快速重构,但是业界中都公认:索引重构的性能损耗是要被考虑的
所以一个表如果频繁地发生增、删、改的话,那它就会引起频繁的重构,于是其实数据库光是忙着重构索引都没空处理查询语句了
小结:频繁增、删、改的字段不要加索引,比如什么次数、金钱、积分、上次登陆时间、个性签名
想想,其实本来就很少用这它去条件去查询的呀,其实那些A表与B表的关联字段比较好做索引,通常这个关联字段不会被删除,少数情况下B表的记录被删要更新A表的值,那就要考虑这个更新频率了
-
索引分4类
主健索引、唯一索引、普通索引、全文索引,其中主健索引也包含唯一性,具体自己上网查MySql索引分类
-
唯一索引的查询效率会快些
这种索引的值都是唯一的嘛,就像目录中每一章节的标题都是不同的,只要找到这个标题了,就不需要再往后找了,赶紧终止索引查询,跑去具体的页码去抓数据了
而普通索引不唯一,所以就算找到了一个值与where里查询的一样,但后面可能还会有啊,于是别停下来,赶紧往后查查还有没有,最后整个索引目录都被它翻遍了!
提示:除了主键以外,倒是很少有其它字段是唯一的,邮箱吧,手机号吧,账号吧,也就这些了,其它看具体业务场景了
-
如何知道SQL语句用了什么索引
你上网找这个关键词吧:MySql explain分析,其中用desc也可以,只是显示方式不一样
-
一般情况下每次查询只使用1个索引
特殊情况下会用2次或2次以上,这个不多解释,凭你的经验感知一下你的查询,建立虚拟数据测试+explain分析一下
-
差异值较低的字段不应做索引
并不是where里用到的字段都要做索引,别盲目这样搞!
主要是那些type、status、flag这些常见的字段,它们的值不多,基本上不会上几十个,而是10~20个以内,那就不用做索引了
你可以设想一下为这些字段建立上目录,然后你看目录时会是什么感受,它说type==1的在1、4、5、6、8、9页(因为不是唯一的)
先从索引里找出这么多页再去这些页找数据,我还没说其它type==2和type==3在目录里的占有量
好了其实想想如果不看索引,直接从书本的第1页扫到第10页就能跟索引差不多的速度找出数据,而索引的存在变成了占空间的存在,这个索引大了,查索引的时间成本也更大,最后还是要跑很多次书页……
反正值的差异化不大的话就别建索引了吧
-
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
逻辑就不会使用索引 -
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语句时,这个中间件就自动判断语句的操作方式再决定分派给后面的哪台服务器