“那啥,你过来一下!”
“怎么了,我代码都单元测试了的,没出问题啊!”我一脸懵逼跑到运维大佬旁边。
“你看看!你看看!多少条报警,赶快优化一下!”运维大佬短信列表里面好多MySQL CPU 100%报警短信。再看看项目名称不就是我前几天刚发布的项目吗!?
我心底一沉,赶快赔上笑脸。“这个一定优化,马上优化!那个,能不能看下数据库监控日志...”
运维大佬又数落了我几句,然后调开了数据库监控日志。
那家伙...每秒300多的连接数,几乎快要封顶的全表扫描数,还有大红色CPU警报。。。
“那个,能不能看看nginx访问日志...我看下访问量...”我弱弱地说到。
运维大佬不情愿的跑了下下面的语句:
grep -c come access.log
come这个接口是其中一个请求量比较大的接口,结果是600多万。那个时候才中午,周末高峰期估计一天得有上千万吧,
我撇了撇嘴,心里想着这么高的请求量,当初那么抠门只给我一台低配数据库还好意思说,不过嘴上肯定是:“好好好,请求量不是很大,看来是数据库问题,我立刻去优化一下!”
“给它弄一个读写分离不就行了吗!?”这时另外一个运维大佬凑了过来,随意地挥了挥手。。。
你问我DBA去哪儿了?DBA当时有点忙,只说让我自己检查一下。。。
优化思路
我这个项目由于上线之前比较赶,所以前期并没有管数据库设计方面的一些问题,如今随着游戏接入,请求量剧增才暴露出来。(其实是前期加班加烦了懒得搞)
这个问题,并不需要增加数据库硬件配置和增加读写分离这种高端手段就能解决,我自个儿挖了多少坑,心里还是有点碧树的。
详细的MySQL优化步骤如下:
检查数据表结构,改善不完善设计
跑一遍主要业务,收集常用的数据库查询SQL
分析查询SQL,适当拆分,添加索引等优化查询
优化SQL的同时,优化代码逻辑
添加本地缓存和redis缓存
这个项目是原生PHP写的,以上这些只能自己做了。
检查数据表结构
因为比较菜,回去看设计的表结构,真是惨不忍睹。
尽可能不要使用NULL值
因为建表的时候,如果不对创建的值设置默认值,MySQL都会设置默认为
NULL
。那么为啥用
NULL
不好呢?
NULL使得索引维护更加复杂,强烈建议对索引列设置NOT NULL
NOT IN、!=等负向条件查询在有NULL值的情况下返回永远为空结果,查询容易出错
NULL列需要一个额外字节作为判断是否为NULL的标志位
使用NULL时和该列其他的值可能不是同种类型,导致问题。(在不同的语言中表现不一样)
MySQL难以优化对可为NULL的列的查询
所以对于那些以前偷懒的字段,手动设置一个默认值吧,空字符串呀,0呀补上。
虽然这种方法对于MySQL的性能来说没有提升多少,但是这是一个好习惯,而且以小见大,不要忽略这些细节。
添加索引
对于经常查询的字段,请加上索引,有索引和没有索引的查询速度相差十倍甚至更多。
一般来说,每张表都需要有一个主键id字段
常用于查询的字段应该设置索引
varchar类型的字段,在建立索引的时候,最好指定长度
查询有多个条件时,优先使用具有索引的条件
像LIKE条件这样的模糊搜索对于字段索引是无效的,需要另外建立关键词索引来解决
请尽量不要在数据库层面约束表和表之间的关系,这些表之间的依赖应该在代码层面去解决
当表和表之间有约束时,虽然增删查的SQL语句变简单了,但是带来的负面效果是插入等操作数据库都会去检查约束(虽然可以手动设置忽略约束),这样相当于把一些业务逻辑写到了数据库层,不便于维护。
优化表字段结构
数据库中那些可以用整形表示的数据就不要使用字符串类型,到底是用
varchar
还是
char
要看字段的可能值。
这种优化往往在数据库中有大量数据以后是不可行的,最好在数据库设计之前就设计好。
对于那些可能值很有限的列,使用tinyint代替VARCHAR,
比如记录移动设备平台,只有两个值:android,ios,那么就可以使用0表示android,1表示ios,这种列一定要写好注释