Because I do a lot of Performance Tuning gigs I get often in contact with these status variables. In the beginning I had a problem to understand them and now I have a problem to memorize the relation of the name and the meaning. Therefore I wrote this little summary:

PREPARE THE EXAMPLE

To show you the effect I have worked out a little example:

CREATE TABLE test (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY
, data VARCHAR(32)
, ts TIMESTAMP
, INDEX (data)
); INSERT INTO test
VALUES (NULL, 'abc', NOW()), (NULL, 'abc', NOW()), (NULL, 'abd', NOW())
, (NULL, 'acd', NOW()), (NULL, 'def', NOW()), (NULL, 'pqr', NOW())
, (NULL, 'stu', NOW()), (NULL, 'vwx', NOW()), (NULL, 'yza', NOW())
, (NULL, 'def', NOW())
; SELECT * FROM test;
+----+------+---------------------+
| id | data | ts |
+----+------+---------------------+
| 1 | abc | 2008-01-18 16:28:40 |
| 2 | abc | 2008-01-18 16:28:40 |
| 3 | abd | 2008-01-18 16:28:40 |
| 4 | acd | 2008-01-18 16:28:40 |
| 5 | def | 2008-01-18 16:28:40 |
| 6 | pqr | 2008-01-18 16:28:40 |
| 7 | stu | 2008-01-18 16:28:40 |
| 8 | vwx | 2008-01-18 16:28:40 |
| 9 | yza | 2008-01-18 16:28:40 |
| 10 | def | 2008-01-18 16:28:40 |
+----+------+---------------------+

To see the effect of a query do the following steps:

  1. FLUSH STATUS;
  2. Execute the query
  3. SHOW SESSION STATUS LIKE 'handler_read%';
  4. Do an EXPLAIN of the query

HANDLER_READ_FIRST

The number of times the first entry was read from an index. If this value is high, it suggests that the server is doing a lot of full index scans.

+-------------+          +---+---+
| Table | | In|ex |
| | | | |
| | | | |
| | | | |
| | | | |
| | | v |
| | | |
| | | |
+-------------+ +-------+ SELECT data FROM test;
10 rows in set +-----------------------+-------+
| Variable_name | Value |
+-----------------------+-------+
| Handler_read_first | 1 |
| Handler_read_key | 0 |
| Handler_read_next | 10 |
+-----------------------+-------+ EXPLAIN SELECT data FROM test;
+----+-------------+-------+-------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | test | index | NULL | data | 35 | NULL | 10 | Using index |
+----+-------------+-------+-------+---------------+------+---------+------+------+-------------+

So what we can basically say is, that we had 1 full index scan and it did 10+1 index fetches.

Let us do some more examples

SELECT data FROM test WHERE data BETWEEN 'A' AND 'O';
6 rows in set +-----------------------+-------+
| Variable_name | Value |
+-----------------------+-------+
| Handler_read_first | 0 |
| Handler_read_key | 1 |
| Handler_read_next | 6 |
+-----------------------+-------+ +----+-------------+-------+-------+---------------+------+---------+------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+------+---------+------+------+--------------------------+
| 1 | SIMPLE | test | range | data | data | 35 | NULL | 5 | Using where; Using index |
+----+-------------+-------+-------+---------------+------+---------+------+------+--------------------------+

Here it seems the query is not starting with Handler_read_first though it could theoretically. Instead of we get a Handler_read_key. What we can also see is the "wrong" estimation of the optimizer in the execution plan.

Whit this example the query really could start from the beginning...

SELECT data FROM test WHERE data < 'O';
6 rows in set +-----------------------+-------+
| Variable_name | Value |
+-----------------------+-------+
| Handler_read_first | 0 |
| Handler_read_key | 1 |
| Handler_read_next | 6 |
+-----------------------+-------+ +----+-------------+-------+-------+---------------+------+---------+------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+------+---------+------+------+--------------------------+
| 1 | SIMPLE | test | range | data | data | 35 | NULL | 5 | Using where; Using index |
+----+-------------+-------+-------+---------------+------+---------+------+------+--------------------------

But it does not!

The same for this query:

