Published on

ClickHouse 重要概念

Authors
  • avatar
    Name
    Liant
    Twitter

重要概念

语法

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)

    https://zhuanlan.zhihu.com/p/573410204

  • 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官网介绍

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 KEYORDER KEY时,需要PRIMARY KEYORDER KEY子集。

二级索引: 参考一级索引,多个索引字段匹配的key值存为一个索引。

  • 数据存储

数据按列存储:数据量不多时所有数据文件被合并为data.bin,数据量较多时,数据按列存储,每个列也是独立存储的,也就是每个列字段都拥有一个与之对应的 *.bin 文件.

压缩数据块:首先数据是经过压缩的;其次,数据会实现按照 ORDER BY 的声明排序;最后数据以压缩数据块的形式被组织并写入 *.bin 文件。

-- 查询文件结构
SELECT table, name, path, active, * FROM `system`.parts WHERE table = '<table name>' 
  • 数据标记
数据标记(.mrk)文件主要用于匹配索引文件和数据块文件。 文件标记

一行标记数据使用一个元组表示,元组内包含两个整型数值的偏移量信息。它们分别表示在此段数据区间内,在对应的.bin压缩文件中,压缩数据块的起始偏移量;以及将该数据压缩块解压后,其未压缩数据的起始偏移量。

  • 写入过程

数据写入的第一步是生成分区目录,伴随着每一批数据的写入,都会生成一个新的分区目录。在后续的某一时刻,属于相同分区的分区目录会被合并到一起。紧接着按照 index_granularity 索引粒度,会分别生成 primary.idx 一级索引(如果声明了二级索引,还会创建二级索引文件)、每一个列字段的压缩数据文件(.bin)和数据标记文件(.mrk),如果数据量不大,则是 data.bindata.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─┐
49441184172951965136494411841729519651398494411841729519651345649441184172951965138586└─────────────────────┴───────────────────┘


----------------------------------------- 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─┐
110111112220221222331└────┴─────┘

-- 分类排序,再在每个分组内取前2条记录
SELECT * FROM limit_by ORDER BY id, val LIMIT 2 BY id
┌─id─┬─val─┐
110111220221331└────┴─────┘

-- 注意,与LIMIT 2的不同
SELECT * FROM limit_by ORDER BY id, val LIMIT 2
┌─id─┬─val─┐
110111└────┴─────┘

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)

Materialized views

物化视图和普通视图最大的区别是物化视图实际存储了一份数据。用户查询的时候和表没有区别,更像是一张时刻在预计算的表。在创建物化视图的时候也需要定义存储引擎。

CREATE MATERIALIZED VIEW [IF NOT EXISTS] [db.]table_name [ON CLUSTER] [TO[db.]name] 
[ENGINE = engine] [POPULATE] AS SELECT ...
  1. 创建物化视图有两种方式:直接创建,即创建物化视图时不带TO [db].[table],且必须指定ENGINE用于存储数据的表引擎,和建表类似,这种方法ClickHouse会创建一个隐藏的目标表来保存视图数据,可以通过SHOW TABLES查看;间接创建,即使用TO [db].[table]创建物化视图,[db].[table]必须是一张已经存在的表,用来保存视图数据,此时创建物化视图相当于在表上面附加了一个物化视图。需要注意,间接创建不能使用POPULATE关键字。

  2. 物化视图存储由CREATE语句中的SELECT查询转换的数据,SELECT语句可以包含DISTINCT、 GROUP BY、ORDER BY、LIMIT,如果是聚合操作,建议使用SummingMergeTree等引擎表。

  3. POPULATE关键字定义了物化视图的初始更新策略,如果指定了POPULATE,则在创建视图时将SELECT表的存量数据插入视图,就像执行 CREATE TABLE … AS SELECT … 。否则,视图只更新创建视图以后插入到表中的数据。需要注意的是如果定义了POPULATE关键字,在创建视图期间插入表中的数据不会被插入到表中,所以不建议在有数据更新的情况下使用POPULATE。如果一定要同步历史数据,则可以选择没有业务数据更新的窗口期执行。

  4. 物化视图只同步插入的数据,对源表数据的任何更改(如更新、删除、删除分区等)都不会更改物化视图数据。

  5. 物化视图使用ALTER语句有一些局限,如果创建视图时使用的是TO [db.]name方式,则可以使用DETACH 语句卸载视图,然后执行ALTER语句,再通过ATTACH 语句加载前面卸载的视图。

  6. 删除视图可使用 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)

Time-to-live

重复数据消除策略

Deduplication Strategies

词典 (Dictionaries)

Dictionaries

轻量级删除 (Lightweight Delete)

Lightweight Delete

Transactional (ACID) support

Transactional (ACID) support

需要注意的场景

  1. 没有完整的事务支持。
  2. 缺少高频率,低延迟的修改或删除已存在数据的能力。
  3. 稀疏索引使得ClickHouse不适合通过其键检索单行的点查询。

资料