MYSQL之HANDLER_READ_*详细讲解


1)简介:Handler_read_*

在对MySQL的Query进行调优时候,我们可以有效的使用EXPLAIN [EXTENDED ],SHOW PROFILE内置工具,但是通过这两个工具,得到的是比较粗糙的信息,我们无法得知MySQL到底是如何操作底层的数据。其实可以通过MySQL Status中的Handler_read计数器获知这些详情。下面是基础操作知识:
a)获取Handler_read_*系类状态值命令:show session status like ‘Handler_read%';
b)重置Handler_read系列状态值命令:flush status;
通过a)的命令,我们获取到Handler_read_*的状态有下面这些,下面会对这些进行一一解释以及测试:
Handler_read_first
Handler_read_last
Handler_read_next
Handler_read_prev
Handler_read_rnd
Handler_read_rnd_next
Handler_read_key

2)测试数据结构:

 

3)解释:Handler_read_*

a)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.
通过index获取数据的次数

b)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.
读取索引第一个条目的次数

c)Handler_read_last:The number of requests to read the last key in an index. With ORDER BY, the server will issue a first-key request followed by several next-key requests, whereas with ORDER BY DESC, the server will issue a last-key request followed by several previous-key requests. This variable was added in MySQL 5.6.1.
读取索引最后一个条目的次数

d)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.
通过索引读取下一条数据的次数

e)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.
通过索引读取上一条数据的次数

f)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.
从固定位置读取数据的次数

g)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.
从数据节点读取读取下一条数据的次数

4)测试:Handler_read_*

################## 单表测试

a)全表扫描

Handler_read_first + 1 : 从键的第一个位置开始读取
Handler_read_key + 1 : 根据第一个位置的KEY读1行,其他9行是根据叶节点的链表依次读取
Handler_read_rnd_next +10 : 从主键的叶节点(行数据)中顺序读取10行

b)索引扫描

Handler_read_first + 1 : 从键的第一个位置开始读取
Handler_read_key + 1 : 根据第一个位置的KEY读1行
Handler_read_next + 9 : 按键顺序依次读取之后的9行

Handler_read_key + 1 : 根据第一个位置的KEY读1行
Handler_read_last + 1 : 从键的最后一个位置开始读取
Handler_read_prev + 9 : 按键顺序依次读取之前的9行

################## 多表测试

a)表数据介绍

test_handler_read表当key4=44有 5行数据
test_handler_read_info_v1中rid是主键

b)where+ drived table sort
####### sort: asc

Handler_read_key + 6 : a 根据idx_key4_id(key4=44)读1次,b 根据 PRIMARY KEY读5次
Handler_read_prev + 4 :a 按键顺序依次向后读取4个id

####### sort: desc

Handler_read_key + 6 : a根据idx_key4_id(key4=44)读1次,b根据PRIMARY KEY读5次
Handler_read_prev + 4: a按键倒序依次向前读取4个id

c)where+ second table sort

####### sort: asc

Handler_read_key + 6 : a 根据idx_key4_id(key4=44)读1次,b 根据PRIMARY KEY读5次
Handler_read_next + 5 : a 按键顺序依次读取4个id. 额外的1次是?(没有查出相关文档)
Handler_read_rnd + 5 : filesort后每行位置都是固定的,limit 5取5行
Handler_read_rnd_next + 6: filesort全表遍历读取temporary表中的5行,进行排序; 额外的1是EOF标志位;

Using temporary; Using filesort 原因: 无法使用a表的索引
1>先查询表a key4=44 的5行与b进行join,将结果保存在temporary表
Handler_read_key + 6, Handler_read_next + 5
2>然后对临时表排序;
Handler_read_rnd_next + 6
3>取前10个。
Handler_read_rnd + 5
d)where条件放在join中

Handler_read_first + 1 : 从表a中idx_key4_id索引开始位置进行读取
Handler_read_key + 6 : a  根据idx_key4_id(key4=44)读1次,b 根据PRIMARY KEY读5次
Handler_read_rnd_next + 12 : filesort全表遍历读取表a中idx_key4_id索引的11行,进行排序; 额外的1是EOF标志位;

没有 Using temporary 是因为先对a的索引idx_key4_id进行排序,然后再join, Using filesort 原因:无法使用a表的索引
1>先对a表排序,取5个id
Handler_read_first + 1, Handler_read_key + 1, Handler_read_rnd_next + 12
2>然后根据a.id=b.rid与表b进行join
Handler_read_key + 5

5)剖析实例

Handler_read_key + 1:从表W_ListenRec_1_8中使用idx_listenrec_userid索引读取
Handler_read_next + 2029:表W_ListenRec_1_8按照索引顺序读取2028, 额外的1是EOF标志位
Handler_read_rnd + 39:filesort后每行位置都是固定的,获取全部的39行数据
Handler_read_rnd_next + 41:filesort全表遍历读取temporary表中的39行,1为EOF标识为,例外的1??自己测试了一下,sql最后加上limit 39,Handler_read_rnd_next=39, 加上limit 40。39,Handler_read_rnd_next=41,多出来的1目前没出查到来源

那么印证一下子猜测的结果:

setp 1:

从上面这个拆解sql看出来,首先这个sql使用了idx_listenrec_userid读取数据一条数据Handler_read_key + 1,然后按照索引顺序向后再读取了2028条记录,最后读到了EOF,Handler_read_next +2028+1

step 2:

从step 2的拆解看的出来,经过group by的操作后,直接临时表中的结果读取出来

step 3:见拆解前的sql
最后的步骤是将临时表中的数据进行排序,然后按照顺序取出来。

6)Tunning

应该怎么修改这个sql才能让他性能有比较大的提升呢?从explain和Handler_read_*的结果来看,我们使用了临时表,使用了临时排序,并且我们在where条件的使用过程中,没有将无用的数据全部过滤下去
step1:缩减无效数据的读取
step2:避免临时表的使用
step3:避免filesort
我们可用通过建立符合索引idx_listenrec_userid_courseid来实现避免无效数据的读取,同时也避免了因为group by courseguid造成的使用Using temporary。
分析一下这个sql,order by id desc的目的主要是为了获取最大的相同courseid下最大的starttime(显然这个语句达不到效果,O(∩_∩)O~),我们可以通过使用max(starttime) as starttime 来实现。如果我们真心相对听课时间有一个由近及远的排序,那么我建议放到程序里面去解决。

调整的步骤如下:
1) 调整索引:
alter table W_ListenRec_1_8 drop index idx_listenrec_userid;
alter table W_ListenRec_1_8 add index idx_listenrec_userid_courseid(userid,courseguid);
2)修改sql:

3)有需要的话再程序中根据starttime进行降序处理

PS:
1)本文针对MySQL5.6版本,MySQL5.5可能略有不同,但原理没有变化,依然可以作为参考
2)本文测试均以engine=innodb进行测试,以其他存储引擎测试时会有些许变化.for example:engine =innodb是索引组织表,而engine=myisam时候是堆表

参考链接:

1)http://dev.mysql.com/doc/refman/5.6/en/server-status-variables.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值