SELECT data FROM test WHERE data LIKE 'a%';
4 rows in set +-----------------------+-------+
| Variable_name | Value |
+-----------------------+-------+
| Handler_read_first | 0 |
| Handler_read_key | 1 |
| Handler_read_next | 4 |
+-----------------------+-------+ +----+-------------+-------+-------+---------------+------+---------+------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+------+---------+------+------+--------------------------+
| 1 | SIMPLE | test | range | data | data | 35 | NULL | 4 | Using where; Using index |
+----+-------------+-------+-------+---------------+------+---------+------+------+--------------------------+

And this query does something completely different:

SELECT data FROM test WHERE data IN ('abc', 'abd', 'acd');
4 rows in set +-----------------------+-------+
| Variable_name | Value |
+-----------------------+-------+
| Handler_read_first | 0 |
| Handler_read_key | 3 |
| Handler_read_next | 4 |
+-----------------------+-------+ +----+-------------+-------+-------+---------------+------+---------+------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+------+---------+------+------+--------------------------+
| 1 | SIMPLE | test | range | data | data | 35 | NULL | 4 | Using where; Using index |
+----+-------------+-------+-------+---------------+------+---------+------+------+--------------------------+

I was not able to get any Handler_read_first count other than by a real full index scan. So I would say that a Handler_read_first is equivalent to Number of full index scans.

A full index scan is better than a full table scan but still not good because they burn a lot of CPU cycles. But sometimes you cannot avoid it...

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.

See also the examples in the previous chapter.

+-------------+          +-------+
| Table | | Index |
| | <------ | | <--+
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
+-------------+ +-------+ SELECT data FROM test where data = 'abc';
2 rows in set +-----------------------+-------+
| Variable_name | Value |
+-----------------------+-------+
| Handler_read_first | 0 |
| Handler_read_key | 1 |
| Handler_read_next | 2 |
+-----------------------+-------+ +----+-------------+-------+------+---------------+------+---------+-------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+-------+------+--------------------------+
| 1 | SIMPLE | test | ref | data | data | 35 | const | 2 | Using where; Using index |
+----+-------------+-------+------+---------------+------+---------+-------+------+--------------------------+

What makes me wondering in this example (an also in the previous) is, that based on the query there is IMHO no reason to access the table (row)...

SELECT * FROM test where data = 'pqr';
1 row in set +-----------------------+-------+
| Variable_name | Value |
+-----------------------+-------+
| Handler_read_first | 0 |
| Handler_read_key | 1 |
| Handler_read_next | 1 |
+-----------------------+-------+ +----+-------------+-------+------+---------------+------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+-------+------+-------------+
| 1 | SIMPLE | test | ref | data | data | 35 | const | 1 | Using where |
+----+-------------+-------+------+---------------+------+---------+-------+------+-------------+

In this example it makes clearly sense...!

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.

See also the examples in the previous chapters.

+-------------+          +-------+
| Table | | Index |
| | | |
| | <------ | + |
| | <------ | | |
| | <------ | v |
| | | |
| | | |
| | | |
+-------------+ +-------+

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.

+-------------+          +-------+
| Table | | Index |
| | | |
| | <------ | ^ |
| | <------ | | |
| | <------ | + |
| | | |
| | | |
| | | |
+-------------+ +-------+ SELECT data FROM test ORDER BY data DESC;
10 rows in set +-----------------------+-------+
| Variable_name | Value |
+-----------------------+-------+
| Handler_read_first | 0 |
| Handler_read_key | 0 |
| Handler_read_next | 0 |
| Handler_read_prev | 10 |
+-----------------------+-------+ +----+-------------+-------+-------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | test | index | NULL | data | 35 | NULL | 10 | Using index |
+----+-------------+-------+-------+---------------+------+---------+------+------+-------------+

There is no such status like Handler_read_last implemented like it could be according to the HANDLER functions [ 1 ].

SELECT * FROM test where data between 'A' and 'B' ORDER BY data DESC;
4 rows in set +-----------------------+-------+
| Variable_name | Value |
+-----------------------+-------+
| Handler_read_first | 0 |
| Handler_read_key | 1 |
| Handler_read_next | 0 |
| Handler_read_prev | 4 |
+-----------------------+-------+ +----+-------------+-------+-------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | test | range | data | data | 35 | NULL | 4 | Using where |
+----+-------------+-------+-------+---------------+------+---------+------+------+-------------+

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 don't use keys properly.

