MySQL 5.6、64 ビット、RHEL 5.8
ORDER BY および LIMIT 'row_count' (または LIMIT 0,'row_count') を使用した大きなテーブルに対するクエリ。「row_count」が結果セットの実際のカウントよりも大きい場合、非常に遅くなります。
ケース 1: 以下のクエリは非常に高速です ('LIMIT' なし):
mysql> SELECT * FROM syslog WHERE
(ReportedTime BETWEEN '2013-11-04' AND '2013-11-05') AND
Priority<3 AND Facility=1 ORDER BY id DESC;
+---
| ...
6 rows in set (0.01 sec)
ケース 2: 以下のクエリも高速です ('LIMIT 5'):
mysql> SELECT * FROM syslog WHERE
(ReportedTime BETWEEN '2013-11-04' AND '2013-11-05') AND
Priority<3 AND Facility=1 ORDER BY id DESC LIMIT 5;
+---
| ...
5 rows in set (0.42 sec)
ケース 3: 以下のクエリは非常に遅いです ('LIMIT 7'、'row_count' 値 > 6 を使用する場合があります):
mysql> SELECT * FROM syslog WHERE
(ReportedTime BETWEEN '2013-11-04' AND '2013-11-05') AND
Priority<3 AND Facility=1 ORDER BY id DESC LIMIT 7;
+---
| ...
6 rows in set (28 min 7.24 sec)
違いは、個体(No LIMIT)、「LIMIT 5」、「LIMIT 7」だけです。
ケース 3 が遅いのはなぜですか?
ケース 3 のいくつかの調査:
- コマンド 'SHOW PROCESS' を実行すると、クエリの状態は 'Sending data' に保持されます
- サーバーのメモリを確認しましたが、まだ十分に使用可能です。
- クエリを実行する直前に SESSION バッファ 'read_buffer_size'、'read_rnd_buffer_size'、'sort_buffer_size' を非常に大きな量 (16MB まで) に拡張しましたが、役に立ちません。
- また、列 'id' (
SELECT id FROM syslog ....
) のみをクエリしますが、同じ結果になります。
- クエリの実行中に同じクエリが発生しましたが、別の mysql 接続で row_count<5 (例: 'LIMIT 5') で、後者の戻りはまだ非常にすぐです。
- 異なる条件で、たとえば、時間範囲を拡張して、結果の行数149
BETWEEN '2013-10-03' to '2013-11-05'
を取得します。で、速いです。では、非常に遅いです。とても奇妙。LIMIT 140
LIMIT 150
現在のところ、私たちのウェブサイトでは、プログラムは最初に実際の結果の行数を取得し ( SELECT COUNT(*) FROM ...
、ORDER BY なし、LIMIT なし)、その後、取得した実際の行数を超えない LIMIT 'row_count' 値でクエリを実行します。醜い。
ケース 3 の EXPLAIN:
-+-----..-+----..+-------+-----..+--------+---------+-----+-----+------------+
| sele.. | table| type | poss..| key | key_len | ref | rows| Extra |
-+-----..-+----..+-------+-----..+--------+---------+-----+-----+------------+
| SIMPLE | syslo| index | ... | PRIMARY| 8 | NULL| 132 | Using where|
-+-----..-+----..+-------+-----..+--------+---------+-----+-----+------------+
1 row in set (0.00 sec)
テーブル定義:
CREATE TABLE syslog (
id BIGINT NOT NULL AUTO_INCREMENT,
ReceivedAt TIMESTAMP NOT NULL DEFAULT 0,
ReportedTime TIMESTAMP NOT NULL DEFAULT 0,
Priority SMALLINT,
Facility SMALLINT,
FromHost VARCHAR(60),
Message TEXT,
InfoUnitID INT NOT NULL DEFAULT 0,
SysLogTag VARCHAR(60) NOT NULL DEFAULT '',
PRIMARY KEY (id),
KEY idx_ReportedTime_Priority_id (ReportedTime,Priority,id),
KEY idx_Facility (Facility),
KEY idx_SysLogTag (SysLogTag(16)),
KEY idx_FromHost (FromHost(16))
);