统计数据的最佳分区 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”)。
我们有一个很大的统计数据 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”)。