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代理和INDEXonip_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

由于您在 上使用 DATE 函数exec_datetime,因此引擎将扫描表的所有行。你应该试试partitioning the table on exec_datetime http://dev.mysql.com/doc/refman/5.1/en/partitioning.html

于 2012-11-23T08:38:03.263 回答
0

理想情况下,您只需要像这样添加复合功能索引:

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

不幸的是,MySQL 不支持功能索引。相反,您有 2 个选择:

  1. 为月和日创建额外的列,并使用这两个新字段创建复合索引。

  2. 或者如果可以的话,改变你的 GROUP BY 以不使用函数。

于 2012-11-23T08:56:42.037 回答