
# ClickHouse数据写入规范
该章节主要介绍ClickHouse数据写入时需遵循的规范和建议。
#### 数据写入
- 【规则】外部模块保证数据导入的幂等性。 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修改表数据。
  - 建议慎用delete、update的mutation操作。 标准SQL的更新、删除操作是同步的，即客户端要等服务端返回执行结果（通常是int值）；而ClickHouse的update、delete是通过异步方式实现的，当执行update语句时，服务端立即返回执行成功还是失败结果，但是实际上此时数据还没有修改完成，而是在后台排队等着进行真正的修改，可能会出现操作覆盖的情况，也无法保证操作的原子性。
    业务场景要求有update、delete等操作，建议使用ReplacingMergeTree、CollapsingMergeTree、VersionedCollapsingMergeTree引擎，使用方式参见[表引擎](https://clickhouse.com/docs/zh/engines/table-engines/mergetree-family/collapsingmergetree)。
    
  
  - 建议少或不增删数据列。 业务提前规划列个数，如果将来有更多列要使用，可以规划预留多列，避免在生产系统跑业务过程中进行大量的alter table modify列操作，导致不可以预知的性能、数据一致性问题。
    
   
 
