参数解读
各参数含义参考这个连接(摘抄如下):MySQL Handler
mysql> show global status like 'Handle%';
+----------------------------+---------------+
| Variable_name | Value |
+----------------------------+---------------+
| Handler_commit | 292547310 |
| Handler_delete | 20004688 |
| Handler_discover | 0 |
| Handler_external_lock | 298778503 |
| Handler_mrr_init | 0 |
| Handler_prepare | 89764296 |
| Handler_read_first | 84936796 |
| Handler_read_key | 197690980 |
| Handler_read_last | 25 |
| Handler_read_next | 449603395282 |
| Handler_read_prev | 488140 |
| Handler_read_rnd | 17904308 |
| Handler_read_rnd_next | 2234678057572 |
| Handler_rollback | 3965287 |
| Handler_savepoint | 2 |
| Handler_savepoint_rollback | 566 |
| Handler_update | 150669454 |
| Handler_write | 1392716731 |
+----------------------------+---------------+
18 rows in set (0.00 sec)
| Handler_commit | 内部提交语句数
| Handler_delete | 请求从表中删除行的次数。
| Handler_discover | MySQL服务器可以问NDB CLUSTER存储引擎是否知道某一名字的表。这被称作发现。Handler_discover说明通过该方法发现的次数。
| Handler_external_lock | 此变量与锁定操作数量有关,主要是在表访问的开始和结束时起作用。
| Handler_mrr_init | 服务器使用存储引擎自己实现的多范围读取的次数。
| Handler_prepare | 用于两阶段提交操作的准备阶段的计数器。
| Handler_read_first | 索引中第一条被读的次数。如果较高,它建议服务器正执行大量全索引扫描;例如,SELECT col1 FROM foo,假定col1有索引。
| Handler_read_key | 根据键读一行的请求数。如果较高,说明查询和表的索引正确。
| Handler_read_last | 根据键读最后一行的请求数。
| Handler_read_next | 按照键顺序读下一行的请求数。如果你用范围约束或如果执行索引扫描来查询索引列,该值增加。
| Handler_read_prev | 按照键顺序读前一行的请求数。该读方法主要用于优化ORDER BY … DESC。
| Handler_read_rnd | 根据固定位置读一行的请求数。如果你正执行大量查询并需要对结果进行排序该值较高。你可能使用了大量需要MySQL扫描整个表的查询或你的连接没有正确使用索引。
| Handler_read_rnd_next | 在数据文件中读下一行的请求数。如果你正进行大量的表扫描,该值较高。通常说明你的表索引不正确或写入的查询没有利用索引。
| Handler_rollback | 内部ROLLBACK语句的数量
| Handler_savepoint | 在一个存储引擎放置一个保存点的请求数量。
| Handler_savepoint_rollback | 在一个存储引擎的要求回滚到一个保存点数目。
| Handler_update | 请求更新表中一行的次数。
| Handler_write | 请求向表中插入一行的次数。
Handler_read_rnd_next的值高则意味着查询运行低效,并且应该建立索引补救或优化。这个值的含义是在数据文件中读取下一行的请求数。如果正在进行大量的表扫描,Handler_read_rnd_next的值比较高通常说明表索引不正确或写入的查询没有有效利用索引。
实际优化中比较看重的几个参数
Handler_read_first和Handler_read_rnd_next
如果前者值较大,说明可能是一个全索引扫描,此外走全表也可能导致这个值比较大;后者值较大,说明扫描的行非常多,可能没有合理的使用索引
Handler_read_key
如果这个值比较大,说明索引使用良好
案例
SELECT CASE WHEN O.FD_ORDER_BUS_TYPE = 1 THEN '自营' ELSE '他营' END AS FD_ORDER_BUS_TYPE, S.SUPPLIER_NAME, G.GOODS_CODE, G.GOODS_NAME, D.FD_NUM
, D.FD_UNIT_NAME, CASE WHEN D.FD_IS_GIFT = '1' THEN '是' ELSE '否' END AS FD_IS_GIFT, (
SELECT dic.dic_name
FROM plat_system_dictionary dic
WHERE dic.DIC_VALUE = D.FD_GIFT_TYPE
AND dic.DIC_DICTYPE_CODE = 'giftType'
) AS FD_GIFT_TYPE_NAME, D.FD_UNIT_PRICE, D.FD_TOTAL_PRICE
, O.FD_ID, O.FD_NO, C.fd_code, C.fd_name, DATE_FORMAT(O.FD_ORDER_DATE, '%Y-%m-%d') AS FD_ORDER_DATE
, (
SELECT CR.fd_code
FROM plat_shop_customer_info CR
WHERE CR.fd_id = O.FD_REC_ORG_ID
) AS FD_REC_ORG_CODE, O.FD_REC_ORG_NAME, (
SELECT CB.fd_code
FROM plat_shop_customer_info CB
WHERE CB.fd_id = O.FD_BILL_ORG_ID
) AS FD_BILL_ORG_CODE, O.FD_BILL_ORG_NAME, CASE WHEN O.FD_IS_TRAN_DIRECT = '1' THEN '是' ELSE '否' END AS FD_IS_TRAN_DIRECT
, CASE WHEN O.FD_IS_ALL_BILL = '1' THEN '是' ELSE '否' END AS FD_IS_ALL_BILL, (
SELECT dic.dic_name
FROM plat_system_dictionary dic
WHERE dic.DIC_VALUE = O.FD_ORDER_STATUS
AND dic.DIC_DICTYPE_CODE = 'orderStatus'
) AS FD_ORDER_STATUS_NAME, O.NC_ORDER_CODE, a.AREATYPE_NAME, U.SYSUSER_NAME AS CREATE_USER_NAME
, substring_index(O.FD_CONTACTS_INFO, ':', 1) AS FD_LINKMAN, substring_index(O.FD_CONTACTS_INFO, ':', -1) AS FD_MOBILE_NUMBER, O.FD_REC_ADDR AS FD_ADDRESS, D.FD_DESC
FROM plat_shop_order_detail D
LEFT JOIN plat_shop_order O ON O.FD_ID = d.FD_ORDER_ID
LEFT JOIN plat_shop_customer_info C ON o.FD_ORDER_ORG_ID = C.fd_id
LEFT JOIN plat_shop_areatype a ON o.FD_AREATYPE_ID = a.AREATYPE_ID
LEFT JOIN plat_shop_goods G ON G.GOODS_ID = D.FD_GOODS_ID
LEFT JOIN plat_shop_supplier S ON G.GOODS_SUPPLIER_ID = S.SUPPLIER_ID
LEFT JOIN plat_system_sysuser U ON U.SYSUSER_ID = O.FD_CREATE_USER_ID
LEFT JOIN plat_system_sysuser U1 ON U1.SYSUSER_ID = O.FD_PLAT_AUDIT_USER_ID
WHERE 1 = 1
AND O.FD_BILL_TYPE = '1'
AND O.FD_BILL_TYPE = '1'
AND O.FD_ORDER_BUS_TYPE = '2'
AND O.FD_ORDER_STATUS = '3'
ORDER BY O.FD_NO DESC;
几张表的数据量都很小,均是不到1W行的水平,然而执行时间接近2s,查看执行计划,可以发现O表和dic表都走的全表,但行数都不算太大,一个4000+,一个1000+
其中O表的using filesort显得比较扎眼,初步判断执行慢是因为排序造成的
对O表添加联合索引,发现需要添加的字段还挺多,而且这些字段的选择性都奇差无比;索引添加完成后,执行时间几乎没有缩短,说明这个filesort并不是查询慢的根本原因。查看handler状态
1.flush status;
2.执行语句
3.show status like '%handler_read%';
可以看到Handler_read_rnd_next高达400W,前面添加的索引几乎没起作用
对gic表添加索引后,Handler_read_rnd_next值下降到6000,执行时间下降为原来的1/10
O表的索引删除后,Handler_read_rnd_next值无变化,执行时间无变化,考虑到O表较小,而且索引列的选择性都奇差无比,这个索引实在是没有存在的必要,故删除该索引
最终的执行计划如下
网友评论