自定义分区键
在大多数情况下,你不需要分区键,在大多数其他情况下,你也不需要比按月更细粒度的分区键,除非针对可观测性用例,按天分区很常见。
你不应该使用过于细粒度的分区。不要使用客户端标识符或名称进行分区。相反,将客户端标识符或名称作为 ORDER BY 表达式中的第一列。
分区适用于 MergeTree 系列表,包括 复制表 和 物化视图。
分区是表中记录的逻辑组合,由指定的标准确定。你可以通过任意标准设置分区,例如按月、按天或按事件类型。每个分区都单独存储,以简化此数据的操作。访问数据时,ClickHouse 使用尽可能最小的子集分区。分区提高了包含分区键的查询的性能,因为 ClickHouse 将针对该分区进行过滤,然后再选择分区内的部分和颗粒。
分区在 创建表时,在 PARTITION BY expr 子句中指定。分区键可以是表列中的任何表达式。例如,要指定按月分区,请使用表达式 toYYYYMM(date_column)
分区键也可以是表达式的元组(类似于 主键)。例如
在此示例中,我们设置了按当前周发生的事件类型进行分区。
默认情况下,不支持浮点分区键。要使用它,请启用设置 allow_floating_point_partition_key。
将新数据插入到表时,此数据将作为按主键排序的单独部分(块)存储。在插入后 10-15 分钟内,相同分区的各个部分将合并成一个完整的部分。
合并仅适用于具有相同分区表达式值的各个数据部分。这意味着 你不应该创建过于细粒度的分区(超过大约一千个分区)。否则,SELECT 查询的性能会因文件系统中过多的文件和打开的文件描述符而下降。
使用 system.parts 表来查看表的各个部分和分区。例如,假设我们有一个按月分区的 visits 表。让我们对 system.parts 表执行 SELECT 查询
partition 列包含分区的名称。在此示例中,有两个分区:201901 和 201902。你可以使用此列值在 ALTER ... PARTITION 查询中指定分区名称。
name 列包含分区数据部分的名称。你可以使用此列在 ALTER ATTACH PART 查询中指定部分名称。
让我们分解部分名称:201901_1_9_2_11
201901是分区名称。1是数据块的最小编号。9是数据块的最大编号。2是块级别(它所形成的合并树的深度)。11是变异版本(如果该部分发生了变异)。
旧类型表的各个部分名称为:20190117_20190123_2_2_0(最小日期 - 最大日期 - 最小块号 - 最大块号 - 级别)。
active 列显示部分的状态。1 表示活动状态;0 表示非活动状态。非活动部分例如是合并到较大部分后剩余的源部分。损坏的数据部分也显示为非活动状态。
如示例所示,同一分区有几个分离的部分(例如,201901_1_3_1 和 201901_1_9_2)。这意味着这些部分尚未合并。ClickHouse 会定期合并插入的各个部分的数据,大约在插入后 15 分钟。此外,你可以使用 OPTIMIZE 查询执行非计划合并。示例
非活动部分将在合并后大约 10 分钟后删除。
另一种查看一组部分和分区的方法是进入表的目录:/var/lib/clickhouse/data/<database>/<table>/。例如
文件夹 '201901_1_1_0'、'201901_1_7_1' 等是各个部分的目录。每个部分与相应的分区相关联,并且仅包含特定月份的数据(此示例中的表按月分区)。
detached 目录包含使用 DETACH 查询从表分离的部分。损坏的部分也移动到此目录,而不是被删除。服务器不使用 detached 目录中的部分。你可以随时添加、删除或修改此目录中的数据 - 服务器在运行 ATTACH 查询之前不会知道这一点。
请注意,在运行服务器上,你无法手动更改文件系统上的各个部分或其数据,因为服务器不会知道这一点。对于非复制表,可以在服务器停止时执行此操作,但不建议这样做。对于复制表,无论如何都无法更改各个部分。
ClickHouse 允许你对分区执行操作:删除它们、从一个表复制到另一个表或创建备份。请参阅 分区和部分操作 部分中的所有操作列表。
使用分区键优化 Group By
对于表的分区键和查询的 Group By 键的某些组合,可能可以独立地为每个分区执行聚合。然后,我们不必在最后合并来自所有执行线程的部分聚合数据,因为我们保证了每个 Group By 键值不能出现在两个不同线程的工作集中。
典型的例子是
此类查询的性能在很大程度上取决于表的布局。因此,优化默认情况下未启用。
良好的性能的关键因素
- 参与查询的分区数应该足够大(大于
max_threads / 2),否则查询将无法充分利用机器 - 分区不应太小,因此批量处理不会退化为逐行处理
- 分区的大小应该相当,以便所有线程执行大致相同的工作量
建议对 partition by 子句中的列应用一些哈希函数,以便在分区之间均匀地分配数据。
相关设置是
allow_aggregate_partitions_independently- 控制是否启用优化force_aggregate_partitions_independently- 在正确性角度适用时强制使用它,但被内部逻辑禁用,该逻辑估计其效率max_number_of_partitions_for_independent_aggregation- 表可以拥有的最大分区数的硬限制