我有以下查询:
SELECT
`date`,
al.cost,
SUM(l.bnd) AS bnd,
l.type,
count(*) AS amount
FROM alogs AS al
INNER JOIN logs AS l ON al.logid = l.id
WHERE
aid = 0 AND
l.`date` >= '2010-01-17' AND
l.`date` <= '2011-04-19'
GROUP BY l.`date`, l.type
日志计数500万行
alogs有430万行
执行时间约为90秒。
我有:
logs.id(auto inc)上的主键
logs.date上的索引(BTREE)
alogs.logid上的索引(BTREE)
alogs.aid上的索引(BTREE)
我试过了:
- logs.type上的索引(BTREE),但没有改进任何东西(我认为因为类型只能是2件事)
- logs.date和logs.type的覆盖索引
- 在内部每月对日志表进行分区,但是使用上面使用的时间跨度(覆盖所有分区)它甚至会变得更慢,无法对辅助进行分区,因为有超过2k个不同的ID
- 从查询中删除功能,直到找到问题所在的位置变得很快
我只需要删除GROUP子句(以及SUM()和count(*)函数来获得正确的结果),这样就可以将执行时间推迟到亚秒级。
- 删除内存中的group子句和组,但超过300万行的结果太多,甚至需要更长的时间。
我还能做其他事吗但我不知道吗?如果是这样,我很乐意听到它!
谢谢,
lordstyx
编辑(2011-04-22 11:30) 这是EXPLAIN EXTENDED结果id| select_type| table| type | possible_keys| key | key_len| ref | rows | Extra
1 | SIMPLE | al | ref | logid,aid | adid | 4 | const | 3010624| Using temporary; Using filesort
1 | SIMPLE | l | eq_ref| PRIMARY,date | PRIMARY| 4 | al.logid| 1 | Using where
答案 0 :(得分:0)
如果日期范围是日志表中的一小部分行,则希望它使用该索引。你说你在logs.date上创建了一个索引,但你需要一个复合索引(logs.date,logs.id),所以mysql不必读取磁盘上的行来获取加入alogs表的id 。你想要一个关于连接的alogs(log_id)的索引。
你也可以通过将SELECT中的列放在索引中来挤出更多,所以
logs(date, id, bnd, type)
alogs(log_id, aid, cost)
答案 1 :(得分:0)
如果aid
过滤器删除了很大一部分行,这样的内容会减少正在加入的数据:
SELECT
l.`date`,
sum(al.cost) as cost,
SUM(l.bnd) AS bnd,
l.type,
sum(qty) AS amount
FROM logs AS l
INNER JOIN
(
SELECT logid, sum(cost) as cost, COUNT(*) as qty
FROM alogs
WHERE aid = 0
GROUP BY logid
) al ON al.logid = l.id
GROUP BY l.`date`, l.type
在不了解数据结构的情况下(每alogs
条记录多个logs
条记录?),很难建议进一步改进。在连接和GROUP BY
子句之前计算数据可以通过减少需要处理的行总数来大大加快执行速度。由于没有分组的查询返回速度非常快,因此进一步索引和调整不太可能提高执行速度。