我正在运行一个已经构建了6亿行并且正在快速增长的表,这一直在减慢需要尽快运行的查询。当前架构是:
CREATE TABLE `user_history` (
`userId` int(11) NOT NULL,
`asin` varchar(10) COLLATE utf8_unicode_ci NOT NULL,
`dateSent` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
KEY `userId` (`userId`,`asin`,`dateSent`),
KEY `dateSent` (`dateSent`,`asin`),
KEY `asin` (`asin`,`dateSent`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
我读到的关于分区的所有内容都表明这是按日期范围进行分区的主要候选者。我们只倾向于使用最近14天的数据,但客户端不想删除旧数据。新架构如下所示:
CREATE TABLE `user_history_partitioned` (
`userId` int(11) NOT NULL,
`asin` varchar(10) COLLATE utf8_unicode_ci NOT NULL,
`dateSent` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`dateSent`,`asin`,`userId`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
PARTITION BY RANGE ( UNIX_TIMESTAMP(dateSent) ) (
PARTITION Apr2013 VALUES LESS THAN (UNIX_TIMESTAMP('2013-05-01')),
etc...
PARTITION Mar2014 VALUES LESS THAN (UNIX_TIMESTAMP('2014-04-01')),
PARTITION Apr2014 VALUES LESS THAN (UNIX_TIMESTAMP('2014-05-01')),
PARTITION May2014 VALUES LESS THAN (UNIX_TIMESTAMP('2014-06-01')),
PARTITION Future VALUES LESS THAN MAXVALUE);
Future分区的想法是因为在填充的分区上运行REORGANIZE PARTITION需要很长时间才能完成。所以Future将永远是空的,可以立即重新组织成新的分区。使用此表的其他查询已重新排序,仅使用主键,以减少表上的索引数。
时间要求严格的查询是:
SELECT SQL_NO_CACHE *
FROM books B
WHERE (non-relevant stuff deleted)
AND NOT EXISTS
(
SELECT 1 FROM user_history H
WHERE
H.userId=$userId
AND H.asin=B.ASIN
AND dateSent > DATE_SUB(NOW(), INTERVAL 14 DAY)
)
AND (non-relevant stuff deleted)
LIMIT 1
因此,我们要避免在过去14天内为同一用户选择的重复项。并且这当前返回< 0.1秒,这可以,但比以前的架构要慢。
对于新架构,内部SELECT已重新排序为:
SELECT 1 FROM user_history_partitioned H
WHERE dateSent > DATE_SUB(NOW(), INTERVAL 14 DAY)
AND H.asin=B.ASIN
AND H.userId=$userId
每次查询需要5分钟。我不明白为什么。这个想法是当前的分区和索引应该在内存中(或者也许是前一个月,在一个月的某些时候),主索引覆盖WHERE子句。但是从它开始的时候开始,它可以在asin或userId上执行全表扫描。这很难从EXPLAIN中识别出来,因为它是一个内部查询。
我错过了什么?我是否需要另一个组合索引(asin,userID)?如果是这样,为什么?
谢谢,
PS:尝试将DATE_SUB(...)包装为UNIX_TIMESTAMP(DATE_SUB(...)),以防它是类型转换问题,但没有区别。