This status comes into account if the old file_sort mechanism is used [ 2 ].

To make this work we have to modify slightly our table:

ALTER TABLE test ADD COLUMN file_sort text;

UPDATE test SET file_sort = 'abcdefghijklmnopqrstuvwxyz' WHERE id = 1;
UPDATE test SET file_sort = 'bcdefghijklmnopqrstuvwxyza' WHERE id = 2;
UPDATE test SET file_sort = 'cdefghijklmnopqrstuvwxyzab' WHERE id = 3;
UPDATE test SET file_sort = 'defghijklmnopqrstuvwxyzabc' WHERE id = 4;
UPDATE test SET file_sort = 'efghijklmnopqrstuvwxyzabcd' WHERE id = 5;
UPDATE test SET file_sort = 'fghijklmnopqrstuvwxyzabcde' WHERE id = 6;
UPDATE test SET file_sort = 'ghijklmnopqrstuvwxyzabcdef' WHERE id = 7;
UPDATE test SET file_sort = 'hijklmnopqrstuvwxyzabcdefg' WHERE id = 8;
UPDATE test SET file_sort = 'ijklmnopqrstuvwxyzabcdefgh' WHERE id = 9;
UPDATE test SET file_sort = 'jklmnopqrstuvwxyzabcdefghi' WHERE id = 10;
SELECT * FROM test ORDER BY file_sort asc;
10 rows in set +-----------------------+-------+
| Variable_name | Value |
+-----------------------+-------+
| Handler_read_rnd | 10 |
| Handler_read_rnd_next | 11 |
+-----------------------+-------+ +----+-------------+-------+------+---------------+------+---------+------+------+----------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+
| 1 | SIMPLE | test | ALL | NULL | NULL | NULL | NULL | 10 | Using filesort |
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+

This is really a performance killer and should be avoided whenever possible!

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.

+------+------+          +-------+
| Table| | | Index |
| | | | |
| | | | |
| | | | |
| | | | |
| v | | |
| | | |
| | | |
+-------------+ +-------+ SELECT * FROM test;
10 rows in set +-----------------------+-------+
| Variable_name | Value |
+-----------------------+-------+
| Handler_read_rnd_next | 11 |
+-----------------------+-------+ +----+-------------+-------+------+---------------+------+---------+------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
| 1 | SIMPLE | test | ALL | NULL | NULL | NULL | NULL | 10 | |
+----+-------------+-------+------+---------------+------+---------+------+------+-------+

Obviously also filtering does not have a impact on the work which is performed:

SELECT * FROM test WHERE ts = '2008-01-18 17:33:39';
Empty set +-----------------------+-------+
| Variable_name | Value |
+-----------------------+-------+
| Handler_read_rnd_next | 11 |
+-----------------------+-------+ +----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | test | ALL | NULL | NULL | NULL | NULL | 10 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+

LITERATURE

  1. [ 1 ] HANDLER Syntax
  2. [ 2 ] File sort

OPEN ITEMS, MORE TO INVESTIGATE

  • What about Falcon, InnoDB, MySQL Cluster and and other Storage Engines?
  • Filesort and read_rnd_buffer_size
  • Why are all values + 1?
  • What about joins?

WHY ARE ALL VALUES + 1?

Roel Van de Paar gave me the following hint:

Values are +1 because of 'end of something' - for instance when you're reading from a data file, the server will try one more time to read the next record and this is what is being logged.

转自:http://www.fromdual.com/mysql-handler-read-status-variables

