我有以下查询,显示每天发出请求的不同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;
这比这个需要大约.9秒
的查询稍慢SELECT COUNT(DISTINCT ip_address) as ip_address, exec_date
FROM requests
GROUP BY exec_date;
我认为我不需要使用日期创建附加表。是否有任何优化可以应用于DISTINCT ip_address
的部分语句(这似乎是瓶颈)?
答案 0 :(得分: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小提琴的示例:http://sqlfiddle.com/#!2/95a42/2
答案 1 :(得分:0)
由于您在exec_datetime
上使用DATE功能,引擎将扫描表格的所有行。
您应该尝试partitioning the table on exec_datetime
http://dev.mysql.com/doc/refman/5.1/en/partitioning.html
答案 2 :(得分:0)
理想情况下,您只需添加复合功能索引,如下所示:
CREATE INDEX month_day_idx
ON requests (MONTH(exec_datetime), DAY(exec_datetime));
不幸的是,MySQL不支持功能索引。相反,您有两个选择:
为月和日创建额外的列,并使用这2个新字段创建复合索引。
如果可以,可以改变GROUP BY不使用功能。