ClickHouse
2025年6月27日大约 2 分钟
查询配置
SELECT name, value FROM system.settings WHERE name LIKE '%memory%' OR name LIKE '%group_by%'配置user.xml
<profiles>
<!-- Default settings. -->
<default>
<!-- 单查询限制为 6GB -->
<max_memory_usage>6442450944</max_memory_usage>
<!-- 用户总内存也限制为 6GB -->
<max_memory_usage_for_user>6442450944</max_memory_usage_for_user>
<!-- 提前落盘,避免内存爆掉 -->
<max_bytes_before_external_group_by>1073741824</max_bytes_before_external_group_by> <!-- 1GB -->
<max_bytes_before_external_sort>1073741824</max_bytes_before_external_sort> <!-- 1GB -->
<!-- 禁止乐观超配,严格控制内存 -->
<memory_overcommit_ratio_denominator>1</memory_overcommit_ratio_denominator>
<memory_overcommit_ratio_denominator_for_user>1</memory_overcommit_ratio_denominator_for_user>
</default>
<!-- Profile that allows only read queries. -->
<readonly>
<readonly>1</readonly>
</readonly>
</profiles>注意时区
Hello World
你这个问题非常关键,关系到 ClickHouse 的分区设计理念:
为什么用表达式 PARTITION BY concat(...),而不是单独新增一个“分区字段”?
1. ClickHouse 分区不是逻辑列,而是物理划分
- ClickHouse 的分区是基于表达式计算的结果来物理划分存储数据的目录,它不是表中的普通列,而是建表时通过表达式“派生”出来的。
- 这意味着不需要额外保存一个“分区字段”列,因为分区信息是根据已有的字段(比如
outtime)计算得出。
2. 直接用时间字段计算分区,避免数据冗余
如果你额外添加一个专门的“分区字段”,
- 会导致数据重复存储这部分信息,浪费存储空间
- 增加写入时数据的复杂度和错误风险(分区字段要和时间字段同步)
直接用
outtime字段计算分区表达式,能保证分区自动、准确。
3. 灵活且便于变更分区策略
- 通过表达式控制分区,修改分区粒度只需修改建表语句中的表达式即可,无需修改表结构。
- 例如想改成按月分区,只改
PARTITION BY toYYYYMM(outtime),不需要加列。
4. 查询性能优化
- 分区表达式是 ClickHouse 用来做数据裁剪(pruning)的依据,直接用时间字段计算,查询时按时间范围过滤,可以高效跳过无关分区。
- 额外字段不一定能带来更好的分区裁剪效果。
总结
| 方式 | 说明 | 推荐度 |
|---|---|---|
用表达式 PARTITION BY ... | 分区由字段直接计算,物理划分,零冗余 | ✅ 最常见推荐方式 |
| 新增分区字段列 | 需要维护字段一致性,数据冗余,复杂 | ❌ 不建议 |
删除重复数据
CREATE TABLE t_access_person_record_dedup AS
SELECT *
FROM (
SELECT *,
row_number() OVER (PARTITION BY access_person_record_id ORDER BY access_time DESC) AS rn
FROM t_access_person_record
)
WHERE rn = 1;
DROP TABLE t_access_person_record;
RENAME TABLE t_access_person_record_dedup TO t_access_person_record;