数据类型

时间字段的类型

建表时能用数值型或日期时间型表示的字段就不要用字符串

虽然ClickHouse底层将DateTime存储为时间戳Long类型,但不建议存储Long类型,因为DateTime不需要经过函数转换处理,执行效率高、可读性好 下面这个例子用了int32存日期, 这样存的话, 要用toDate转换下才能用, 效率低, 应该直接用DateTime的
  1. create table t_type2(
  2. id UInt32,
  3. sku_id String,
  4. total_amount Decimal(16,2) ,
  5. create_time Int32
  6. ) engine =ReplacingMergeTree(create_time)
  7. partition by toYYYYMMDD(toDate(create_time)) -- 需要转换一次,否则报错
  8. primary key (id)
  9. order by (id, sku_id);

空值存储类型

官方已经指出Nullable类型几乎总是会拖累性能,因为存储Nullable列时需要创建一个额外的文件来存储NULL的标记,并且Nullable列无法被索引。因此除非极特殊情况,应直接使用字段默认值表示空,或者自行指定一个在业务中无意义的值(例如用-1表示没有商品ID)

  1. CREATE TABLE t_null(x Int8, y Nullable(Int8)) ENGINE TinyLog;
  2. INSERT INTO t_null VALUES (1, NULL), (2, 3);
  3. SELECT x + y FROM t_null;

1 + NULL = NULL:

  1. SELECT x + y
  2. FROM t_null
  3. Query id: 9fc5ca79-fdfb-47eb-b53c-361eda2f6c60
  4. ┌─plus(x, y)─┐
  5. ᴺᵁᴸᴸ
  6. 5
  7. └────────────┘
  8. 2 rows in set. Elapsed: 0.002 sec.
单独的文件来存储NULL值:
  1. [root@aliyun ~]# cd /var/lib/clickhouse/data/default/t_null/
  2. [root@aliyun t_null]# ll
  3. total 16
  4. -rw-r----- 1 clickhouse clickhouse 91 Sep 21 08:28 sizes.json
  5. -rw-r----- 1 clickhouse clickhouse 28 Sep 21 08:28 x.bin
  6. -rw-r----- 1 clickhouse clickhouse 28 Sep 21 08:28 y.bin
  7. -rw-r----- 1 clickhouse clickhouse 28 Sep 21 08:28 y.null.bin

分区和索引

分区粒度根据业务特点决定,不宜过粗或过细。一般选择按天分区,也可以指定为Tuple(),以单表一亿数据为例,分区大小控制在10-30个为最佳

必须指定索引列,ClickHouse中的索引列即排序列,通过order by指定,一般在查询条件中经常被用来充当筛选条件的属性被纳入进来;可以是单一维度,也可以是组合维度的索引;

通常需要满足高级列在前、查询频率大的在前原则;

还有基数特别大的不适合做索引列,如用户表的userid字段;

通常筛选后的数据满足在百万以内为最佳

表参数

Index_granularity是用来控制索引粒度的,默认是8192,如非必须不建议调整 如果表中不是必须保留全量历史数据,建议指定TTL(生存时间值),可以免去手动过期历史数据的麻烦,TTL也可以通过alter table语句随时修改

写入和删除优化

  1. 尽量不要执行单条或小批量删除和插入操作,这样会产生小分区文件,给后台Merge任务带来巨大压力
  1. 不要一次写入太多分区,或数据写入太快,数据写入太快会导致Merge速度跟不上而报错,一般建议每秒钟发起2-3次写入操作,每次操作写入2w~5w条数据(依服务器性能而定)

写入过快报错,报错信息

  1. 1. Code: 252, e.displayText() = DB::Exception: Too many parts(304).
  2. Merges are processing significantly slower than inserts
  3. 2. Code: 241, e.displayText() = DB::Exception: Memory limit (for query)
  4. exceeded:would use 9.37 GiB (attempt to allocate chunk of 301989888
  5. bytes), maximum: 9.31 GiB

处理方式:

Too many parts处理:使用WAL预写日志,提高写入性能

in_memory_parts_enable_wal默认为true

在服务器内存充裕的情况下增加内存配额,一般通过max_memory_usage来实现在服务器内存不充裕的情况下,建议将超出部分内容分配到系统硬盘上,但会降低执行速度,一般通过max_bytes_before_external_group_bymax_bytes_before_external_sort参数来实现

常见配置

配置项主要在config.xml或users.xml中,基本上都在users.xml里

config.xml的配置项:

https://clickhouse.com/docs/en/operations/server-configuration-parameters/settings/

users.xml的配置项:

https://clickhouse.com/docs/en/operations/settings/settings/

CPU资源

建表优化 - 图1

内存资源

建表优化 - 图2

存储

ClickHouse不支持设置多数据目录,为了提升数据IO性能,可以挂载虚拟卷组,一个卷组绑定多块物理磁盘提升读写性能,多数据查询场景SSD会比普通机械硬盘快2-3倍