3

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 のいくつかの調査:

  1. コマンド 'SHOW PROCESS' を実行すると、クエリの状態は 'Sending data' に保持されます
    • サーバーのメモリを確認しましたが、まだ十分に使用可能です。
    • クエリを実行する直前に SESSION バッファ 'read_buffer_size'、'read_rnd_buffer_size'、'sort_buffer_size' を非常に大きな量 (16MB まで) に拡張しましたが、役に立ちません。
    • また、列 'id' ( SELECT id FROM syslog ....) のみをクエリしますが、同じ結果になります。
  2. クエリの実行中に同じクエリが発生しましたが、別の mysql 接続で row_count<5 (例: 'LIMIT 5') で、後者の戻りはまだ非常にすぐです。
  3. 異なる条件で、たとえば、時間範囲を拡張して、結果の行数149BETWEEN '2013-10-03' to '2013-11-05'を取得します。で、速いです。では、非常に遅いです。とても奇妙。LIMIT 140LIMIT 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))
);
4

1 に答える 1

6

ORDER BY DESC + LIMIT Mysql は句の周りの動作で有名です。

参照: http://www.mysqlperformanceblog.com/2006/09/01/order-by-limit-performance-optimization/

してみてください:

SELECT * 
FROM syslog FORCE INDEX (Facility)
WHERE 
    ReportedTime BETWEEN '2013-11-04' AND '2013-11-05' 
AND Priority<3 
AND Facility=1 
ORDER BY id DESC 
LIMIT 7;

最初のクエリで使用されるインデックスの使用を強制する必要があります。(Explain Plan の KEY 列から取得)

于 2013-11-06T19:01:52.253 に答える