我发现自己陷入了困境。我有一个用于页面命中跟踪的表,有近1.05亿行。(!)看起来像这样:
CREATE TABLE `media_hits` (
`id` int(10) unsigned NOT NULL auto_increment,
`media_code` char(7) NOT NULL,
`day` date NOT NULL,
`hits` int(10) unsigned NOT NULL default '0',
PRIMARY KEY (`id`),
UNIQUE KEY `media_code` (`media_code`,`day`)
) ENGINE=InnoDB;
您可以想象在此表上运行任何类型的查询都需要很长时间。典型的查询如下:
SELECT DISTINCT(`media_code`), COUNT(*) AS c
FROM `media_hits`
WHERE `day` >= DATE_SUB(NOW(), INTERVAL 1 DAY)
GROUP BY(`media_code`)
ORDER BY c DESC
LIMIT 200;
这个查询需要永远。对查询的解释给了我这个:
id: 1
select_type: SIMPLE
table: media_hits
type: index
possible_keys: NULL
key: media_code
key_len: 10
ref: NULL
rows: 104773158
Extra: Using where; Using index; Using temporary; Using filesort
这简直太糟糕了。所以我的问题是:我能做些什么呢?现在尝试添加适当的索引是不可能的。 ALTER TABLE查询可能需要一周时间才能运行。我尝试删除超过6个月的行,但24小时后该查询仍在运行。
我需要解决这个问题。我唯一想到的就是创建一个带有适当索引的新表,然后开始在该表中记录匹配。在后台我可以有一个脚本从旧的media_hits表中慢慢插入记录。任何人都可以提供有关如何索引此表的建议,并可能提供一些关于我应该索引哪些列的提示吗?
答案 0 :(得分:2)
对于这种工作,单独编制索引很可能对你没什么帮助。更好地考虑某种缓存策略,其中一些附加表存储了您需要的聚合。
例如,对于上面的查询,您可以添加第二个表“media_code_per_day”,其中包含3列“media_code”,“counter”和“date”。每次在原始表中插入一行时,也要相应地更新“media_code_per_day”。然后,您可以在“media_code_per_day”而不是原始查询上运行新查询。
当然,要在您的情况下初始化新表,您必须进行一次批量运行一次所有现有行,但这只需要一次。
答案 1 :(得分:2)
对于该特定查询,(day,media_code)上的索引最有帮助。它仍然必须使用临时表,因为group by和do filesort,因为你按count(*)排序,但该索引将减少它必须显着扫描的行数。
如果你需要比这更好的性能,那么你可能必须这样做,因为@DocBrown说并制作一些聚合表。但是在进行额外表的所有工作之前,我会首先尝试索引以查看它是否足够有用。
此外,如果要慢慢清理旧行而不必运行需要数天的大删除,则可以为删除查询添加限制。您可以批量删除它们(例如一次10K或100K行)以缓慢缩小该表的大小,直到它足够小以添加索引。
答案 2 :(得分:0)
您还可以查看vertica社区版。像
这样简单的东西SELECT count(*) FROM event_track;
count
------------
1595756573
(1 row)
在最近未提交查询的系统上在6秒内返回。是的,接近16亿行,并且我在一个非常合理的响应时间内(通常是几秒钟,通常是几分钟),一直在查询上面提到的那个。
好消息是,在将实时数据从mysql转储到一个巨大的csv文件后,使用单个COPY FROM命令将其快速轻松地导入到vertica中。
https://dba.stackexchange.com/a/35614/20451详细了解了如何下载vertica。