我的桌子每天增长数千万行。表中的行包含有关页面视图流量的每小时信息。
表格中的索引位于网址和日期时间。
我希望按天而不是每小时汇总信息。我该怎么做?这是一个例子,说明了我要做的事情:
SELECT url, sum(pageviews), sum(int_views), sum(ext_views)
FROM news
WHERE datetime >= "2012-08-29 00:00:00" AND datetime <= "2012-08-29 23:00:00"
GROUP BY url
ORDER BY pageviews DESC
LIMIT 10;
但上述查询永远不会完成。表中有数百万行。有没有更有效的方法可以获得这些汇总数据?
答案 0 :(得分:6)
每天有数百万行 非常多。
假设:
url
的类型为TEXT
,平均(Punycode)长度为〜77 characters; pageviews
的类型为INT
; int_views
的类型为INT
; ext_views
的类型为INT
;和datetime
的类型为DATETIME
然后每天的数据将占据大约9.9×10 8 字节,几乎是1GiB /天。实际上可能会更多,因为上述假设非常保守。
MySQL的maximum table size由其数据文件所在的底层文件系统决定。如果您正在使用MyISAM引擎(如下面的评论所示)而不在Windows或Linux上进行分区,那么限制几个GiB并不罕见;这意味着该表将在一个工作周内达到其容量!
提到@Gordon Linoff,你应该partition你的桌子;但是,每个表都有limit个1024个分区。每天使用1个分区(在您的情况下会非常敏感),在分区开始重用之前,您将被限制在一个表中存储3年以下的数据。
因此,我建议您将每年的数据保存在自己的表中,每个数据按日分区。此外,作为@Ben explained,(datetime, url)
上的复合索引会有所帮助(我实际上建议从date
创建一个DATE(datetime)
列并对其进行索引,因为它将启用MySQL CREATE TABLE news_2012 (
INDEX (date, url(100))
)
Engine = MyISAM
PARTITION BY HASH(TO_DAYS(date)) PARTITIONS 366
SELECT *, DATE(datetime) AS date FROM news WHERE YEAR(datetime) = 2012;
CREATE TRIGGER news_2012_insert BEFORE INSERT ON news_2012 FOR EACH ROW
SET NEW.date = DATE(NEW.datetime);
CREATE TRIGGER news_2012_update BEFORE UPDATE ON news_2012 FOR EACH ROW
SET NEW.date = DATE(NEW.datetime);
3}}执行查询时的分区);并且,如果行级锁定和事务完整性对您来说并不重要(对于这种类型的表,它们可能不是),使用MyISAM可能并不愚蠢:
UNION
如果您选择使用MyISAM,则不仅可以存档已完成的年份(使用prune),还可以使用包含所有SELECT
的{{3}}替换原始表格。您的基础年表(在InnoDB中也可以使用的替代方法是创建myisampack
,但它仅对UNION
语句有用,因为DROP TABLE news;
CREATE TABLE news (
date DATE,
INDEX (date, url(100))
)
Engine = MERGE
INSERT_METHOD = FIRST
UNION = (news_2012, news_2011, ...)
SELECT * FROM news_2012 WHERE FALSE;
视图既不可更新也不可插入:
SELECT url, SUM(pageviews), SUM(int_views), SUM(ext_views)
FROM news
WHERE date = '2012-08-29'
GROUP BY url
ORDER BY SUM(pageviews) DESC
LIMIT 10;
然后,您可以在此合并表上运行上述查询(以及任何其他查询):
{{1}}
答案 1 :(得分:5)
几点:
datetime
作为第一列的索引。pageviews
订购。我原以为你想按sum(pageviews)
订购。<
,以避免遗漏任何内容。 SELECT url, sum(pageviews), sum(int_views), sum(ext_views)
FROM news
WHERE datetime >= '2012-08-29 00:00:00'
AND datetime < '2012-08-30 00:00:00'
GROUP BY url
ORDER BY sum(pageviews) DESC
LIMIT 10;
您可以在datetime, url, pageviews, int_views, ext_views
上对此进行索引,但我认为这样会有点过分;所以,如果索引不是太大datetime, url
似乎是一个很好的方法。唯一可以确定的方法是测试它并确定查询中的任何性能改进是否值得在索引维护中花费额外的时间。
正如Gordon刚刚在评论中提到的那样,你可能需要研究partitioning。这使您可以查询较小的“表”,该表是较大的表。如果您的所有查询都基于日期级别,那么您可能需要每天创建一个新查询。