- Published on
ClickHouse 重要概念
- Authors
- Name
- Liant
重要概念
表
语法
CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
(
name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1] [TTL expr1],
name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2] [TTL expr2],
...
INDEX index_name1 expr1 TYPE type1(...) GRANULARITY value1,
INDEX index_name2 expr2 TYPE type2(...) GRANULARITY value2
) ENGINE = MergeTree()
ORDER BY expr
[PARTITION BY expr]
[PRIMARY KEY expr]
[SAMPLE BY expr]
[TTL expr [DELETE|TO DISK 'xxx'|TO VOLUME 'xxx'], ...]
[SETTINGS name=value, ...]
ORDER BY expr
排序键,必填表示排序键,用于指定在一个分区内,数据以何种标准进行排序。排序键既可以是单个字段,例如 ORDER BY CounterID,也可以是通过元组声明的多个字段,例如 ORDER BY (CounterID, EventDate)。如果是多个字段,那么会先按照第一个字段排序,如果第一个字段中有相同的值,那么再按照第二个字段排序,依次类推。总之在每个分区内,数据是按照分区键排好序的,但多个分区之间就没有这种关系了。
定义表排序键为:
order by A,B,C
,那么通常过滤查询Where A
很快,但是Where C
会慢一些。PARTITION BY
表分区表示分区键,用于指定表数据以何种标准进行分区。分区键既可以是单个字段、也可以通过元组的形式指定多个字段,同时也支持使用列表达式。如果不支持分区键,那么 ClickHouse 会生成一个名称为 all 的分区,合理地使用分区可以有效的减少查询时数据量。最常见的莫过于按照时间分区了,数据量非常大的时候可以按照天来分区,一天一个分区,这样查找某一天的数据时直接从指定分区中查找即可。
PRIMARY KEY
主键(一级索引),注:主键必须是排序键的子集表示主键,声明之后会依次按照主键字段生成一级索引,用于加速表查询。如果不指定,那么主键默认和排序键相同,所以通常直接使用 ORDER BY 代为指定主键,无须使用 PRIMARY KEY 声明。所以一般情况下,在每个分区内,数据与一级索引以相同的规则升序排列(因为数据是按照排序键排列的,而一级索引也是按排序键、也就是主键进行排列的)。和其它关系型数据库不同,MergeTree 允许主键有重复数据(可以通过 ReplacingMergeTree 实现去重)。
SAMPLE BY
抽样表达式抽样表达式。用于声明数据以何种标准进行采样,注意:如果声明了此配置项,那么主键的配置中也要声明同样的表达式。例如:
SAMPLE BY intHash32(UserID)
TTL
数据的存活时间在 MergeTree 中,可以为某个列字段或整张表设置 TTL。当时间到达时,如果是列字段级别的 TTL,则会删除这一列的数据;如果是表级别的 TTL,则会删除整张表的数据;如果同时设置了列级别和表级别的 TTL,则会以先到期的那个为主。
SETTINGS
控制(可选)行为的附加参数用于指定一些额外的参数,以 name=value 的形式出现,name 主要包含 index_granularity、min_compress_block_size、index_granularity_bytes、enbale_mixed_granularity_parts、merge_with_ttl_timeout、storage_policy,比如:
index_granularity=8192, min_compress_block_size=6536
表引擎
MergeTree (合并)
MergeTree是由特殊设计的文件存储方式实现的,核心知识点主要包括:
分区 分区合并 索引 数据存储 数据标记 写入过程 查询过程
- 分区
数据是以分区目录的形式进行组织的,每个分区目录下包含索引、数据块等文件。完整的存储结构如下

