分区
PARTITION BY 子句指定的。该子句可以包含基于任意列的 SQL 表达式,其结果决定每一行会被写入哪个分区。
磁盘上的每个分区都会在逻辑上关联相应的数据分区片段 (通过相同的文件夹名前缀) ,并且可以单独查询。以下面的示例为例,默认的 otel_logs schema 使用表达式 toDate(Timestamp) 按天分区。随着行被插入 ClickHouse,系统会对每一行计算该表达式,并将其路由到对应的分区;如果某一天的第一行到来,则会创建该分区。
otel_logs 表按天分区。如果写入结构化日志数据集,其中将包含数天的数据:
otel_logs_archive,用于存储较早的数据。可以按分区高效地将数据迁移到这张表中 (这只是元数据层面的变更) 。
INSERT INTO SELECT,并将数据重写到新的目标表中。
此外,还可以按分区高效删除数据。与其他替代方案 (变更或轻量级删除) 相比,这种方式的资源效率要高得多,因此应优先采用。
使用设置
ttl_only_drop_parts=1 时,生存时间 (TTL) 会利用这一特性。更多详情,请参见使用 生存时间 (TTL) 进行数据管理。应用场景
- 分层架构 - 在不同存储层之间移动数据 (参见 存储层级) ,从而构建冷热架构。
- 高效删除 - 当数据达到指定的生存时间 (TTL) 时 (参见 Data management with 生存时间 (TTL))
查询性能
使用 生存时间 (TTL) (生存时间) 进行数据管理
表级 生存时间 (TTL)
ttl 键进行设置,例如:
h,并确保其与分区周期保持一致。例如,如果按天分区,请确保其为天数的整数倍,例如 24h、48h、72h。 这样会自动为表添加 生存时间 (TTL) 子句,例如当 ttl: 96h 时。
计划性 生存时间 (TTL)生存时间 (TTL) 不会立即生效,而是如上所述按计划执行。MergeTree 表设置
merge_with_ttl_timeout 用于设置再次执行带删除生存时间 (TTL) 的合并前的最小延迟时间 (秒) 。默认值为 14400 秒 (4 小时) 。但这只是最小延迟,实际触发生存时间 (TTL) 合并可能还需要更长时间。如果该值过低,会执行大量计划外合并,并可能消耗大量资源。可以使用命令 ALTER TABLE my_table MATERIALIZE TTL 强制触发生存时间 (TTL) 过期处理。ttl_only_drop_parts=1 ** (默认 schema 会应用该设置) 。启用此设置后,当某个 part 中的所有行都已过期时,ClickHouse 会直接删除整个 part。相比清理 part 中部分已过期的 生存时间 (TTL) 行 (在 ttl_only_drop_parts=0 时,这需要通过资源密集型变更来完成) ,直接删除整个 part 可以使用更短的 merge_with_ttl_timeout,并降低对系统性能的影响。如果数据按执行 生存时间 (TTL) 过期所依据的相同单位进行分区,例如按天分区,那么 part 自然只会包含该时间间隔内的数据。这将确保 ttl_only_drop_parts=1 能够被高效应用。
列级生存时间 (TTL)
Body 列,以防新增了尚未在写入时提取的动态元数据,比如新的 Kubernetes 标签。经过一段时间后,例如 1 个月,可能就会明显看出这些额外元数据并不实用——因此继续保留 Body 列的价值也就有限了。
下面,我们将说明如何在 30 天后删除 Body 列。
指定列级生存时间 (TTL) 需要用户自行定义 schema。OTel collector 中无法进行此项配置。
重新压缩数据
ZSTD(1),但你也可以尝试不同的压缩算法或更高的压缩级别,例如 ZSTD(3)。除了可以在创建 schema 时指定外,还可以将压缩配置为在设定的一段时间后切换。如果某种 codec 或压缩算法能提升压缩率,但会导致查询性能下降,那么这种做法可能是合适的。对于查询频率较低的旧数据,这种权衡或许可以接受;但对于较新的数据则未必如此,因为这些数据在调查中会更频繁地使用。
下面展示了一个示例:4 天后使用 ZSTD(3) 压缩数据,而不是将其删除。
评估性能我们建议用户始终评估不同压缩级别和算法对 insert 和查询性能的影响。例如,delta 编解码器有助于压缩时间戳。不过,如果这些时间戳属于主键的一部分,过滤性能可能会受到影响。
存储层级
不适用于 ClickHouse CloudClickHouse Cloud 使用单份数据副本,以 S3 为后端,并配有基于 SSD 的节点缓存。因此,ClickHouse Cloud 不需要存储层级。
ALTER TABLE MOVE PARTITION 命令在磁盘之间手动移动数据,但也可以使用 TTL 来控制数据在卷之间的移动。完整示例请参见这里。
管理 schema 变更
使用默认值
DEFAULT 值向 schema 中添加列。如果在 INSERT 时未指定,则会使用指定的默认值。
在修改任何 materialized view 转换逻辑或 OTel collector 配置之前,可以先更改 schema,这样后续就会发送这些新列。
schema 更改完成后,你可以重新配置 OTel collectors。假设用户采用了“使用 SQL 提取结构”中介绍的推荐流程,即 OTel collectors 将数据发送到使用 Null table engine 的表,由 materialized view 负责提取目标 schema,并将结果发送到目标表中存储,那么可以使用 ALTER TABLE ... MODIFY QUERY 语法来修改该视图。假设我们有下面这个目标表及其对应的 materialized view (类似于“使用 SQL 提取结构”中使用的方式) ,用于从 OTel 结构化日志中提取目标 schema:
LogAttributes 中提取一个新的列 Size。我们可以使用 ALTER TABLE 将其添加到 schema 中,并指定默认值:
LogAttributes 中的 size 键 (如果该键不存在,则为 0) 。这意味着,对于未插入该值的行,查询这一列时必须访问该 Map,因此速度会更慢。我们也可以很容易地将其指定为一个常量,例如 0,从而降低后续查询这些没有该值的行时的开销。查询此表可以看到,该值已按预期从 Map 中填充:
ALTER TABLE 语法来修改 materialized view:
Size 列会在写入时自动填充。
创建新表
ALTER TABLE MODIFY QUERY. 修改任何 materialized view,使其改用这张新表。采用这种方式,你可以为表添加版本号,例如 otel_logs_v3。
这种方式会导致用户需要面对多个可查询的表。若要跨表查询,可以使用merge 函数,它支持对表名使用通配符 pattern。下面我们通过查询 otel_logs 表的 v2 和 v3 版本来演示这一点:
merge 函数,并向最终用户提供一张整合多个表的表,则可以使用 Merge 表引擎。下面我们来演示这一点:
EXCHANGE 表语法来更新。例如,要添加 v4 表,可以先创建一个新表,再通过原子方式将其与上一版本进行交换。