Handler_read_*的总结

在分析一个SQL的性能好坏时,除了执行计划,另外一个常看的指标是”Handler_read_*”相关变量。

  • Handler_read_key
  • Handler_read_first
  • Handler_read_last
  • Handler_read_next
  • Handler_read_prev
  • Handler_read_rnd
  • Handler_read_rnd_next

这七个变量,官方文档也有讲解,但很多人看完后,还是一头雾水。

下面结合具体的示例,来看看这七个变量的具体含义和区别。

Handler

首先说说什么是handler。

handler是一个类,里面按不同的功能模块定义了若干接口(具体可参考sql/handler.h)。其中,

DML操作相关的接口有:

  • write_row()
  • update_row()
  • delete_row()
  • delete_all_rows()
  • start_bulk_insert()
  • end_bulk_insert()

索引扫描相关的接口有:

  • index_read_map()
  • index_init()
  • index_end()
  • index_read_idx_map()
  • index_next()
  • index_prev()
  • index_first()
  • index_last()
  • index_next_same()
  • index_read_last_map()
  • read_range_first()
  • read_range_next()

其它相关接口可参考sql/handler.h,sql/handler.cc文件。

如此设计,有两点显而易见的好处:

  1. Server层与存储引擎层解耦。MySQL Server层在与存储引擎层交互时,无需关心存储引擎层的实现细节,直接调用handler对象的相关方法即可。

  2. 降低了新引擎的引入门槛。如MyRocks。

测试数据

Handler_read_key

首先看看官档的解释

The number of requests to read a row based on a key. If this value is high, it is a good indication that your tables are properly indexed for your queries.

简而言之,即基于索引来定位记录,该值越大,代表基于索引的查询越多。

看看下面这个Demo。

测试中有两点发现:

  1. 无论是基于主键,还是二级索引进行等值查询,Handler_read_key都会加1。

  2. 对于二级索引,如果返回了N条记录,Handler_read_next会相应加N。

Handler_read_first

首先看看官档的解释

The number of times the first entry in an index was read. If this value is high, it suggests that the server is doing a lot of full index scans (for example, SELECT col1 FROM foo, assuming that col1 is indexed).

读取索引的第一个值,该值越大,代表涉及索引全扫描的查询越多。

但是,这并不意味着查询利用到了索引,还需要结合其它的Handler_read_xxx来分析。

看看下面这个Demo

基于c来查询,c不是索引,故走的是全表扫描(通过Handler_read_rnd_next的值和表的总行数也可判断出来),但Handler_read_first和 Handler_read_key同样也增加了。

下面再看看另外一个Demo

t2和t1基本一样,只不过t2是MyISAM表,此时只增加了Handler_read_rnd_next。

之所以会这样,是因为t1是Innodb表,而Innodb是索引组织表,全表扫描实际上是基于主键来做的,所以Handler_read_first和Handler_read_key都会相应加1。

而t2是MyISAM表,MyISAM是堆表。

所以,单凭Handler_read_first很难评估查询的优劣。

Handler_read_last

首先看看官档的解释

The number of requests to read the last key in an index. With ORDER BY, the server issues a first-key request followed by several next-key requests, whereas with ORDER BY DESC, the server issues a last-key request followed by several previous-key requests.

和Handler_read_first相反,是读取索引的最后一个值。

该值增加基本上可以判定查询中使用了基于索引的order by desc子句。

看看下面两个Demo

  1. 基于主键的正向排序

可以看到,增加的还是Handler_read_first和Handler_read_nex t。

  1. 基于主键的反向排序

此时增加的是Handler_read_last和Handler_read_ prev。

Handler_read_next

首先看看官档的解释

The number of requests to read the next row in key order. This value is incremented if you are querying an index column with a range constraint or if you are doing an index scan.

根据索引的顺序来读取下一行的值,常用于基于索引的范围扫描和order by limit子句中。

看看下面两个Demo

  1. 基于索引的范围查询

  2. 基于索引的order by子句

注意:该查询使用了hint,强制索引,如果没用的话,会走全表扫描。

Handler_read_prev

首先看看官档的解释