一张数据表的完整物理结构分为3个层级,依次是数据表目录(库/表)、分区目录及各分区下具体的数据文件。接下来就逐一介绍它们的作用。
partition
:分区目录,余下各类数据文件(primary.idx、[Column].mrk、[Column].bin等)都是以分区目录的形式被组织存放的,相同分区数据会被合并到同一个分区目录;
checksums.txt
:校验文件,使用二进制格式存储。它保存了余下各类文件(primary.idx、count.txt等)的size大小及size的哈希值,用于快速校验文件的完整性和正确性。
columns.txt
:列信息文件,使用明文格式存储。用于保存此数据分区下的列字段信息。
count.txt
:计数文件,使用明文格式存储。用于记录当前数据分区目录下数据的总行数。
primary.idx
:一级索引文件,使用二进制格式存储。用于存放 稀疏索引 ,借助稀疏索引,在数据查询的时能够排除主键条件范围之外的数据文件;
[Column].bin
:数据文件,使用压缩格式存储,默认为LZ4压缩格式,每一个列字段都拥有独立的.bin数据文件,并以列字段名称命名;
[Column].mrk
:列字段数据标记文件,使用二进制格式存储。标记文件中保存了.bin文件中数据的偏移量信息。标记文件与稀疏索引对齐,又与.bin文件一一对应,所以MergeTree通过标记文件建立了primary.idx稀疏索引与.bin数据文件之间的映射关系;
partition.dat与minmax[Column].idx
:如果使用了分区键,例如PARTITION BY EventTime,则会额外生成partition.dat与minmax索引文件,它们均使用二进制格式存储。partition.dat用于保存当前分区下分区表达式最终生成的值;而minmax索引用于记录当前分区下分区字段对应原始数据的最小和最大值;
skp_idx_[Column].idx与skp_idx_[Column].mrk
:如果在建表语句中声明了二级索引,则会额外生成相应的二级索引与标记文件,它们同样也使用二进制存储。二级索引在ClickHouse中又称跳数索引。
分区目录名:PartitionID_MinBlockNum_MaxBlockNum_Level,如果建表的时候没有指定分区,则默认只有一个分区以
all_
开头,如:all_1_4_1
。
- 分区合并
每一次数据写入(一次INSERT语句),MergeTree都会生成一项新的分区目录。在一段时间之后,系统触发合并操作.会将不同区块的数据做合并成一个新的区块.老的区块在一段时间后才会被删除。
- 索引
一级索引:
MergeTree的主键使用PRIMARY KEY
定义,待主键定义之后,MergeTree会依据index_granularity间隔(默认8192行),为数据表生成一级索引并保存至primary.idx文件内,索引数据按照PRIMARY KEY
排序。如果不指定PRIMARY KEY
,则使用ORDER KEY
。同时指定PRIMARY KEY
与ORDER KEY
时,需要PRIMARY KEY
为ORDER KEY
子集。
二级索引:
参考一级索引,多个索引字段匹配的key值存为一个索引。
- 数据存储
数据按列存储:
数据量不多时所有数据文件被合并为data.bin
,数据量较多时,数据按列存储,每个列也是独立存储的,也就是每个列字段都拥有一个与之对应的 *.bin
文件.
压缩数据块:
首先数据是经过压缩的;其次,数据会实现按照 ORDER BY
的声明排序;最后数据以压缩数据块的形式被组织并写入 *.bin
文件。
-- 查询文件结构
SELECT table, name, path, active, * FROM `system`.parts WHERE table = '<table name>'
- 数据标记

