轉自博客http://www.path8.net/tn/archives/5613
1.監控語法:
在MySQL里,使用SHOW STATUS查詢服務器狀態,語法一般來說如下:
SHOW [GLOBAL | SESSION] STATUS [LIKE 'pattern' | WHERE expr]
執行命令后會看到很多內容,其中有一部分是Handler_read_*,它們顯示了數據庫處理SELECT查詢語句的狀態,對於調試SQL語句有很大意義。
mysql> show session status like 'Handler_read%';
+-----------------------+-------+
| Variable_name | Value |
+-----------------------+-------+
| Handler_read_first | 0 |
| Handler_read_key | 1 |
| Handler_read_last | 0 |
| Handler_read_next | 1 |
| Handler_read_prev | 0 |
| Handler_read_rnd | 0 |
| Handler_read_rnd_next | 0 |
+-----------------------+-------+
7 rows in set (0.00 sec)
2.測試流程及用例
每次執行SQL時按照如下過程執行:
FLUSH STATUS;
SELECT ...;
SHOW SESSION STATUS LIKE 'Handler_read%';
EXPLAIN SELECT ...;
測試用例:
mysql> show create table artist\G;
*************************** 1. row ***************************
Table: artist
Create Table: CREATE TABLE `artist` (
`artist_id` int(10) unsigned NOT NULL,
`type` enum('Band','Person','Unknown','Combination') NOT NULL,
`name` varchar(255) NOT NULL,
`name_reverse` varchar(255) DEFAULT NULL,
`gender` enum('Male','Female') DEFAULT NULL,
`founded` year(4) DEFAULT NULL,
`country_id` smallint(5) unsigned DEFAULT NULL,
PRIMARY KEY (`artist_id`),
KEY `name` (`name`),
KEY `idx_founded` (`founded`),
KEY `type` (`type`),
KEY `idx_name_reverse` (`name_reverse`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
1 row in set (0.00 sec)
3.參數測試
3.1 Handler_read_first
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.
此選項表明SQL是在做一個全索引掃描,注意是全部,而不是部分,所以說如果存在WHERE語句,這個選項是不會變的。如果這個選項的數值很大,既是好事也是壞事。說它好是因為畢竟查詢是在索引里完成的,而不是數據文件里,說它壞是因為大數據量時,即使是索引文件,做一次完整的掃描也是很費時間的。
mysql> flush status;
Query OK, 0 rows affected (0.00 sec)
mysql> select name from artist;
......
577983 rows in set (1.50 sec)
mysql> show session status like 'Handler_read%';
ERROR 1317 (70100): Query execution was interrupted
mysql> show session status like 'Handler_read%';
+-----------------------+--------+
| Variable_name | Value |
+-----------------------+--------+
| Handler_read_first | 1 |
| Handler_read_key | 1 |
| Handler_read_last | 0 |
| Handler_read_next | 577983 |
| Handler_read_prev | 0 |
| Handler_read_rnd | 0 |
| Handler_read_rnd_next | 0 |
+-----------------------+--------+
7 rows in set (0.00 sec)
mysql> explain select name from artist\G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: artist
type: index
possible_keys: NULL
key: name
key_len: 257
ref: NULL
rows: 585801
Extra: Using index
1 row in set (0.00 sec)
使用了只讀索引。
3.2 Handler_read_key
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.
此選項數值如果很高,那么恭喜你,你的系統高效的使用了索引,一切運轉良好。
mysql> flush status;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from artist where name='Enya';
+-----------+--------+------+--------------+--------+---------+------------+
| artist_id | type | name | name_reverse | gender | founded | country_id |
+-----------+--------+------+--------------+--------+---------+------------+
| 88 | Person | Enya | aynE | Female | 1961 | 103 |
+-----------+--------+------+--------------+--------+---------+------------+
1 row in set (0.00 sec)
mysql> show session status like 'Handler_read%';
+-----------------------+-------+
| Variable_name | Value |
+-----------------------+-------+
| Handler_read_first | 0 |
| Handler_read_key | 1 |
| Handler_read_last | 0 |
| Handler_read_next | 1 |
| Handler_read_prev | 0 |
| Handler_read_rnd | 0 |
| Handler_read_rnd_next | 0 |
+-----------------------+-------+
7 rows in set (0.00 sec)
mysql> explain select * from artist where name='Enya'\G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: artist
type: ref
possible_keys: name
key: name
key_len: 257
ref: const
rows: 1
Extra: Using index condition
1 row in set (0.00 sec)
使用了索引下推技術。
3.3 Handler_read_last
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.
3.4 Handler_read_next
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.
此選項表明在進行索引掃描時,按照索引從數據文件里取數據的次數。
mysql> flush status;
Query OK, 0 rows affected (0.00 sec)
mysql> select name from artist order by name;
......
577983 rows in set (1.55 sec)
mysql> show session status like 'Handler_read%';
+-----------------------+--------+
| Variable_name | Value |
+-----------------------+--------+
| Handler_read_first | 1 |
| Handler_read_key | 1 |
| Handler_read_last | 0 |
| Handler_read_next | 577983 |
| Handler_read_prev | 0 |
| Handler_read_rnd | 0 |
| Handler_read_rnd_next | 0 |
+-----------------------+--------+
7 rows in set (0.00 sec)
mysql> explain select name from artist order by name\G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: artist
type: index
possible_keys: NULL
key: name
key_len: 257
ref: NULL
rows: 585801
Extra: Using index
1 row in set (0.00 sec)
3.4 Handler_read_prev
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_next是ORDER BY ... ASC的方式取數據。
mysql> flush status;
Query OK, 0 rows affected (0.00 sec)
mysql> select name from artist order by name desc;
......
577983 rows in set (1.55 sec)
mysql> show session status like 'Handler_read%';
+-----------------------+--------+
| Variable_name | Value |
+-----------------------+--------+
| Handler_read_first | 0 |
| Handler_read_key | 1 |
| Handler_read_last | 1 |
| Handler_read_next | 0 |
| Handler_read_prev | 577983 |
| Handler_read_rnd | 0 |
| Handler_read_rnd_next | 0 |
+-----------------------+--------+
7 rows in set (0.00 sec)
mysql> explain select name from artist order by name desc\G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: artist
type: index
possible_keys: NULL
key: name
key_len: 257
ref: NULL
rows: 585801
Extra: Using index
1 row in set (0.00 sec)
3.5 Handler_read_rnd
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.
簡單的說,就是查詢直接操作了數據文件,很多時候表現為沒有使用索引或者文件排序。
mysql> flush status;
Query OK, 0 rows affected (0.00 sec)
mysql> flush status;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from artist order by country_id asc;
577983 rows in set (1.54 sec)
mysql> show session status like 'Handler_read%';
ERROR 1317 (70100): Query execution was interrupted
mysql> show session status like 'Handler_read%';
+-----------------------+--------+
| Variable_name | Value |
+-----------------------+--------+
| Handler_read_first | 1 |
| Handler_read_key | 1 |
| Handler_read_last | 0 |
| Handler_read_next | 0 |
| Handler_read_prev | 0 |
| Handler_read_rnd | 0 |
| Handler_read_rnd_next | 577984 |
+-----------------------+--------+
7 rows in set (0.00 sec)
mysql> explain select country_id from artist order by country_id asc\G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: artist
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 585801
Extra: Using filesort
1 row in set (0.00 sec)
3.6 Handler_read_rnd_next
Handler_read_rnd_next表示“在數據文件中讀下一行的請求數。如果你正進行大量的表掃描,該值較高。通常說明你的表索引不正確或寫入的查詢沒有利用索引。”
這個說明跟你的SQL語句有很大的關系,你可以通過explain工具或者是慢查詢日志找出對應的慢SQL,並對執行慢的SQL語句進行調試,直到找到最優的執行計划,這樣Handler_read_rnd_next的值就會往下降了。
很多時候,為了完成一個查詢任務,我們往往可以寫出幾種查詢語句,這時,你不妨挨個按照上面的方式執行,根據結果中的Handler_read_*數值,你就能相對容易的判斷各種查詢方式的優劣。
還有一種監控的方法就是profile
mysql> show variables like 'profil%';
+------------------------+-------+
| Variable_name | Value |
+------------------------+-------+
| profiling | OFF |
| profiling_history_size | 15 |
+------------------------+-------+
2 rows in set (0.01 sec)
mysql> set profiling=on;
Query OK, 0 rows affected, 1 warning (0.00 sec)
mysql> show profiles;
+----------+------------+-------------------------------------------------------+
| Query_ID | Duration | Query |
+----------+------------+-------------------------------------------------------+
| 1 | 2.94708450 | select country_id from artist order by country_id asc |
+----------+------------+-------------------------------------------------------+
1 row in set, 1 warning (0.00 sec)
mysql> show profile;
+----------------------+----------+
| Status | Duration |
+----------------------+----------+
| starting | 0.000201 |
| checking permissions | 0.000023 |
| Opening tables | 0.000062 |
| init | 0.000069 |
| System lock | 0.000035 |
| optimizing | 0.000011 |
| statistics | 0.000030 |
| preparing | 0.000019 |
| Sorting result | 0.000014 |
| executing | 0.000010 |
| Sending data | 0.000044 |
| Creating sort index | 1.601273 |
| end | 0.000070 |
| query end | 0.000026 |
| closing tables | 0.000029 |
| freeing items | 1.344915 |
| logging slow query | 0.000207 |
| cleaning up | 0.000048 |
+----------------------+----------+
18 rows in set, 1 warning (0.00 sec)