Java性能 -- 高性能SQL
慢SQL诱因
- 无索引、索引失效
- 锁等待
- InnoDB支持行锁,MyISAM支持表锁
- InnoDB支持行锁更适合高并发场景,但行锁有可能会升级为表锁
- 一种情况是在批量更新时
- 行锁是基于索引加的锁,如果在更新操作时,条件索引失效,那么行锁会升级为表锁
- 基于表锁的数据库操作,会导致SQL阻塞等待,影响执行速度
- 在写大于读的情况下,不建议使用MyISAM
- 行锁相对于表锁,虽然粒度更细,并发能力提升,但也带来了新的问题,那就是死锁
- 不恰当的SQL
SELECT *
SELECT COUNT(*)
- 大表中使用
LIMIT M,N
- 对非索引字段进行排序
SQL诊断
EXPLAIN
- id:每个执行计划都有一个id,如果是一个联合查询,会有多个id
- select_type
- SIMPLE:普通查询,即没有联合查询、子查询
- PRIMARY:主查询
- UNION:UNION中后面的查询
- SUBQUERY:子查询
- table:当前执行计划查询的表,如果表有别名,则显示别名
- partitions:分区表信息
- type
- 从表中查询到行所执行的方式
- 由好到坏:_**
system > const > eq_ref > ref > range > index > ALL
**_ - system/const
- 表中只有一行数据匹配,根据索引查询一次就能找到对应的数据
- eq_ref
- 使用唯一索引扫描,常见于多表连接中使用主键和唯一索引作为关联条件
- ref
- 使用非唯一索引扫描,还可见于唯一索引最左原则匹配扫描
- range
- 索引范围扫描,如
<
、>
、between
等操作
- 索引范围扫描,如
- index
- 索引全表扫描,遍历整个索引树
- ALL
- 全表扫描,遍历全表来找到对应的行
- possible_keys:可能使用到的索引
- key:实际使用到的索引
- key_len:当前使用的索引的长度,单位Byte
- ref:关联id等信息
- rows:查找到记录所扫描的行数
- filtered:查找到所需记录占总扫描记录数的比例
- Extra:额外信息
Show Profile
- 通过EXPLAIN分析执行计划,仅仅停留在分析SQL的外部执行情况
- 如果需要深入MySQL内核,从执行线程的状态和时间来分析,就需要选择Profile
- Profile除了可以分析执行线程的状态和时间
- 还支持查询在ALL、CPU、MEMORY、BLOCK IO、CONTEXT SWITCHES上所消耗的时间
- MySQL是从5.0.37才开始支持
Show Profile
Show Profile
只显示最新发给服务器的SQL语句,默认记录最新15条- 可以设置
profiling_history_size
,最大值为100
- 可以设置
1 | SHOW PROFILE [type [, type] ... ] |
1 | mysql> select @@version; |
SQL优化
优化分页查询
- 经常使用
LIMIT M,N
+ORDER BY
来实现分页查询- 在没有任何索引条件支持的情况下,需要做大量的文件排序操作(file sort),性能很差
- 即便有对应的索引,也只是在刚开始时效率比较理想,越往后,性能越差
- 使用
LIMIT M,N
时,偏移量M越大,数据库检索的数据也会越多 - 例如
LIMIT 10000,10
,数据库需要检索10010条记录,但最后只返回10条记录
- 使用
- 优化方案:子查询 + 覆盖索引
1 | -- 使用了索引,扫描了100010行 |
1 | -- 子查询用到了覆盖索引(Using index),无需回表 |
优化SLECT COUNT(*)
- COUNT()是一个聚合函数,用来统计行数或某一列的行数量(不包括NULL值)
- 常用的是
COUNT(*)
和COUNT(1)
,两者没有本质区别,在InnoDB,都会利用主键列实现行数的统计 - 通常没有任何查询条件下的
COUNT(*)
,MyISAM的查询速度要明显快于InnoDB- 这是因为MyISAM记录了整个表的行数,无需遍历计算,直接获取即可,而InnoDB需要扫描表来统计具体的行数
- 如果带上查询条件,MyISAM和InnoDB都需要扫描表来进行行数的统计
- 优化方案
- 使用近似值,借助
EXPLAIN
中的rows
- 增加汇总统计,使用汇总统计表或缓存
- 使用近似值,借助
优化SLECT *
- 尽量使用覆盖索引
记录慢SQL
1 | mysql> show variables like '%slow_query%'; |
参考资料
All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.