1

毎日リクエストを行った個別の IP アドレスを示す次のクエリがあります。

SELECT COUNT(DISTINCT ip_address) as ip_address, DATE(exec_datetime) as day
FROM requests
GROUP BY MONTH(exec_datetime), DAY(exec_datetime);

の出力EXPLAINは次のとおりです

id  select_type table   type    possible_keys   key key_len ref rows    Extra
1   SIMPLE  requests    ALL NULL    NULL    NULL    NULL    472043  Using filesort

インデックスを作成したとき、クエリが完了するまでに時間がかかったので、インデックスをカバーすることについて明確に理解していません。

ALTER TABLE requests ADD INDEX unique_ip_per_time(ip_address, exec_datetime);

これがの出力ですEXPLAIN

id  select_type table   type    possible_keys   key key_len ref rows    Extra
1   SIMPLE  requests    index   NULL    unique_ip_per_time  268 NULL    472043  Using index; Using filesort

インデックスを作成するか書き直して、このクエリを最適化するにはどうすればよいでしょうか?

編集

実行時間は、両方のステートメント (カバー インデックスの有無) で約 15 秒です。このテーブルの唯一の他のキーはUNIQUEサロゲートとINDEXオンですip_address

show indexes from requests

Table   Non_unique  Key_name    Seq_in_index    Column_name Collation   Cardinality Sub_part    Packed  Null    Index_type  Comment Index_comment
requests    0   PRIMARY 1   request_id  A   386577  NULL    NULL        BTREE       
requests    1   ip_address  1   ip_address  A   193288  NULL    NULL    YES BTREE       
requests    1   unique_ip_per_time  1   ip_address  A   163 NULL    NULL    YES BTREE       
requests    1   unique_ip_per_time  2   exec_datetime   A   163 NULL    NULL    YES BTREE       

編集2

私はEisbergの指示に従いましたが、このクエリには約1.1秒かかります...

EXPLAIN SELECT
  A.request_day,
  (
    SELECT COUNT(DISTINCT B.ip_address)
    FROM requests B
    WHERE B.exec_date = A.request_day
  ) as num_ip_addr
FROM request_days A
ORDER BY A.request_day ASC;

これは、約 0.9 秒かかるこのクエリよりもわずかに遅いです

SELECT COUNT(DISTINCT ip_address) as ip_address, exec_date
FROM requests
GROUP BY exec_date;

日付を含む追加のテーブルを作成する必要はないと思います。ステートメントの一部に適用できる最適化はありますかDISTINCT ip_address(ボトルネックのようです)?

4

3 に答える 3

1

この種の問題に対する小さな回避策を作成しました。しかし、あなたはそれにいくつかの仕事を入れる必要があります。

まず、選択中の余分な計算を避けるために、リクエストに応じて追加の列を作成します。

ALTER TABLE requests ADD COLUMN (request_day DATE);

ALTER TABLE requests ADD INDEX i1(request_day);

UPDATE requests SET request_day = DATE(exec_datetime);

選択できる/したい日を記憶するために、追加のテーブルが必要になります。

CREATE TABLE request_days (
  request_day DATE
);

ALTER TABLE request_days ADD UNIQUE INDEX i1(request_day);

INSERT IGNORE INTO request_days SELECT DATE(exec_datetime) FROM requests;

最後に、次のことができます。

EXPLAIN
SELECT
  A.request_day,
  (
    SELECT COUNT(DISTINCT B.ip_address)
    FROM requests B
    WHERE B.request_day = A.request_day
  )
FROM request_days A
ORDER BY A.request_day DESC

これにより、次のことが得られます。

ID  SELECT_TYPE         TABLE   TYPE    POSSIBLE_KEYS   KEY KEY_LEN REF                         ROWS    EXTRA
1   PRIMARY             A       index   (null)          i1  4       (null)                      1       Using index
2   DEPENDENT SUBQUERY  B       ref     i1              i1  4       db_2_95a42.A.request_day    1       Using where

これがお役に立てば幸いです。

SQL Fiddle の例: http://sqlfiddle.com/#!2/95a42/2

于 2012-11-23T08:51:18.733 に答える
0

理想的には、次のような複合機能インデックスを追加するだけで済みます。

CREATE INDEX month_day_idx
   ON requests (MONTH(exec_datetime), DAY(exec_datetime));

残念ながら、MySQL は関数インデックスをサポートしていません。代わりに、次の 2 つの選択肢があります。

  1. 月と日の追加の列を作成し、これら 2 つの新しいフィールドで複合インデックスを作成します。

  2. または、可能であれば関数を使用しないように GROUP BY を変更します。

于 2012-11-23T08:56:42.037 に答える
0

で DATE 関数を使用しているためexec_datetime、エンジンはテーブルのすべての行をスキャンします。http://dev.mysql.com/doc/refman/5.1/en/partitioning.htmlを試す必要がありますpartitioning the table on exec_datetime

于 2012-11-23T08:38:03.263 に答える