
本文共 5351 字,大约阅读时间需要 17 分钟。
文章目录
一、建表规范
- 数据表必须设置主键
- 数据表必须有以下字段
created_date
updated_date
created_by
updated_by
1.1 数据表引擎选择
Log系列
Log系列表引擎功能相对简单,主要用于快速写入小表(1百万行左右的表),然后全部读出的场景。
几种Log表引擎的共性是:
- 数据被顺序append写到磁盘上。
- 不支持delete、update。
- 不支持index。
- 不支持原子性写。
- insert会阻塞select操作。
它们彼此之间的区别是:
- TinyLog:不支持并发读取数据文件,查询性能较差;格式简单,适合用来暂存中间数据。
- StripLog:支持并发读取数据文件,查询性能比TinyLog好;将所有列存储在同一个大文件中,减少了文件个数。
- Log:支持并发读取数据文件,查询性能比TinyLog好;每个列会单独存储在一个独立文件中。
Integration系列
该系统表引擎主要用于将外部数据导入到ClickHouse中,或者在ClickHouse中直接操作外部数据源。
- Kafka:将Kafka Topic中的数据直接导入到ClickHouse。
- MySQL:将Mysql作为存储引擎,直接在ClickHouse中对MySQL表进行select等操作。
- JDBC/ODBC:通过指定jdbc、odbc连接串读取数据源。
- HDFS:直接读取HDFS上的特定格式的数据文件;
Special系列
Special系列的表引擎,大多是为了特定场景而定制的。这里也挑选几个简单介绍,不做详述。
- Memory:将数据存储在内存中,重启后会导致数据丢失。查询性能极好,适合于对于数据持久性没有要求的1亿一下的小表。在ClickHouse中,通常用来做临时表。
- Buffer:为目标表设置一个内存buffer,当buffer达到了一定条件之后会flush到磁盘。
- File:直接将本地文件作为数据存储。
- Null:写入数据被丢弃、读取数据为空。
MergeTree系列
Log、Special、Integration主要用于特殊用途,场景相对有限。MergeTree系列才是官方主推的存储引擎,支持几乎所有ClickHouse核心功能。
以下重点介绍MergeTree、ReplacingMergeTree、CollapsingMergeTree、VersionedCollapsingMergeTree、SummingMergeTree、AggregatingMergeTree引擎。
MergeTree
MergeTree表引擎主要用于海量数据分析,支持数据分区、存储有序、主键索引、稀疏索引、数据TTL等。MergeTree支持所有ClickHouse SQL语法,但是有些功能与MySQL并不一致,比如在MergeTree中主键并不用于去重.MergeTree虽然有主键索引,但是其主要作用是加速查询,而不是类似MySQL等数据库用来保持记录唯一。即便在Compaction完成后,主键相同的数据行也仍旧共同存在。
ReplacingMergeTree
为了解决MergeTree相同主键无法去重的问题,ClickHouse提供了ReplacingMergeTree引擎,用来做去重。
虽然ReplacingMergeTree提供了主键去重的能力,但是仍旧有以下限制:
- 在没有彻底optimize之前,可能无法达到主键去重的效果,比如部分数据已经被去重,而另外一部分数据仍旧有主键重复。
- 在分布式场景下,相同primary key的数据可能被sharding到不同节点上,不同shard间可能无法去重。
- optimize是后台动作,无法预测具体执行时间点。
- 手动执行optimize在海量数据场景下要消耗大量时间,无法满足业务即时查询的需求。
因此ReplacingMergeTree更多被用于确保数据最终被去重,而无法保证查询过程中主键不重复。
CollapsingMergeTree
ClickHouse实现了CollapsingMergeTree来消除ReplacingMergeTree的功能限制。该引擎要求在建表语句中指定一个标记列Sign,后台Compaction时会将主键相同、Sign相反的行进行折叠,也即删除。
CollapsingMergeTree将行按照Sign的值分为两类:Sign=1的行称之为状态行,Sign=-1的行称之为取消行。
每次需要新增状态时,写入一行状态行;需要删除状态时,则写入一行取消行。
在后台Compaction时,状态行与取消行会自动做折叠(删除)处理。而尚未进行Compaction的数据,状态行与取消行同时存在。
因此为了能够达到主键折叠(删除)的目的,需要业务层进行适当改造:
- 执行删除操作需要写入取消行,而取消行中需要包含与原始状态行主键一样的数据(Sign列除外)。所以在应用层需要记录原始状态行的值,或者在执行删除操作前先查询数据库获取原始状态行。
- 由于后台Compaction时机无法预测,在发起查询时,状态行和取消行可能尚未被折叠;另外,ClickHouse无法保证primary key相同的行落在同一个节点上,不在同一节点上的数据无法折叠。因此在进行count(*)、sum(col)等聚合计算时,可能会存在数据冗余的情况。为了获得正确结果,业务层需要改写SQL,将count()、sum(col)分别改写为sum(Sign)、sum(col * Sign)。
CollapsingMergeTree虽然解决了主键相同的数据即时删除的问题,但是状态持续变化且多线程并行写入情况下,状态行与取消行位置可能乱序,导致无法正常折叠。
VersionedCollapsingMergeTree
为了解决CollapsingMergeTree乱序写入情况下无法正常折叠问题,VersionedCollapsingMergeTree表引擎在建表语句中新增了一列Version,用于在乱序情况下记录状态行与取消行的对应关系。主键相同,且Version相同、Sign相反的行,在Compaction时会被删除。
与CollapsingMergeTree类似, 为了获得正确结果,业务层需要改写SQL,将count()、sum(col)分别改写为sum(Sign)、sum(col * Sign)。
SummingMergeTree
ClickHouse通过SummingMergeTree来支持对主键列进行预先聚合。在后台Compaction时,会将主键相同的多行进行sum求和,然后使用一行数据取而代之,从而大幅度降低存储空间占用,提升聚合计算性能。
值得注意的是:
- ClickHouse只在后台Compaction时才会进行数据的预先聚合,而compaction的执行时机无法预测,所以可能存在部分数据已经被预先聚合、部分数据尚未被聚合的情况。因此,在执行聚合计算时,SQL中仍需要使用GROUP BY子句。
- 在预先聚合时,ClickHouse会对主键列之外的其他所有列进行预聚合。如果这些列是可聚合的(比如数值类型),则直接sum;如果不可聚合(比如String类型),则随机选择一个值。
- 通常建议将SummingMergeTree与MergeTree配合使用,使用MergeTree来存储具体明细,使用SummingMergeTree来存储预先聚合的结果加速查询。
AggregatingMergeTree
AggregatingMergeTree也是预先聚合引擎的一种,用于提升聚合计算的性能。与SummingMergeTree的区别在于:SummingMergeTree对非主键列进行sum聚合,而AggregatingMergeTree则可以指定各种聚合函数。
AggregatingMergeTree的语法比较复杂,需要结合物化视图或ClickHouse的特殊数据类型AggregateFunction一起使用。在insert和select时,也有独特的写法和要求:写入时需要使用-State语法,查询时使用-Merge语法。
1.2 数据表字段选择
所有字段选择按照最小原则,字段选取从以下类型中选取
数值类型
Int类型
固定长度的整数类型又包括有符号和无符号的整数类型。
- 有符号整数类型
类型 | 字节 | 范围 |
---|---|---|
Int8 | 1 | [-2^7 ~2^7-1] |
Int16 | 2 | [-2^15 ~ 2^15-1] |
Int32 | 4 | [-2^31 ~ 2^31-1] |
Int64 | 8 | [-2^63 ~ 2^63-1] |
Int128 | 16 | [-2^127 ~ 2^127-1] |
Int256 | 32 | [-2^255 ~ 2^255-1] |
Decimal类型
有符号的定点数,可在加、减和乘法运算过程中保持精度。ClickHouse提供了Decimal32、Decimal64和Decimal128三种精度的定点数,支持几种写法:
-
Decimal(P, S)
-
Decimal32(S)
数据范围:( -1 * 10^(9 - S), 1 * 10^(9 - S) )
-
Decimal64(S)
数据范围:( -1 * 10^(18 - S), 1 * 10^(18 - S) )
-
Decimal128(S)
数据范围: ( -1 * 10^(38 - S), 1 * 10^(38 - S) )
-
Decimal256(S)
数据范围:( -1 * 10^(76 - S), 1 * 10^(76 - S) )
其中:P代表精度,决定总位数(整数部分+小数部分),取值范围是1~76
S代表规模,决定小数位数,取值范围是0~P
- 两个不同精度的数据进行四则运算时,结果数据已最大精度为准
字符串类型
String
字符串可以是任意长度的。它可以包含任意的字节集,包含空字节。因此,字符串类型可以代替其他 DBMSs 中的VARCHAR、BLOB、CLOB 等类型。
UUID
UUID是一种数据库常见的主键类型,在ClickHouse中直接把它作为一种数据类型。UUID共有32位,它的格式为8-4-4-4-12,比如:
61f0c404-5cb3-11e7-907b-a6006ad3dba0-- 当不指定uuid列的值时,填充为000000000-0000-0000-0000-000000000000123
日期类型
Date类型
用两个字节存储,表示从 1970-01-01 (无符号) 到当前的日期值。日期中没有存储时区信息。
DateTime类型
用四个字节(无符号的)存储 Unix 时间戳。允许存储与日期类型相同的范围内的值。最小值为 0000-00-00 00:00:00。时间戳类型值精确到秒(没有闰秒)。时区使用启动客户端或服务器时的系统时区。
db01 :) CREATE TABLE t_date (x date,y datetime) ENGINE=TinyLog;CREATE TABLE t_date( `x` date, `y` datetime)ENGINE = TinyLogOk.0 rows in set. Elapsed: 0.004 sec. db01 :) INSERT INTO t_date VALUES('2020-10-01','2020-10-01 00:00:00');INSERT INTO t_date VALUESOk.1 rows in set. Elapsed: 0.012 sec. db01 :) select * from t_date;SELECT *FROM t_date┌──────────x─┬───────────────────y─┐│ 2020-10-01 │ 2020-10-01 00:00:00 │└────────────┴─────────────────────┘1 rows in set. Elapsed: 0.007 sec.
二、SQL规范
-
禁止select *
只查询需要的字段可以减少磁盘io和网络io,提升查询性能
-
禁止在大结果集上构造虚拟列
虚拟列非常消耗资源浪费性能,拿到pv uv后在前端显示时构造比率
-
关联查询时小表在后(大表 join 小表),并必须有关联条件(clickhouse建议控制在两张表以内的join)
无论是Left Join 、Right Join还是Inner Join永远都是拿着右表中的每一条记录到左表中查找该记录是否存在
-
使用 uniqCombined 替代 distinct
uniqCombined对去重进行了优化,通过近似去重提升十倍查询性能
-
建议查询指定分区
通过指定分区字段会减少底层数据库扫描的文件数量,提升查询性能
-
建议使用 limit 限制返回数据条数
使用limit返回指定的结果集数量,不会进行向下扫描,大大提升了查询效率
发表评论
最新留言
关于作者
