美文网首页
MySQL handler解读

MySQL handler解读

作者: 飞翔的Tallgeese | 来源:发表于2019-10-29 11:45 被阅读0次

    参数解读

    各参数含义参考这个连接(摘抄如下):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表较小,而且索引列的选择性都奇差无比,这个索引实在是没有存在的必要,故删除该索引

    最终的执行计划如下

    相关文章

      网友评论

          本文标题:MySQL handler解读

          本文链接:https://www.haomeiwen.com/subject/enkevctx.html