THE HANDLER_READ_* STATUS VARIABLES的更多相关文章

  1. Mysql命令show global status求根溯源

    近来,发现好多公司对mysql的性能监控是通过show global status实现的,因此对于这个命令想要探究一番,看他是否是实时更新的. 在此之前,我们必须搞明白mysql对于这个命令的执行过程 ...

  2. Show Global Status 整理

    原文来源:MySQL 5.5 Reference Manual 部分翻译取自:<MySQL_5.1中文参考手册> 转载请注明原文链接http://www.cnblogs.com/lenag ...

  3. MySQL - Show Global Status 整理

    2019独角兽企业重金招聘Python工程师标准>>> MySQL - Show Global Status 整理 原文来源:MySQL 5.5 Reference Manual 部 ...

  4. 有关binlog的那点事(mysql5.7.13)

    binlog作为mysql中最重要的日志之一,能实现异常恢复以及主从复制. 我们主要讨论的是主从复制中的binlog,这里将以mysql5.7.13的源码为主要依据来分析binlog. 在主从复制中, ...

  5. MySQL Range Optimization

    8.2.1.3 Range Optimization MYSQL的Range Optimization的目的还是尽可能的使用索引 The range access method uses a sing ...

  6. Linux 平台MySQL启动关闭方式总结

    MySQL的启动方法有很多种,下面对比.总结这几种方法的一些差异和特性,下面实验的版本为MySQL 5.6.如有疏漏或不足,敬请指点一二.   1:使用mysqld启动.关闭MySQL服务 mysql ...

  7. [MySQL Reference Manual] 8 优化

    8.优化 8.优化 8.1 优化概述 8.2 优化SQL语句 8.2.1 优化SELECT语句 8.2.1.1 SELECT语句的速度 8.2.1.2 WHERE子句优化 8.2.1.3 Range优 ...

  8. MySQL主从同步延迟

    早上接到open-falcon报警,一台mysql从库同步延迟2w多秒,mysql版本比较老,用的5.1.37. 连接从库查找原因: show processlist一下,查看哪些线程在跑. 看到Ti ...

  9. Nagios配置文件详解

    首先要看看目前Nagios的主配置路径下有哪些文件.[root@nagios etc]# ll总用量 152-rwxrwxr-x. 1 nagios nagios 1825 9月  24 14:40 ...

随机推荐

  1. iOS 使用XCode6打开项目以后再用XCode5出现的问题fatal error: malformed or corrupted AST file: 'Unable to load module

    使用不同版本的XCode出现的问题: fatal error: malformed or corrupted AST file: 'Unable to load module "/Users ...

  2. JavaScript:通过id来进行元素的取得

    每一个HTML元素都使用id来进行一个标注,随后可以通过document.getElementById(“ID名称”)取得指定的ID元素对象,取得元素对象之后就可以对其进行操作. 但是document ...

  3. saltstack之(二)软件包下载安装

    由于salt组件的安装依赖较多,最好使用yum源安装(不建议使用源码安装).由于试验环境限制,不能使用网络,故增加了实验的难度.下面分可以访问internet和不可以访问internet两方面介绍sa ...

  4. Java Map 按Key排序和按Value排序

    Map排序的方式有很多种,这里记录下自己总结的两种比较常用的方式:按键排序(sort by key), 按值排序(sort by value). 1.按键排序 jdk内置的java.util包下的Tr ...

  5. rsync传输性能测试总结 转

    测试环境 1.1服务器硬件信息 1.2 服务器软件信息 1.3 Rsync所能够支持的功能 (1)支持断点续传 (2)支持使用ssh传输加密 (3)支持128位MD4校验(3.0以后版本使用MD5加密 ...

  6. mysql运行参数详解

    1, 查看MySQL服务器配置信息 mysql> show variables; 2, 查看MySQL服务器运行的各种状态值 mysql> show global status; 3, 慢 ...

  7. strtok&strsep

    strtok,strtok_r,strsep--extract tokens from strings Tje strsep() function was introduced as a replac ...

  8. HTTPS and the TLS handshake protocol阅读笔记

    目的 为能够透彻理解HTTPS报文交互过程,做此笔记. 本文大部分内容来自 : http://albertx.mx/blog/https-handshake/ http://www.cnblogs.c ...

  9. mysql:sql行列转换

    今天一个同学遇到一个问题问我了,由于本人平时学习的mysql比较基础,确实没解决,后来google了一下,才知道是sql的一种技法[行列转换],话不多说先上图: 想得到下面的结果: +------+- ...

  10. XMPP协议介绍

    一.xmpp协议工作原理 二.XMPP地址模式 三.xmpp消息格式 xmpp xml包括三个元素,message,presence,iq,也翻译做三种节. 在'jabber:client'和'jab ...