索引相关
1.使用最左前缀规则
如果使用联合索引,要遵守最左前缀规则。即要求使用联合索引进行查询,从索引的最左前列开始,不跳过索引中的列并且不能使用范围查询(>、<、between、like)。
2.模糊查询不能利用索引(like '%XX'或者like '%XX%')
假如索引列code的值为'AAA','AAB','BAA','BAB',如果where code like '%AB'条件,由于条件前面是模糊的,所以不能利用索引的顺序,必须逐个查找,看是否满足条件。这样会导致全索引扫描或者全表扫描。
如果是where code like 'A%',就可以查找code中A开头的数据,当碰到B开头的数据时,就可以停止查找了,因为后面的数据一定不满足要求,这样可以提高查询效率。
3.不要过多创建索引
过多的索引会占用更多的空间,而且每次增、删、改操作都会重建索引。
在一般的互联网场景中,查询语句的执行次数远远大于增删改语句的执行次数,所以重建索引的开销可以忽略不计。但在大数据量导入时,可以考虑先删除索引,批量插入数据,然后添加索引。
尽量扩展索引,比如现有索引(a),现在又要对(a,b)进行索引,那么只需要修改索引(a)即可,避免不必要的索引冗余。
4.索引长度尽量短
短索引可以节省索引空间,使查找的速度得到提升,同时内存中也可以装载更多的索引键值。
太长的列,可以选择建立前缀索引
5.索引更新不能频繁
更新非常频繁的数据不适宜建索引,因为维护索引的成本。
6.索引列不能参与计算
查询时的优化
1.小表驱动大表
多表关联第一张表是全表索引(要以此关联其他表),其余表的查询类型type为range(索引区间获得),也就是6 * 1 * 1,共遍历查询6次即可;
建议使用left join时,以小表关联大表,因为使用join的话,第一张表是必须全扫描的,以少关联多就可以减少这个扫描次数.
这里所说的表的type,指的是explain执行计划中的结果字段。详情点击查看,explain的属性详解与提速百倍的优化示例
2.避免全表扫描
mysql在使用不等于(!=或者<>)的时候无法使用导致全表扫描。在查询的时候,如果对索引使用不等于的操作将会导致索引失效,进行全表扫描
避免mysql放弃索引查询
如果mysql估计使用全表扫描要比使用索引快,则不使用索引。(最典型的场景就是数据量少的时候)
3.使用覆盖索引,少使用select*
需要用到什么数据就查询什么数据,这样可以减少网络的传输和mysql的全表扫描。
尽量使用覆盖索引,比如索引为name,age,address的组合索引,那么尽量覆盖这三个字段之中的值,mysql将会直接在索引上取值(using index),并且返回值不包含不是索引的字段。
减少select*
如果查询select的列过多,覆盖索引的效率会大大下降,这时可以考虑放弃覆盖索引查询。
4.order by的索引生效
order by排序应该遵循最佳左前缀查询,如果是使用多个索引字段进行排序,那么排序的规则必须相同(同是升序或者降序),否则索引同样会失效。
不正确的使用导致索引失效
如果查询中有某个列的范围查询,则其右边所有列都无法使用索引。
1.for update锁表
A, B两个事务分别使用select ... where ... for update进行查询时:
- A事务执行查询操作的时候,如果这个查询结果为空,无论where条件是否是索引字段,B事务执行查询操作时,不会被阻塞。
- A事务执行查询操作的时候,当where条件是索引字段,则B事务执行同样的查询时会被行加锁阻塞;当where条件不是索引字段,则B事务执行有结果集的查询,都会被阻塞。
for update操作一定要谨慎,之前笔者就遇到过for update产生gap锁,导致后续请求阻塞的问题。
之后的博客单独介绍MySQL的锁机制,同时讲解下更多死锁的情况。
其他优化
1.开启慢查询
开启慢查询日志,可以让MySQL记录下查询超过指定时间的语句,通过定位分析性能的瓶颈,更好的优化数据库系统的性能。
以后单独的博客进行详细的讲解
2.实时获取有性能问题的SQL
利用information_schema数据库的processlist表,实时查看执行时间过长的线程,定位需要优化的SQL。
例如下面的SQL的作用是查看正在执行的线程,并按Time倒排序,查看执行时间过长的线程。
select * from information_schema.processlist where Command != 'Sleep' order by Time desc;
3.垂直分割
“垂直分割”是一种把数据库中的表,按列变成几张表的方法。这样可以降低表的复杂度和字段的数目,从而达到优化的目的。
示例一:
在Users表中有一个字段是address,它是可选字段,并且不需要经常读取或是修改。
那么,就可以把它放到另外一张表中,这样会让原表有更好的性能。
示例二:
有一个叫 “last_login”的字段,它会在每次用户登录时被更新,每次更新时会导致该表的查询缓存被清空。
所以,可以把这个字段放到另一个表中。
这样就不会影响对用户ID、用户名、用户角色(假设这几个属性并不频繁修改)的不停地读取了,因为查询缓存会增加很多性能。
4.拆分执行时间长的DELETE或INSERT语句
避免在生产环境上执行会锁表的DELETE或INSERT的操作。一定把其拆分,或者使用LIMIT条件也是一个好的方法。