统计数据的最佳分区 table

Best partition for stats table

我们有一个很大的统计数据 table,它记录每个产品每天的点击量。

   date      | datetime
   productid | int
   hits      | int
   channel   | enum

大约 200M 行+

我们有两种查询此日期的方法 - 它总是在 WHERE 中有 productid(或多个 productID),然后也可以选择指定日期。大多数查询都会有两者。

我们允许用户查询通常日期'buckets' 例如Today/Yesterday/Last 7 days/Last month/Last 3 months/This year/Last 年。他们还可以选择任意日期。

所以我在日期分区和产品 ID 之间犹豫不决。我的想法是,如果我们一次查询多个 productID(使用 IN()),那么这可能意味着跨越多个分区。但是,由于大多数查询都在一年内,按日期分区最好吗?

由于大多数查询都是短期的(例如 days/weeks),也许我们有类似的东西:

过去 3 个月过去 3-6 个月过去 6-12 个月然后是每年一次。

我们的数据可以追溯到 2005 年。

感谢您的建议。

首先确保数据类型尽可能小。会有十亿个productids吗? INT 是 4 个字节。 MEDIUMINT UNSIGNED 是 3 个字节,允许 0 到 1600 万之间的值。等等

你有这些的组合吗?

AND productid = 123  (or, equivalently, productid IN (123))  -- (1)

AND productid IN (234,345,456)  -- (2)

AND date >= '2020...'
AND date <  '2020...' + INTERVAL 7 DAY    -- (3)

您的“上个月”shorthand 可以很容易地转换为包含开始日期和 + INTERVAL 1 MONTH.

的日期范围

For AND clause (1) or (1 and 3) or (2): PRIMARY KEY(productid, date) 即使没有分区也是最优的。如果那对是唯一的,那么就这样做。 (听起来好像是。)

(单独 3)和(2 和 3)比较棘手。

(3) 需要 INDEX(date)

您需要删除“旧”统计数据吗?如果是这样,那么强烈建议 PARTITION BY RANGE(date)。它可以让你 DROP PARTITION(非常快)而不是 DELETE。参见 http://mysql.rjweb.org/doc.php/partitionmaint

通过该分区,您可以获得(2 和 3)的一些二维帮助。或者优化器可能足够聪明,可以使用我推荐的 PK 绕过 table。

我会在 2020 年之前建立年度存储桶,然后从 2020 年 1 月开始建立每月存储桶。而且我不会在未来将每月合并为每年,因为它会阻止 table“太多”。分区:

1  pre-2005 (empty) (see link for reason)
15 2005..2019  (Thanks for providing the '2005')
11(so far)  2020, jan..nov
1  "future" (see link)
(28 currently, but growing)

并每晚尝试在需要之前创建一个新的'month'分区。 (不要预先构建大量分区。)

(注意:年+月分区是一次性的,以后所有的分区都是“月”,我避免把月合并成年。而且你在生命周期内不会遇到任何分区限制计算机或项目。)

他们从不测试 channel 吗?他们从不SUM(hits) .. GROUP BY吗?这些会引发更多讨论。

“参考地点”...

数据可能占用10GB?你有多少内存? innodb_buffer_pool_size的设置是什么?用户 通常 阅读“最近的”数据吗?答案相互作用以预测需要多少 I/O,因此查询的速度有多快 运行。

我上面推荐的索引和分区是针对

  • 非常低 I/O 如果 所有 查询都是针对“最近”数据的。
  • 相当低I/O,即使有偶尔“旧”查询。 (听起来这就是你的情况。)
  • 即使日期分散在所有时间,也会“工作”(以某种速度)。

您可能已经注意到我主要集中在 productid 上。效果如下:

  • 二维索引很棘手;一个维度需要在PK中先行;另一个可以通过分区。
  • 如果有,比方说,10K 个产品,那么“插入”将有 10K 个“热点”。也就是说,buffer_pool 基本上一直都有 10K 个 16KB 块,准备接收下一次阅读。这仅仅是 160MB,这无疑是 buffer_pool_size 的一小部分。因此没有 I/O 用于插入。

11个月的数据大概不到1GB?同样,对于所有“最近”的查询,这可能会在 buffer_pool 中保留。 (参考位置;“分区 p运行ing”)。