The number of requests to read the previous row in key order. This read method is mainly used to optimize ORDER BY … DESC.

根据索引的顺序来读取上一行的值。一般用于基于索引的order by desc子句中。

具体示例可参考Handler_read_last。

Handler_read_rnd

首先看看官档的解释

The number of requests to read a row based on a fixed position. This value is high if you are doing a lot of queries that require sorting of the result. You probably have a lot of queries that require MySQL to scan entire tables or you have joins that do not use keys properly.

基于固定位置来读取记录。

关于固定位置的定义,不同的存储引擎有不同的说法

For MyISAM, position really means a byte offset from the beginning of the file. For InnoDB, it means to read a row based on a primary key value.

下面看看Handler_read_rnd的使用场景

Usually Handler_read_rnd is called when a sort operation gathers a list of tuples and their “position” values, sorts the tuples by some criterion, and then traverses the sorted list, using the position to fetch each one. This is quite likely to result in retrieving rows from random points in the table, although that might not actually result in random IO if the data is all in memory.

大意是对记录基于某种标准进行排序,然后再根据它们的位置信息来遍历排序后的结果,这往往会导致表的随机读。

看看下面这个Demo

这里使用了order by rand()来生成随机记录。虽然只生成了10条记录,但Handler_read_rnd_next却调用了202次,比全表扫描还多,所以线上不建议使用order by rand()来生成随机记录。

Handler_read_rnd_next

首先看看官档的解释

The number of requests to read the next row in the data file. This value is high if you are doing a lot of table scans. Generally this suggests that your tables are not properly indexed or that your queries are not written to take advantage of the indexes you have.

读取下一行记录的次数,常用于全表扫描中。

实现原理

Handler_read_rnd_next is incremented when handler::rnd_next() is called. This is basically a cursor operation: read the “next” row in the table. The operation advances the cursor position so the next time it’s called, you get the next row.

看看下面两个Demo

  1. 全表扫描,带有limit条件

  2. 全表扫描

细心的童鞋可能会发现,limit 50时Handler_read_rnd_next为50,而不带limit条件时,Handler_read_rnd_next却为101,不是只有100行数据么?

实际上,在做全表扫描时,MySQL也并不知道表有多少行,它会不断调用handler::rnd_next()函数,直至记录返回完毕。

所以最后一次调用虽然为空,但毕竟调用了这个函数,故Handler_read_rnd_next需在表的总行数的基础上加1。

综合案例

最后,来个综合一点的案例,看看两表关联查询,各状态值又是怎样的呢?

在这里,会涉及到MySQL的Nest Loop算法。

通过执行计划可以看出,该查询的处理流程大致如下:

接着,来分析下输出结果

  1. 对t_1表进行全表扫描,全表扫描对应的状态值是Handler_read_first = 1,Handler_read_key = 1,Handle r_read_rnd_next = 101。

  2. 因为t_1表有100行,所以会对t_2基于k值进行100次查询,对应的,Handler_read_key = 100。

  3. 观察t1表k值的分布,当id=1,2,3时,k的值均为1,其它id的k值不相同。所以一共会返回106条记录,对应的,Handler_read_next = 106。

  4. Handler_read_key的值越大越好,代表基于索引的查询较多。

  5. Handler_read_first,Handler_read_last,Handler_read_next,Handler_read_prev都会利用索引。但查询是否高效还需要结合其它Handler_read值来判断。

  6. Handler_read_rnd不宜过大。

  7. Handler_read_rnd_next不宜过大,过大的话,代表全表扫描过多,要引起足够的警惕。

参考资料

