Partition Cache
多个 SQL 使用相同的表分区时可命中缓存。
Partition Cache 是试验性功能,没有得到很好的维护,将在2.1版本中删除,请谨慎使用。
需求场景 & 解决方案
见 缓存概览 文档。
设计原理
SQL 可以并行拆分,Q = Q1 ∪ Q2 ... ∪ Qn,R= R1 ∪ R2 ... ∪ Rn,Q 为查询语句,R 为结果集
SQL 只使用 DATE、INT、BIGINT 类型的分区字段聚合,且只扫描一个分区,因此不支持按天分区,只支持按年、月分区。
将查询结果集中部分日期的结果缓存,然后缩减 SQL 中扫描的日期范围,本质 PartitionCache 并没有减少扫描的分区数量,而且缩减扫描的日期范围,从而减少扫描数据量。
此外一些限制:
只支持按分区字段分组,不支持按其他字段分组,按其他字段分组,该分组数据都有可能被更新,会导致缓存都失效
只支持结果集的前半部分、后半部分以及全部命中缓存,不支持结果集被缓存数据分割成几个部分,且结果集的日期必须连续,如果某一天在结果集中没有数据,那只有这一天之前的日期会被缓存。
如果 predicate 有分区之外的列,那么必须给分区 predicate 加上括号
where k1 = 1 and (key >= "2023-10-18" and key <= "2021-12-01")
查询的天数必须大于 1,小于 cache_result_max_row_count,否则无法使用 partition cache。
分区字段的 predicate 只能是 key >= a and key <= b 或者 key = a or key = b 或者 key in(a,b,c)。
使用方式
确保 fe.conf 的 cache_enable_partition_mode=true (默认是 true)
vim fe/conf/fe.conf
cache_enable_partition_mode=true
在 MySQL 命令行中设置变量
MySQL [(none)]> set [global] enable_partition_cache=true;
如果同时开启了两个缓存策略,下面的参数,需要注意一下:
cache_last_version_interval_second=30
如果分区的最新版本的时间离现在的间隔,大于 cache_last_version_interval_second,则会优先把整个查询结果缓存。如果小于这个间隔,如果符合 PartitionCache 的条件,则按 PartitionCache 数据。
具体参数介绍和未尽事项见 query-cache.md。
未尽事项
拆分为只读分区和可更新分区,只读分区缓存,更新分区不缓存
如查询最近 7 天的每天用户数,如按日期分区,数据只写当天分区,当天之外的其他分区的数据,都是固定不变的,在相同的查询 SQL 下,查询某个不更新分区的指标都是固定的。如下,在 2020-03-09 当天查询前 7 天的用户数,2020-03-03 至 2020-03-07 的数据来自缓存,2020-03-08 第一次查询来自分区,后续的查询来自缓存,2020-03-09 因为当天在不停写入,所以来自分区。
因此,查询 N 天的数据,数据更新最近的 D 天,每天只是日期范围不一样相似的查询,只需要查询 D 个分区即可,其他部分都来自缓存,可以有效降低集群负载,减少查询时间。
实现原理示例:
MySQL [(none)]> SELECT eventdate,count(userid) FROM testdb.appevent WHERE eventdate>="2020-03-03" AND eventdate<="2020-03-09" GROUP BY eventdate ORDER BY eventdate;
+------------+-----------------+
| eventdate | count(`userid`) |
+------------+-----------------+
| 2020-03-03 | 15 |
| 2020-03-04 | 20 |
| 2020-03-05 | 25 |
| 2020-03-06 | 30 |
| 2020-03-07 | 35 |
| 2020-03-08 | 40 | //第一次来自分区,后续来自缓存
| 2020-03-09 | 25 | //来自分区
+------------+-----------------+
7 rows in set (0.02 sec)
在 PartitionCache 中,缓存第一级 Key 是去掉了分区条件后的 SQL 的 128 位 MD5 签名,下面是改写后的待签名的 SQL:
SELECT eventdate,count(userid) FROM testdb.appevent GROUP BY eventdate ORDER BY eventdate;
缓存的第二级 Key 是查询结果集的分区字段的内容,比如上面查询结果的 eventdate 列的内容,二级 Key 的附属信息是分区的版本号和版本更新时间。
下面演示上面 SQL 在 2020-03-09 当天第一次执行的流程:
- 从缓存中获取数据
+------------+-----------------+
| 2020-03-03 | 15 |
| 2020-03-04 | 20 |
| 2020-03-05 | 25 |
| 2020-03-06 | 30 |
| 2020-03-07 | 35 |
+------------+-----------------+
- 从 BE 中获取数据的 SQL 和数据
SELECT eventdate,count(userid) FROM testdb.appevent WHERE eventdate>="2020-03-08" AND eventdate<="2020-03-09" GROUP BY eventdate ORDER BY eventdate;
+------------+-----------------+
| 2020-03-08 | 40 |
+------------+-----------------+
| 2020-03-09 | 25 |
+------------+-----------------+
- 最后发送给终端的数据
+------------+-----------------+
| eventdate | count(`userid`) |
+------------+-----------------+
| 2020-03-03 | 15 |
| 2020-03-04 | 20 |
| 2020-03-05 | 25 |
| 2020-03-06 | 30 |
| 2020-03-07 | 35 |
| 2020-03-08 | 40 |
| 2020-03-09 | 25 |
+------------+-----------------+
- 发送给缓存的数据
+------------+-----------------+
| 2020-03-08 | 40 |
+------------+-----------------+
Partition 缓存,适合按日期分区,部分分区实时更新,查询 SQL 较为固定。
分区字段也可以是其他字段,但是需要保证只有少量分区更新。