我发现自己陷入了困境。我有一个用于跟踪页面命中率的表,其中包含近 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;
此查询需要很长时间。查询中的 EXPLAIN 给了我这个:
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 表中插入记录。任何人都可以提供有关如何索引此表的建议,并可能提供一些有关我应该索引哪些列的提示吗?
最佳答案
对于这种工作,单靠索引很可能帮不上什么忙。最好考虑某种缓存策略,使用一些额外的表来存储您需要的聚合。
例如,对于上面的查询,您可以添加第二个表“media_code_per_day”,其中包含 3 列“media_code”、“counter”和“date”。每次在原始表中插入一行时,也要相应地更新“media_code_per_day”。然后,您可以对“media_code_per_day”运行新查询,而不是原来的查询。
当然,要根据您的情况初始化新表,您必须对所有现有行进行一次批处理,但这只需要一次。
关于MySQL:具有 100+ 百万行的索引表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3892623/