ClickHouse组件使用规范
本章节介绍ClickHouse组件使用规范。
建表规范
- 【规则】不要在system库中创建业务表。system数据库是ClickHouse默认的系统数据库,默认数据库中的系统表记录的是系统的配置、元数据等信息数据。业务在使用ClickHouse的时候,需要指定自己业务的数据库进行连接和使用,业务相关的表创建在自己业务库中,不要将业务表创建在系统数据库中,避免对系统数据库造成不必要的影响。
- 【规则】数据库和表的命名尽量不要使用SQL保留字,请注意大小写敏感。如果必须使用一些保留关键字,请使用双引号或者反引号进行转义。
- 【规则】不允许使用字符类型存放时间或日期类数据,尤其是日期字段进行运算或比较的时候。
- 【规则】不允许使用字符类型存放数值类型的数据,尤其是数值字段进行运算或者比较的时候。
- 【建议】不建议表使用Nullable列,可以考虑使用字符串“NA”。
Nullable类型的列在做查询条件判断时,会进一步做判空等处理,防止造成额外的计算开销。根据现网的历史经验,Nullable类型的字符串查询性能比String慢20%至30%左右,从性能方面考虑,非必要不使用Nullable类型。
数据写入
- 【规则】外部模块保证数据导入的幂等性。
ClickHouse不支持数据写入的事务保证。通过外部导入数据模块控制数据的幂等性,比如某个批次的数据导入异常,则drop对应分区数据或清理掉导入的数据后,重新导入该分区或批次数据。
- 【规则】大批量少频次的写入数据。
ClickHouse每次插入数据时,都会生成一到多个part文件,如果data part过多,merge压力会变大,甚至出现各种异常情况影响数据插入。建议每个批次5k到100k行,根据写入字段数量调整写入行数,降低写入节点内存和CPU的压力,每秒不超过1次插入。
- 【建议】一次只插入一个分区内的数据。
如果数据属于不同的分区,则每次插入不同分区的数据会独立生成part文件,导致part总数量膨胀,建议一批插入的数据属于同一个分区。
- 【建议】慎用分布式表批量插入。
- 写分布式表时,数据会分发到集群的所有本地表,每个本地表插入的数据量是总插入量的1/N,batch size可能比较小,会导致data part过多,merge压力变大,甚至出现异常影响数据插入。
- 数据的一致性问题:数据先在分布式表写入节点的主机落盘,然后数据被异步地发送到本地表所在主机进行存储,中间没有一致性的校验,如果分布式表写入数据的主机出现异常,会存在数据丢失风险。
- 对于数据写分布式表和数据写本地表相比,分布式表数据写入性能会变慢,单批次分布式表写入节点的磁盘和网络IO会成为性能的瓶颈点。
- 分布式表转发给各个shard成功与否,插入数据的客户端是无法感知,转发失败的数据会不断重试转发消耗CPU。
- 只有在数据去重的场景下,可以使用分布式表插入,通过sharding key将要去重的数据转发到同一个shard,方便后续去重查询。
- 【建议】慎用delete、update操作。
标准SQL的更新、删除操作是同步的,即客户端等服务端返回执行结果在执行。而Clickhouse的update、delete是通过异步方式实现的,当执行update语句时,服务端立即响应,实际上此时数据还没变,而是排队等待,可能会出现操作覆盖的情况,也无法保证操作的原子性。如果业务场景有update、delete等操作,建议使用ReplacingMergeTree、CollapsingMergeTree、VersionedCollapsingMergeTree引擎。
- 【建议】谨慎执行optimize操作。
Optimize一般会对表做重写操作,建议在业务压力小时候进行操作,否则对IO/MEM/CPU资源有较大消耗,导致业务查询变慢或不可用。
- 【建议】降低对表的修改频次,多个字段修改时,建议使用单条ALTER TABLE命令修改。
默认场景下ClickHouse执行alter语句是异步执行,对同一张表频繁执行alter操作可能导致业务失败。ClickHouse每次修改列时都会进行元数据的操作,多次操作会增加集群的负担,尤其是zookeeper的负担,影响集群的稳定。可以使用一条语句进行多列的修改。
数据查询
- 【规则】不要使用select *,只查询需要的字段,减少机器负载,提升查询性能。
OLAP分析场景,一张大宽表通常能有几百甚至上千列,选择其中少数的几列做维度列、指标列计算。在这种场景下,ClickHouse的数据也是按照列存储。如果使用select *,会加重系统的压力。
- 【规则】通过limit限制查询返回的数据量,节省计算资源、减少网络开销。
如果返回的数据量过大,客户端有可能出现内存溢出等服务异常。在前端使用ClickHouse的场景下,如果要查询的数据量比较大,建议每次可适当的进行分页查询返回数据,以减少查询数据量对网络带宽和计算资源的占用。
- 【规则】关联查询必须大表join小表。
对于ClickHouse来说,原则上需要把多表join模型提前加工为宽表模型,多个表以及维度表变化比较频繁情况下,不适合进行宽表加工处理,必须使用Join模型以实时查询到最新数据。两个表做join操作,建议大表join小表,必须使用关联条件。小表的数据量控制在百万~千万行级别,且需要在join前把小表数据通过条件进行有效过滤。
- 【建议】使用GLOBAL JOIN/IN替换普通的JOIN。
ClickHouse基于分布式表查询会转换成所有分片的本地表操作,再汇总结果。实际使用中,join和global join的执行逻辑差别很大,建议使用global join做分布式表查询。
- 【规则】合理使用数据表的分区字段和索引字段。
- MergeTree引擎,数据是以分区目录形式进行组织存储的,在进行数据查询时,使用分区可以有效跳过无用的数据文件,减少数据的读取。
- MergeTree引擎会根据索引字段进行数据排序,并且根据index_granularity的配置生成稀疏索引。根据索引字段查询,能快速过滤数据,减少数据的读取,大大提升查询性能。
- 【建议】明确数据查询的范围。
增加条件过滤和查询数据周期过滤,缩小数据查询范围。例如查询指定分区,通过指定分区字段会减少底层数据库扫描的文件数量,提升查询性能。例如:700个分区的千列大表,需要查询一个分区中有7000万数据,其他699个分区中无数据,虽然只有一个分区有数据,其他分区无数据,但是查询指定分区为百毫秒级性能,没有指定分区查询性能为1~2秒左右,性能相差20倍。
- 【建议】慎用final查询。
在查询语句的最后跟上final,通常是对于ReplacingMergeTree引擎,数据不能完全去重情况下,少数开发人员习惯使用final关键字进行实时合并去重操作(merge-on-read),保证查询数据无重复数据。可以通过argMax函数或其他方式规避此问题。
- 【建议】使用物化视图加速查询。
- 【建议】物化视图创建时不会进行语法校验,只有发生实际数据插入与查询时才会出错。物化视图上线前,需做好充分验证。