Original: https://www.cnblogs.com/ivictor/p/14877885.html
Author: iVictor
Title: Handler_read_*的总结

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/599075/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 模板语法之过滤器

    过滤器的作用 用来修改变量的输出结果 语法 {{变量名|过滤器1:’参数值1’|过滤器2:’参数值2}} <h1>&#x8F…

    数据库 2023年6月14日
    098
  • CentOS 安装 Docker CE

    CentOS 安装 Docker CE 警告:切勿在没有配置 Docker YUM 源的情况下直接使用 yum 命令安装 Docker. 准备工作 系统要求 Docker CE 支…

    数据库 2023年6月6日
    088
  • MySQL系统安装与部署

    数据库版本标准化 1.确认Supported Platforms https://www.mysql.com/support/ 2.确认安装版本 推荐:5.7.22 ,8.0.20…

    数据库 2023年5月24日
    087
  • 安装多个Jdk

    安装 官方下载地址 目录结构: 配置 PATH 修改 注意:此处的操作可能会有其他未知的问题,最好还是理解window的命令查找机制,然后还是需要根据具体问题具体分析解决。 测试 …

    数据库 2023年6月11日
    0101
  • 外卖项目

    项目介绍: 本项目,瑞吉外卖是专门为餐饮企业,餐厅,饭店定制的一款软件产品,包括系统管理,后台和移动端应用两部分,其中系统管理后台主要提供给餐饮企业内部员工使用,可以对餐厅的菜品,…

    数据库 2023年6月16日
    0114
  • MyBatisPlus代码生成示例

    一、依赖 com.baomidou mybatis-plus-generator 3.5.3 org.projectlombok lombok 1.18.16 compile or…

    数据库 2023年6月11日
    087
  • 【StoneDB】从库如何规避不支持的DML和DDL

    (以下情况仅针对StoneDB 1.0版本不支持的部分DML和DDL操作,StoneDB 2.0及以上版本将无需此类操作)在主从复制中,主库的任何更新都将同步到从库。如果从库不想重…

    数据库 2023年5月24日
    096
  • django中有关ajax的部分

    Django_ajax AJAX(Asynchronous Javascript And XML)翻译成中文就是”异步Javascript和XML”。即使用…

    数据库 2023年6月14日
    096
  • [SWPU2019] Android2

    有一个超长的线程延迟和永远为假的判断条件,应该就是要修改这两点。 使用apktool反编译apk之后,直接打开MainActivity.smali文件,找到 1000000000的…

    数据库 2023年6月11日
    069
  • entitybuilder–一个简单的业务通用框架

    关于业务通用框架的思考 业务系统是千差万别的,例如,保存、更新和删除订单,或者保存订单和保存客户,走的根本不是一个流程。但是,它们还是有共同点,它们的流程大致可以分成下面的几个部分…

    数据库 2023年6月6日
    093
  • CentOS服务器的网络配置与部署

    1.系统安装与软件安装 1.1选择CentOs7.9release版本用作所研发系统部署服务器,官网以及所选择镜像为地址为:http://ftp.sjtu.edu.cn/cento…

    数据库 2023年6月6日
    093
  • 数据库原理三—MySQL数据库优化

    MySQL优化 MySQL优化分为以下几个大类: 数据库调优在一般情况下都是SQL调优,那么,应该如何进行SQL调优呢? id select_type description 1 …

    数据库 2023年6月6日
    0238
  • 三分钟图解事务隔离级别,看一遍就懂

    前文说过,”锁” 是数据库系统区别于文件系统的一个关键特性,其对象是 事务,用来锁定的是数据库中的对象,如表、页、行等。锁确实提高了并发性,但是却不可避免地…

    数据库 2023年5月24日
    0121
  • 我的第一本算法书 第一章

    1.1 决定数据顺序和位置关系的是数据结构 电话簿的数据结构 按获取顺序排序 按拼音顺序排序 添加简单 查询麻烦 查询简单 添加麻烦 两者结合 分别使用不同的表存储不同的首字母, …

    数据库 2023年6月11日
    098
  • NO.2 Windows桌面图标-间距参数调整

    遇到如下问题: 桌面图标自动排序后间隔过大,且如图二这种指向图标能看到图标之间的间隔虚框,此方法可调整虚框的水平和垂直距离,即调整图标之间的间距。 测试电脑: 华为 mateboo…

    数据库 2023年6月14日
    0103
  • ASP.NET MVC通用权限管理系统源代码开源发布(AngelRM_MVC)v2.1

    一、Angel工作室简单通用权限系统简介 AngelRM(Asp.net MVC)是基于asp.net(C#)MVC打造后端原生态代码+前端bootstrap+ztree+loda…

    数据库 2023年6月14日
    089
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球