ClickHouse
是2016
年开源的列式存储数据库(DBMS),使用C++
语言编写,主要用于在线分析处理查询OLAP
,能够使用SQL
查询实时生成分析数据报告。
一、列式存储
以下面的Tabel
为例
ID | Name | Gender |
---|---|---|
1 | 吴彦祖 | 男 |
2 | 刘亦菲 | 女 |
3 | 陈冠希 | 男 |
【1】采用行式存储时,数据在磁盘上的组织结构为:优点是获取某人所有的属性时,可以通过一次磁盘查找加顺序读取就可以。但需要查所有人的姓名时,遍历的很多不需要的数据,甚至需要全表扫描才行。
1 | 吴彦祖 | 男 | 2 | 刘亦菲 | 女 | 3 | 陈冠希 | 男 |
---|
【2】采用列式存储时,数据在磁盘上的组织结构为:查询所有人的姓名时只需把姓名那一列拿出来就可以了
1 | 2 | 3 | 吴彦祖 | 刘亦菲 | 陈冠希 | 男 | 女 | 男 |
---|
列式储存的优点:
【1】对于列的聚合,计数,求和等统计操作优于行式存储。
【2】由于某一列的数据类型都是相同的,针对于数据存储更容易进行数据压缩,每一列选择更优的数据压缩算法,大大提高了数据的压缩比重。
【3】由于数据压缩比更好,一方面节省了磁盘空间,另一方面对于cache
也有了更大的发挥空间。
二、表引擎
表引擎是ClickHouse
的一大特色。可以说,表引擎决定了如何存储表的数据。表引擎的使用方式就是必须显式在创建表时定义该表使用的引擎,以及引擎使用的相关参数。特别注意:引擎的名称大小写敏感
MergeTree
ClickHouse
中最强大的表引擎当属MergeTree
合并树引擎及该系列*MergeTree
中的其他引擎,支持索引和分区,地位可以相当于Innodb
之于Mysql
。而且基于MergeTree
还衍生除了很多子产品,也是非常有特色的引擎。
建表语句: MergeTree
其实还有很多参数(绝大多数用默认值即可),但是三个参数是更加重要的,也涉及了关于MergeTree
的很多概念。
create table t_order_mt(
id UInt32,
sku_id String,
total_amount Decimal(16,2),
create_time Datetime
) engine =MergeTree
partition by toYYYYMMDD(create_time)
primary key (id)
order by (id,sku_id);
插入数据
insert into t_order_mt values
(101,'sku_001',1000.00,'2020-06-01 12:00:00')
partition by
分区(可选): 学过hive
的应该都不陌生,分区的目的主要是降低扫描的范围,优化查询速度。如果不填,只会有一个分区。分区目录: MergeTree
是以列文件+索引文件+表定义文件组成的,但是如果设定了分区那么这些文件就会保存到不同的分区目录中。并行: 分区后,面对涉及跨分区的查询统计,ClickHouse
会以分区为单位并行处理。
数据写入与分区合并:任何一个批次的数据写入都会产生一个临时分区,不会纳入任何一个已有的分区。写入后的某个时刻(大概 10-15 分钟后),ClickHouse
会自动执行合并操作(等不及也可以手动通过optimize
执行),把临时分区的数据,合并到已有分区中。
optimize table xxxx final;
例如: 再次执行上面的插入操作
insert into t_order_mt values
(101,'sku_001',1000.00,'2020-06-01 12:00:00') ,
(102,'sku_002',2000.00,'2020-06-01 11:00:00'),
(102,'sku_004',2500.00,'2020-06-01 12:00:00'),
(102,'sku_002',2000.00,'2020-06-01 13:00:00'),
(102,'sku_002',12000.00,'2020-06-01 13:00:00'),
(102,'sku_002',600.00,'2020-06-02 12:00:00');
查看数据并没有纳入任何分区
手动optimize
之后,再次查询
hadoop102 :) optimize table t_order_mt final;
primary key
主键(可选):ClickHouse
中的主键,和其他数据库不太一样,它只提供了数据的一级索引,但是却不是唯一约束。这就意味着是可以存在相同primary key
的数据的。主键的设定主要依据是查询语句中的where
条件。根据条件通过对主键进行某种形式的二分查找,能够定位到对应的 index granularity
,避免了全表扫描。index granularity
:直接翻译的话就是索引粒度,指在稀疏索引中两个相邻索引对应数据的间隔。ClickHouse
中的MergeTree
默认是8192
。官方不建议修改这个值,除非该列存在大量重复值,比如在一个分区中几万行才有一个不同数据。
稀疏索引:稀疏索引的好处就是可以用很少的索引数据,定位更多的数据,代价就是只能定位到索引粒度的第一行,然后再进行进行一点扫描。
order by
(必选):order by
设定了分区内的数据按照哪些字段顺序进行有序保存。order by
是MergeTree
中唯一一个必填项,甚至比primary key
还重要,因为当用户不设置主键的情况,很多处理会依照order by
的字段进行处理(比如后面会讲的去重和汇总)。要求:主键必须是order by
字段的前缀字段。比如order by
字段是id,sku_id
那么主键必须是id
或者id,sku_id
二级索引
目前在ClickHouse
的官网上二级索引的功能在v20.1.2.4
之前是被标注为实验性的,在这个版本之后默认是开启的。
老版本使用二级索引前需要增加设置是否允许使用实验性的二级索引(v20.1.2.4
开始,这个参数已被删除,默认开启)
set allow_experimental_data_skipping_indices=1;
创建测试表: 其中GRANULARITY N
是设定二级索引对于一级索引粒度的粒度。
create table t_order_mt2(
id UInt32,
sku_id String,
total_amount Decimal(16,2),
create_time Datetime,
INDEX a total_amount TYPE minmax GRANULARITY 5
) engine =MergeTree
partition by toYYYYMMDD(create_time)
primary key (id)
order by (id, sku_id);
插入数据
insert into t_order_mt2 values
(101,'sku_001',1000.00,'2020-06-01 12:00:00') ,
(102,'sku_002',2000.00,'2020-06-01 11:00:00'),
(102,'sku_004',2500.00,'2020-06-01 12:00:00'),
(102,'sku_002',2000.00,'2020-06-01 13:00:00'),
(102,'sku_002',12000.00,'2020-06-01 13:00:00'),
(102,'sku_002',600.00,'2020-06-02 12:00:00');
对比效果: 那么在使用下面语句进行测试,可以看出二级索引能够为非主键字段的查询发挥作用。
[root lib]$ clickhouse-client --send_logs_level=trace <<< 'select * from t_order_mt2 where total_amount > toDecimal32(900., 2)';