Clickhouse表引擎
表引擎是Clickhouse的一大特色,表引擎决定了如何存储数据,包括:
- 数据的存储方式和位置,写到哪里以及从哪里读取数据
- 支持哪些查询以及如何支持
- 并发数据访问
- 索引的使用(如果存在)
- 是否可以执行多线程请求
- 数据复制参数
特别注意:引擎的名称大小写敏感
一、TinyLog
专门存放小数据,什么参数不用设置,以列文件形式存储在磁盘中,不支持索引,没有并发控制,会出现覆盖数据风险。生产环境上作用有限,可以用于平时练习测试用。
二、Memory
内存引擎,数据保存在内存中,Clickhouse的服务不能停,服务一停,数据就会没有;查询速度快,没有并发控制,生产环境上作用有限,可以用于平时练习测试用。
三、MergeTree
Clickhouse最强大的表引擎当属MergeTree(合并树)引擎及该系列(*MergeTree)中的其他引擎;地位相当于innodb之于Mysql; 而且基于MergeTree,还衍生出了很多非常有特色的引擎。
特点:有索引,可分区
建表语句:
CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
(
name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1],
name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2],
...
INDEX index_name1 expr1 TYPE type1(...) GRANULARITY value1,
INDEX index_name2 expr2 TYPE type2(...) GRANULARITY value2
) ENGINE = MergeTree()
[PARTITION BY expr] --toYYYYMMDD(datetime)
[ORDER BY expr]
[PRIMARY KEY expr]
[SAMPLE BY expr]
[TTL expr [DELETE|TO DISK 'xxx'|TO VOLUME 'xxx'], ...]
[SETTINGS name=value, ...]
intHash32()和toYYYYDD()两个函数在建表时常用
order by是必要的,partition by和primary key等不是必须的
CREATE TABLE tutorial.hits_v1(
`WatchID` UInt64,
`JavaEnable` UInt8,
`Title` String,
`GoodEvent` Int16,
`EventTime` DateTime,
`EventDate` Date,
`CounterID` UInt32,
`ClientIP` UInt32,
`ClientIP6` FixedString(16),
`RegionID` UInt32,
`UserID` UInt64,
`CounterClass` Int8,
`OS` UInt8,
`UserAgent` UInt8,
`URL` String,
`Referer` String,
`URLDomain` String,
`RefererDomain` String,
`Refresh` UInt8,
`IsRobot` UInt8,
`RefererCategories` Array(UInt16),
`URLCategories` Array(UInt16),
`URLRegions` Array(UInt32),
`RefererRegions` Array(UInt32),
`ResolutionWidth` UInt16,
`ResolutionHeight` UInt16,
`ResolutionDepth` UInt8,
`FlashMajor` UInt8,
`Params` Array(String),
`Goals` Nested(
ID UInt32,
Serial UInt32,
EventTime DateTime,
Price Int64,
OrderID String,
CurrencyID UInt32)
)
ENGINE = MergeTree()
PARTITION BY toYYYYMM(EventDate)
ORDER BY (CounterID, EventDate, intHash32(UserID))
SAMPLE BY intHash32(UserID)
SETTINGS index_granularity = 8192
Import Data:由文件(tsv,csv)导入数据进指定表,在Clickhouse-client命令中Shell语句执行,代码如下:
clickhouse-client --query "INSERT INTO tutorial.hits_v1 FORMAT TSV" --max_insert_block_size=100000 < hits_v1.tsv
clickhouse-client --query "INSERT INTO tutorial.visits_v1 FORMAT TSV" --max_insert_block_size=100000 < visits_v1.tsv
SAMPLE BY — An expression for sampling. Optional.
If a sampling expression is used, the primary key must contain it. Example: SAMPLE BY intHash32(UserID) ORDER BY (CounterID, EventDate, intHash32(UserID)).
The partition key can also be a tuple of expressions (similar to the primary key). For example:
ENGINE = ReplicatedCollapsingMergeTree('/clickhouse/tables/name', 'replica1', Sign)
PARTITION BY (toMonday(StartDate), EventType)
ORDER BY (CounterID, StartDate, intHash32(UserID));
MergerTree表对新插入的数据,往往会在15钟内进行按分区自动合同,如果想快点,也可以使用OPTIMIZE语句,加快速度,一般不要做,生产环境还是靠后台自己合并;如下例子:
OPTIMIZE TABLE visits [final] PARTITION 201902;
Primary Key:
索引:只是一个一级索引,不是唯一主键,主键可以设置常用维度设为主键,不用在乎唯一,稀疏索引; index granulartiy: 索引颗粒度:两个索引之间的间隔
索引 + 扫描:先用索引确定要查询的值的区间,然后再去这个区间中扫描。
好处:索引量小了,可以把索引放在内存中,mysql就做不到把索引加载到内存中Order By(必填):
主键默认值是Order by, 如果要自定义主键,必须是Order by的前缀
比如order by 字段是(id,sku_id),那么主键必须是id 或者(id,sku_id)
Order by设定了分区内的数据按照哪些字段顺序进行有序保存。二级索引
目前在Clickhouse的官网上二级索引的功能是被标注为实验性的。例子如下:
create table t_order_mt(
id UInt32,
sku_id String,
total_amount Decimal(16,2),
create_time Datetime,
INDEX idx_amount total_amount TYPE minmax GRANULARITY 5
) engine=MergeTree
partition by toYYYYMMDD(create_time)
primary key(id)
order by (id,sku_id)
其中INDEX idx_mount total_mount TYPE minmax GRANULARITY 5是定义二级索引的
GRANULARITY: 是设定二级索引对于一级索引粒度的粒度。
- 数据TTL
TTL即Time To Live,MergeTree提供了可以管理数据或者列的生命周期功能
列级别TTL,如下:
create table t_order_mt(
id UInt32,
sku_id String,
total_amount Decimal(16,2) TTL create_time + interval 10 SECOND,
create_time Datetime,
) engine=MergeTree
partition by toYYYYMMDD(create_time)
primary key(id)
order by (id,sku_id)
表级别TTL,更加常用,如下:
alter table t_order_mt modify TTL create_time + INTERVAL 10 SECOND;
涉及判断的字段必须是Date或者Datetime类型,推荐使用分区的日期字段;
能够使用的时间周期
-SECOND
-MINUTE
-HOUR
-DAY
-WEEK
-MONTH
-QUARTER
-YEAR
加快效果可以使用OPTIMIZE
四、HDFS
ENGINE = HDFS(URI, format)
The URI parameter is the whole file URI in HDFS.
The format parameter specifies one of the available file formats.
To performSELECT queries, the format must be supported for input, and to performINSERT queries – for output.
The available formats are listed in the Formats
section.
五、Kafka
CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster](
name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1],
name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2],
...
) ENGINE = Kafka()
SETTINGS
kafka_broker_list = 'host:port',
kafka_topic_list = 'topic1,topic2,...',
kafka_group_name = 'group_name',
kafka_format = 'data_format'[,]
[kafka_row_delimiter = 'delimiter_symbol',]
[kafka_schema = '',]
[kafka_num_consumers = N,]
[kafka_max_block_size = 0,]
[kafka_skip_broken_messages = N,]
[kafka_commit_every_batch = 0,]
[kafka_thread_per_consumer = 0]
kafka_format:表示kafka消息中数据格式,这边一般推荐使用JSONEachRow
,如下图所示是Clickhouse能接受的数据格式
[图片上传失败...(image-7878a9-1600845006241)]
建表例子如下:
CREATE TABLE queue2 (
timestamp UInt64,
level String,
message String
) ENGINE = Kafka SETTINGS kafka_broker_list = 'localhost:9092',
kafka_topic_list = 'topic',
kafka_group_name = 'group1',
kafka_format = 'JSONEachRow',
kafka_num_consumers = 4;
字节跳动目前使用Clickhouse实时接kafka消息的,就是使用kafka engine处理的,不过字节跳动对Clickhouse进行了二次开发,进行了优化
六、ReplacingMergeTree
ReplacingMergeTree是MergeTree的一个变种,它存储特性完全继承MergeTree,多了一个去重功能
去重时机:数据的去重只会在合并的过程中出现;合并会在未知的时间在后台进行,所以你无法预先作出计划。有一些数据可能仍未被处理。
去重范围:如果表经过了分区,去重只会在分区内部进行去重,不能执行跨分区去重。
所以 ReplacingMergeTree能力有限,ReplacingMergeTree适用在后台清除重复的数据以节省空间,但是不保证没有重复的数据出现。
create table t_order_rmt(
id UInt32,
sku_id String,
total_amount Decimal64(2),
create_time Datetime
)engine = ReplacingMergeTree(create_time)
partition by toYYYYMMDD(create_time)
primary key (id)
order by (id,sku_id)
ReplacingMergeTree()填入的参数为版本字段,重复数据保留版本字段值最大的,如果不填版本字段,默认保留最后一条,是使用order by字段去重
总结:
- 实际上是使用order by字段作为唯一建
- 去重不能跨分区
- 只有合并分区才会去重
- 认定重复的数据保留,版本字段值最大的
- 如果版本字段相同则保留最后一笔
七、SummingMergeTree
对于不查询明细,只关心以维度进行汇总聚合结果的场景,如果只使用普通的MergeTree的话,无论是存储空间的开销,还是查询时临时聚合的开销都比较大。
Clickhouse为了这种场景,提供了一种能够“预聚合”引擎,SummingMergeTree
例子:
create table t_order_smt(
id UInt32,
sku_id String,
total_amount Decimal64(2),
create_time Datetime
)engine = SummingMergeTree(total_amount)
partition by toYYYYMMDD(create_time)
primary key (id)
order by (id,sku_id)
SummingMergeTree()中填的是需要sum的字段,以order by为维度列进行聚合,不能跨分区聚合
既不是维度,又不是度量的其他列:保留第一行
设计聚合表的话,主键id(唯一健值可以去掉),所有字段全部是维度,度量或者时间戳
统计有延迟
FAQ: 能不能直接select 获取聚合好的值呢:
答: 不行,数据可能会有两部分,一部分是历史聚合好的值,还有一部分还没来得及聚合的临时明细,还需要进行sum聚合,使用SummingMergeTree性能会快很多