一行标记数据使用一个元组表示,元组内包含两个整型数值的偏移量信息。它们分别表示在此段数据区间内,在对应的.bin压缩文件中,压缩数据块的起始偏移量;以及将该数据压缩块解压后,其未压缩数据的起始偏移量。
- 写入过程
数据写入的第一步是生成分区目录,伴随着每一批数据的写入,都会生成一个新的分区目录。在后续的某一时刻,属于相同分区的分区目录会被合并到一起。紧接着按照 index_granularity 索引粒度,会分别生成 primary.idx 一级索引(如果声明了二级索引,还会创建二级索引文件)、每一个列字段的压缩数据文件(.bin)和数据标记文件(.mrk),如果数据量不大,则是 data.bin
和 data.mrk
文件。
- 查询过程
数据查询的本质可以看做是一个不断减少数据范围的过程,在最理想的情况下,MergeTree 首先可以借助分区索引、一级索引和二级索引将数据扫描范围缩至最小。然后再借助数据标记,将需要解压与计算的数据范围缩至最小。以下图为例,该图展示了在最优的情况下,经过层层过滤,最终获取最小数据范围的过程。
ReplacingMergeTree (合并去重)
- 不同分区,不能去重
- 去重是以
ORDER BY
为基准,不是PRIMARY KEY
。 - 查询时无法保证重复数据
-- 示例
CREATE TABLE emp_replacingmergetree_2
(
emp_id UInt16,
name String,
work_place String,
age UInt8,
depart String,
salary Decimal32(2)
) ENGINE = ReplacingMergeTree()
ORDER BY (emp_id, name) -- 注意排序key是两个字段
PRIMARY KEY emp_id -- 主键是一个字段
PARTITION BY work_place
;
CollapsingMergeTree (折叠合并删除数据)
- 建表时需要一个Sign字段,1为保留,-1为删除。
- 查询时,需要考虑Sign值。
- 多线程时,乱序无法正常折叠。
CREATE TABLE emp_collapsingmergetree
(
emp_id UInt16 COMMENT '员工id',
name String COMMENT '员工姓名',
work_place String COMMENT '工作地点',
age UInt8 COMMENT '员工年龄',
depart String COMMENT '部门',
salary Decimal32(2) COMMENT '工资',
sign Int8
) ENGINE = CollapsingMergeTree(sign)
ORDER BY (emp_id, name)
PARTITION BY work_place
;
VersionedCollapsingMergeTree (多线程合并删除数据)
- 建表时需要一个Sign字段,一个Version字段。
- 查询时,需要考虑Sign值。
CREATE TABLE emp_versioned
(
emp_id UInt16 COMMENT '员工id',
name String COMMENT '员工姓名',
work_place String COMMENT '工作地点',
age UInt8 COMMENT '员工年龄',
depart String COMMENT '部门',
salary Decimal32(2) COMMENT '工资',
sign Int8,
version Int8
) ENGINE = VersionedCollapsingMergeTree(sign, version)
ORDER BY (emp_id, name)
PARTITION BY work_place
;
SummingMergeTree (合并数据)
- 会将主键相同的多行进行预先求和(sum),不能求和则随机选择一个值。
CREATE TABLE emp_summingmergetree
(
emp_id UInt16 COMMENT '员工id',
name String COMMENT '员工姓名',
work_place String COMMENT '工作地点',
age UInt8 COMMENT '员工年龄',
depart String COMMENT '部门',
salary Decimal32(2) COMMENT '工资'
) ENGINE = SummingMergeTree(salary)
ORDER BY (emp_id, name)
PRIMARY KEY emp_id
PARTITION BY work_place
;
AggregatingMergeTree (聚合计算)
- 可以指定各种聚合函数
-- 使用雾化视图示例,另外一种直接指定表引擎为AggregatingMergeTree
-- 基础明细表
CREATE TABLE emp_mergetree_base
(
emp_id UInt16 COMMENT '员工id',
name String COMMENT '员工姓名',
work_place String COMMENT '工作地点',
age UInt8 COMMENT '员工年龄',
depart String COMMENT '部门',
salary Decimal32(2) COMMENT '工资'
) ENGINE = MergeTree()
ORDER BY (emp_id, name)
PARTITION BY work_place
;
-- 聚合物化视图,使用 AggregatingMergeTree 作为表引擎
CREATE MATERIALIZED VIEW view_emp_agg
ENGINE = AggregatingMergeTree()
PARTITION BY emp_id
ORDER BY (emp_id, name)
AS SELECT emp_id,
name,
sumState(salary) AS salary
FROM emp_mergetree_base
GROUP BY emp_id, name;
-- 向基础表插入数据
INSERT INTO emp_mergetree_base
VALUES (1, 'tom', '上海', 25, '技术部', 20000),
(1, 'tom', '上海', 26, '人事部', 10000);
-- 从基础明细表查询明细数据
SELECT * FROM emp_mergetree_base;
-- 从物化视图查询聚合数据
SELECT emp_id, name, sumMerge(salary) AS salary
FROM view_emp_agg
GROUP BY emp_id, name;
GraphiteMergeTree (用来存储时序数据库Graphites的数据)
该引擎用来对 Graphite数据进行瘦身及汇总。
查询语法
[WITH expr_list|(subquery)]
SELECT [DISTINCT] expr_list
[FROM [db.]table | (subquery) | table_function] [FINAL]
[SAMPLE sample_coeff]
[ARRAY JOIN ...]
[GLOBAL] [ANY|ALL|ASOF] [INNER|LEFT|RIGHT|FULL|CROSS] [OUTER|SEMI|ANTI] JOIN (subquery)|table (ON <expr_list>)|(USING <column_list>)
[PREWHERE expr]
[WHERE expr]
[GROUP BY expr_list] [WITH TOTALS]
[HAVING expr]
[ORDER BY expr_list] [WITH FILL] [FROM expr] [TO expr] [STEP expr]
[LIMIT [offset_value, ]n BY columns]
[LIMIT [n, ]m] [WITH TIES]
[UNION ALL ...]
[INTO OUTFILE filename]
[FORMAT format]
ClickHouse的SELECT语句的语法和通用的SQL的SELECT语句非常类似,包括:
- SELECT:指定返回结果字段
- DISTINCT:去重
- FROM:指定要查询的表或子查询
- JOIN:表连接,支持内连接和外连接、左连接和右连接
- WHERE:筛选条件
- GROUP BY:分组,和聚合函数一起使用
- HAVING:分组后筛选
- ORDER BY:排序
- LIMIT:限制返回记录数
- UNION ALL:并集;ClickHouse目前只支持UNION ALL,还不支持UNION
ClickHouse的SELECT语句中也有一些特殊的用法:
- WITH:设置查询中要用到的变量
- SAMPLE:数据取样,类似Pandas库的sample()函数
- PREWHERE:预筛选,起到提升性能作用
- ARRAY JOIN:数组连接,用来展开数组或嵌套字段,一行变多行
- LIMIT BY:分组,再从每组中取前n条记录
- INTO OUTFILE:到处表数据到文件,再用FORMAT指定文件格式
查询子句
WITH 子句
-- 在WITH子句中定义一个变量并赋值,然后在SELECT子句中通过别名使用该变量
with '2014-03-17' as dt \
select count(1) from hits_v1 where EventDate = dt;
-- 在WITH子句中定义一个函数,然后在SELECT子句中通过别名使用该函数
with round(Duration / 60)as duration_minutes \
select StartDate, max(duration_minutes) as max_duration_minutes from visits_v1 \
group by StartDate, Duration \
order by max_duration_minutes desc \
limit 10;
-- 在WITH子句中定义一个子查询,然后在SELECT子句中通过别名使用该子查询
-- 该子查询只能返回一行数据
with ( \
select sum(Duration) from visits_v1 \
) as total_duration \
select StartDate, sum(Duration) / total_duration as duration_percentage from visits_v1 \
group by StartDate, Duration \
limit 10;
SAMPLE 子句
需要在建表的时候指定 SAMPLE BY
CREATE TABLE tutorial.hits_v1 \
(
...
)
ENGINE = MergeTree() \
PARTITION BY toYYYYMM(EventDate) \
ORDER BY (CounterID, EventDate, intHash32(UserID)) \
SAMPLE BY intHash32(UserID) \
SETTINGS index_granularity = 8192;
-- 按比例采样
-- 采样结果记录数
select count(1) from hits_v1 sample 0.1
-- 采样数据,默认限制返回10000条
select CounterID, UserID, EventDate, EventTime from hits_v1 sample 0.1
-- 采样数据,限制返回10条
select CounterID, UserID, EventDate, EventTime from hits_v1 sample 0.1 limit 10
-- 按记录数采样
-- 采样记录数较小时,采样结果数据为0条
select count(1) from hits_v1 sample 100
-- 采样记录数大过索引粒度时,采样结果数据记录数接近采样记录数
elect count(1) from hits_v1 sample 10000
-- 采样数据,默认限制返回10000条
select CounterID, UserID, EventDate, EventTime from hits_v1 sample 20000
-- 采样数据,限制返回10条
select CounterID, UserID, EventDate, EventTime from hits_v1 sample 20000 limit 10
-- 按比例和偏移量采样,类似于按比例采样
select CounterID, UserID, EventDate, EventTime from hits_v1 sample 0.1 offset 0.3 limit 10
PREWHERE 子句
-- optimize_move_to_prewhere为1时,表示开始PREWHERE自动优化
select name, value from system.settings where name like '%prewhere%'
ARRAY JOIN 子句
----------------------------------------- list类型 ---------------------------------------
-- 不使用ARRAY JOIN
select WatchID, RefererCategories from hits_v1 where WatchID = 4944118417295196513
-- 结果:
┌─────────────WatchID─┬─RefererCategories─┐
│ 4944118417295196513 │ [6,98,456,8586] │
└─────────────────────┴───────────────────┘
-- 使用ARRAY JOIN
select WatchID, RefererCategories \
from hits_v1 \
array join RefererCategories \
where WatchID = 4944118417295196513;
-- 结果:
─────────────WatchID─┬─RefererCategories─┐
│ 4944118417295196513 │ 6 │
│ 4944118417295196513 │ 98 │
│ 4944118417295196513 │ 456 │
│ 4944118417295196513 │ 8586 │
└─────────────────────┴───────────────────┘
----------------------------------------- nested类型 ---------------------------------------
-- 不使用ARRAY JOIN
select WatchID, ParsedParams.Key1, ParsedParams.Key2 from hits_v1 where WatchID = 5024825574842900819
-- 结果:
┌─────────────WatchID─┬─ParsedParams.Key1───────────┬─ParsedParams.Key2───┐
│ 5024825574842900819 │ ['gen_timestamp','Toolbar'] │ ['group','true" /'] │
└─────────────────────┴─────────────────────────────┴─────────────────────┘
-- 使用ARRAY JOIN
select WatchID, ParsedParams.Key1, ParsedParams.Key2 \
from hits_v1 \
array join ParsedParams \
where WatchID = 5024825574842900819;
-- 结果:
┌─────────────WatchID─┬─ParsedParams.Key1─┬─ParsedParams.Key2─┐
│ 5024825574842900819 │ gen_timestamp │ group │
│ 5024825574842900819 │ Toolbar │ true" / │
└─────────────────────┴───────────────────┴───────────────────┘
LIMIT BY 子句
CREATE TABLE limit_by(id Int, val Int) ENGINE = Memory;
INSERT INTO limit_by VALUES (1, 10), (1, 11), (1, 12), (2, 20), (2, 21), (2, 22), (3, 31);
-- 排序
SELECT * FROM limit_by ORDER BY id, val
┌─id─┬─val─┐
│ 1 │ 10 │
│ 1 │ 11 │
│ 1 │ 12 │
│ 2 │ 20 │
│ 2 │ 21 │
│ 2 │ 22 │
│ 3 │ 31 │
└────┴─────┘
-- 分类排序,再在每个分组内取前2条记录
SELECT * FROM limit_by ORDER BY id, val LIMIT 2 BY id
┌─id─┬─val─┐
│ 1 │ 10 │
│ 1 │ 11 │
│ 2 │ 20 │
│ 2 │ 21 │
│ 3 │ 31 │
└────┴─────┘
-- 注意,与LIMIT 2的不同
SELECT * FROM limit_by ORDER BY id, val LIMIT 2
┌─id─┬─val─┐
│ 1 │ 10 │
│ 1 │ 11 │
└────┴─────┘
INTO OUTFILE子句
-- 输出到当前目录
-- 默认格式为TSV
-- 注意文件名必须用单引号来括起来,且不能用双引号括起来,否则会报错:Expected string literal
-- 目录下不能存在同名文件,否则会报错
select WatchID, JavaEnable, EventDate from hits_v1 limit 10 into outfile 'test.tsv'
-- 设置格式为CSV,CSV需要为全大小
select WatchID, JavaEnable, EventDate from hits_v1 limit 10 into outfile 'out.csv' format CSV
联表查询
SELECT
*
FROM imdb.roles
JOIN imdb.actors_dictionary
ON imdb.roles.actor_id = imdb.actors_dictionary.id
CRUD
插入数据
INSERT INTO helloworld.my_first_table (user_id, message, timestamp, metric) VALUES
(101, 'Hello, ClickHouse!', now(), -1.0 ),
(102, 'Insert a lot of rows per batch', yesterday(), 1.41421 ),
(102, 'Sort your data based on your commonly-used queries', today(), 2.718 ),
(101, 'Granules are the smallest chunks of data read', now() + 5, 3.14159 )
修改
ALTER TABLE [<database>.]<table> UPDATE <column> = <expression> WHERE <filter_expr>
-- 示例
ALTER TABLE website.clicks
UPDATE visitor_id = getDict('visitors', 'new_visitor_id', visitor_id)
WHERE visit_date < '2022-01-01'
删除
- 一般删除
ALTER TABLE [<database>.]<table> DELETE WHERE <filter_expr>
-- 示例
ALTER TABLE clicks ON CLUSTER main_cluster WHERE visit_date < '2022-01-02 15:00:00' AND page_id = '573'
- 轻量级删除
SET allow_experimental_lightweight_delete = true;
DELETE FROM hits WHERE Title LIKE '%hello%';
PROJECTION
可以将Projection看成一种更加智能的物化视图
projection在功能上有点类似mysql的index,但跟MergeTree的index完全是两码事,MergeTree的index是主键基础上的跳数索引。Projection的主要构件包含三个部分,分别是:Projection定义、Projection存储、Projection查询分析。
-- 1)创建Projection
ALTER TABLE [db].name ADD PROJECTION name ( SELECT <COLUMN LIST EXPR> [GROUP BY] [ORDER BY] )
-- 2)删除Projection
ALTER TABLE [db].name DROP PROJECTION name
-- 3)手动触发物化(首次创建Projection时使用,否则只有新插入数据会自动物化)
ALTER TABLE [db.]table MATERIALIZE PROJECTION name IN PARTITION partition_name
-- 4)删除Projection磁盘文件,但不删除说明(类似Mysql的truncate)
ALTER TABLE [db.]table CLEAR PROJECTION name IN PARTITION partition_name
雾化视图 (Materialized views)
物化视图和普通视图最大的区别是物化视图实际存储了一份数据。用户查询的时候和表没有区别,更像是一张时刻在预计算的表。在创建物化视图的时候也需要定义存储引擎。
CREATE MATERIALIZED VIEW [IF NOT EXISTS] [db.]table_name [ON CLUSTER] [TO[db.]name]
[ENGINE = engine] [POPULATE] AS SELECT ...
创建物化视图有两种方式:直接创建,即创建物化视图时不带TO [db].[table],且必须指定ENGINE用于存储数据的表引擎,和建表类似,这种方法ClickHouse会创建一个隐藏的目标表来保存视图数据,可以通过SHOW TABLES查看;间接创建,即使用TO [db].[table]创建物化视图,[db].[table]必须是一张已经存在的表,用来保存视图数据,此时创建物化视图相当于在表上面附加了一个物化视图。需要注意,间接创建不能使用POPULATE关键字。
物化视图存储由CREATE语句中的SELECT查询转换的数据,SELECT语句可以包含DISTINCT、 GROUP BY、ORDER BY、LIMIT,如果是聚合操作,建议使用SummingMergeTree等引擎表。
POPULATE关键字定义了物化视图的初始更新策略,如果指定了POPULATE,则在创建视图时将SELECT表的存量数据插入视图,就像执行 CREATE TABLE … AS SELECT … 。否则,视图只更新创建视图以后插入到表中的数据。需要注意的是如果定义了POPULATE关键字,在创建视图期间插入表中的数据不会被插入到表中,所以不建议在有数据更新的情况下使用POPULATE。如果一定要同步历史数据,则可以选择没有业务数据更新的窗口期执行。
物化视图只同步插入的数据,对源表数据的任何更改(如更新、删除、删除分区等)都不会更改物化视图数据。
物化视图使用ALTER语句有一些局限,如果创建视图时使用的是TO [db.]name方式,则可以使用DETACH 语句卸载视图,然后执行ALTER语句,再通过ATTACH 语句加载前面卸载的视图。
删除视图可使用 DROP VIEW,虽然 DROP TABLE 也可以删除视图,但是不建议使用。
CREATE MATERIALIZED VIEW order_mv1
ENGINE=SummingMergeTree
PARTITION BY toYYYYMMDD(order_date) ORDER BY (id,order_date)
AS SELECT
id,
order_date,
sum(pay_number) as number,
sum(pay_amount) as amount
FROM order_detail
WHERE order_date > '2021-08-14'
GROUP BY id,order_date;
物化视图与表一样,也可以指定表引擎、分区键、主键以及设置参数。物化视图的本质是一个流式数据的使用场景,是累计计算的技术,所以要用历史数据做去重这样的分析,在物化视图里面是不太好用的。而且如果一张表加了好多物化视图,在写这张表的时候,就会消耗很多机器的资源,比如数据带宽占满、存储一下子增加了很多。
生存时间 TTL (Time-to-live)
重复数据消除策略
词典 (Dictionaries)
轻量级删除 (Lightweight Delete)
Transactional (ACID) support
需要注意的场景
- 没有完整的事务支持。
- 缺少高频率,低延迟的修改或删除已存在数据的能力。
- 稀疏索引使得ClickHouse不适合通过其键检索单行的点查询。