p231~294. 分为2部分, p231~259, p260~p294. 此为第1部分.
一些语句
查询性能预估
select * from xxx;
show status like 'last_query_cost';
开启慢查询, 参考链接.
set global slow_query_log=ON;//开启慢查询
set long_query_time=1;//设置慢查询, 1表示比1秒长算慢查询
show status like 'slow_queries';//查询慢查询
常见引起慢查询的清空
请求了多余的数据
- 查询不需要的记录. 如
select * from table1
, 然后程序分页, 只取20条, 这样就浪费了. 可以加LIMT解决. - 多表关联取出所有列. 如
select * from table1 inner join table2 on table1.id = table2.t_id
. 可以只拿需要的列代替select *
- 重复查询相同的数据. 如多次查询相同的数据, 可以先缓存到应用程序端, 这样避免多次访问数据库.
额外扫描了多余的记录
3个指标衡量查询
- 响应时间
- 扫描的行数
- 返回的行数
UNION查询
先将一系列的单个查询放入临时表, 再从临时表中读出数据即为结果
关联查询
select A.col1,B.col2 from A
inner join B on A.Id =B.aId
where A.col1 in (5,6)
从A中查找匹配的行, 对于每一个A的行去B中取匹配的行, 生成结果. 伪代码如下
结果泳道图如下
子查询
MySQL在From子句遇到子查询时, 会先将子查询放入临时表, 将这个临时表当作一个普通表对待.
执行计划
和其他关系数据库不同, MySQL不会生成出擦寻字节码来执行查询. MySQL生成查询的一颗指令树, 然后通过存储引擎执行这颗指令树并返回结果.
关联查询优化
多表关联时, 顺序很重要. 例如A join B join C. A有1000行数据, B有500条数据, C有200条数据.
如果以A表为驱动表, 查找B表, 再以join后的结果查询C表. 假设join关系都是索引列, 那么步骤就是
- 执行1000次循环, 根据值去B表索引取到B表主键, 再去B表回表查询B表值. 作为临时结果, 假设有1000*BCount条.(如果是inner join, BCount可能小于1)
- 执行1000*BCount次循环, 根据值去C表索引取C表主键, 再去C表回表查询C表值. 生成结果.
如果以C表为驱动表, 查找B表, 再以join后的结果查询A表. 假设join关系都是索引列, 那么步骤就是
- 执行200次循环, 根据值去B表索引取到B表主键, 再去B表回表查询B表值. 作为临时结果, 假设有200*BCount2条.(如果是inner join, BCount2可能小于1)
- 执行200*BCount2次循环, 根据值去A表索引取A表主键, 再去A表回表查询A表值. 生成结果.
这样以不同的表作为驱动表, 性能是不同的. 数据库会自动调优, 以最优的顺序进行查询. 也可以用straight join
以手动指定的join顺序来查询.
排序优化
排序成本很高, 尽量不要排序或者不要对大量数据进行排序. 当排序列没有索引时, MySQL需要自己排序, 数据量小就在内存排序, 数据量大就在磁盘排序, 统一都用"filesort".
关联查询需要排序有2种情况
- "Using filesort", 如果Order by子句中的所有列都来自关联的第一个表, 那么在关联处理第一个表时就进行文件排序.
- "Using temporary; Using filesort", 排序列不是全部来自第一个表, 就将结果存放到一个临时表, 等关联结束后再进行排序.