ClickHouse简介
ClickHouse是Yandex提供的一个开源的列式存储数据库管理系统,多用于联机分析(OLAP)场景,可提供海量数据的存储和分析,同时利用其数据压缩和向量化引擎的特性,能提供快速的数据搜索。注意到ClickHouse是一个数据库管理系统,而不是单个数据库。
ClickHouse 特点
- 读多于写
- 大宽表,读大量行但是少量列,结果集较小 通常存在一张或是几张多列的大宽表,列数高达数百甚至数千列。对数据分析处理时,选择其中的少数几列作为维度列、其他少数几列作为指标列,然后对全表或某一个较大范围内的数据做聚合计算。这个过程会扫描大量的行数据,但是只用到了其中的少数列。而聚合计算的结果集相比于动辄数十亿的原始数据,也明显小得多
- 向量引擎 :数据不仅按列存储,而且通过向量(列的一部分)进行处理,从而可以实现较高的CPU效率。
- 实时数据更新 :ClickHouse支持具有主键的表。为了在主键范围内快速执行查询,使用合并树对数据进行增量排序。因此,可以将数据连续添加到表中。摄取新数据时不采取任何锁定。
- 数据批量写入:且数据不更新或少更新 由于数据量非常大,通常更加关注写入吞吐,要求海量数据能够尽快导入完成。一旦导入完成,历史数据往往作为存档,不会再做更新、删除操作。
- 无需事务,数据一致性要求低
- 灵活多变,不适合预先建模 分析场景下,随着业务变化要及时调整分析维度、挖掘方法,以尽快发现数据价值、更新业务指标。而数据仓库中通常存储着海量的历史数据,调整代价十分高昂。预先建模技术虽然可以在特定场景中加速计算,但是无法满足业务灵活多变的发展需求,维护成本过高
- 数据有序存储 ClickHouse支持在建表时,指定将数据按照某些列进行sort by。排序后,保证了相同sort key的数据在磁盘上连续存储,且有序摆放。在进行等值、范围查询时,where条件命中的数据都紧密存储在一个或若干个连续的Block中,而不是分散的存储在任意多个Block, 大幅减少需要IO的block数量。另外,连续IO也能够充分利用操作系统page cache的预取能力,减少page fault
- 高吞吐写入能: 能够达到50MB-200MB/s的写入吞吐能力,按照每行100Byte估算,大约相当于50W-200W条/s的写入速度
- 分布式计算 ClickHouse会自动将查询拆解为多个task下发到集群中,然后进行多机并行处理,最后把结果汇聚到一起。
- 多核并行:MySQL单条SQL是单线程的,只能跑满一个core,ClickHouse相反,ClickHouse将数据划分为多个partition,每个partition再进一步划分为多个index granularity,然后通过多个CPU核心分别处理其中的一部分来实现并行数据处理。在这种设计下,单条Query就能利用整机所有CPU。极致的并行处理能力,极大的降低了查询延时。
支持接口
ClickHouse提供了两个网络接口(为了安全起见,都可以选择将两者包装在TLS中):
在大多数情况下,建议使用适当的工具或库,而不是直接与它们进行交互。Yandex官方支持以下内容:
还有大量的第三方库可用于ClickHouse:
ClickHouse 表引擎
1. MergeTree :允许您依据主键和日期创建索引,并进行实时的数据更新操作。MergeTree 是 ClickHouse 里最为先进的表引擎
2. ReplacingMergeTree :该引擎和MergeTree的不同之处在于它会删除具有相同主键的重复项。数据的去重只会在合并的过程中出现。有一些数据可能仍未被处理。因此,ReplacingMergeTree适用于在后台清除重复的数据以节省空间,但是它不保证没有重复的数据出现。在一定程度上可以弥补clickhouse不能对数据做更新的操作
在数据过多重复场景对数据进行去重
3. SummingMergeTree: 表的数据片段时,ClickHouse 会把所有具有相同主键的行合并为一行,该行包含了被合并的行中具有数值数据类型的列的汇总值。如果主键的组合方式使得单个键值对应于大量的行,则可以显著的减少存储空间并加快数据查询的速度,对于不可加的列,会取一个最先出现的值
对某个字段长期的汇总查询场景
4. AggregatingMergeTree :该引擎继承自 MergeTree,并改变了数据片段的合并逻辑。 ClickHouse 会将相同主键的所有行(在一个数据片段内)替换为单个存储一系列聚合函数状态的行。可以使用 AggregatingMergeTree 表来做增量数据统计聚合,包括物化视图的数据聚合。引擎需使用 AggregateFunction 类型来处理所有列。如果要 按一组规则来合并减少行数,则使用 AggregatingMergeTree 是合适的。对于AggregatingMergeTree不能直接使用insert来查询写入数据。一般是用insert select。但更常用的是创建物化视图 ,做增量数据统计聚合,包括物化视图的数据聚合
5.Distributed 分布式引擎本身不存储数据, 但可以在多个服务器上进行分布式查询。 读是自动并行的。读取时,远程服务器表的索引(如果有的话)会被使用。 分布式引擎参数:服务器配置文件中的集群名,远程数据库名,远程表名,数据分片键
整合引擎
与其他数据存储和处理系统进行通信的引擎。
- kafka
- mysql
该引擎可与Apache Kafka一起使用。
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_broker_list–以逗号分隔的经纪人列表(例如,localhost:9092)。
- kafka_topic_list – Kafka主题列表。
- kafka_group_name–一群Kafka消费者。分别跟踪每个组的阅读边距。如果您不想在群集中复制邮件,请在各处使用相同的组名。
- kafka_format–消息格式。使用与SQL FORMAT函数相同的符号,例如JSONEachRow。有关更多信息,请参见格式部分。
可选参数:
- kafka_row_delimiter –分隔符,结束消息。
- kafka_schema–如果格式需要架构定义,则必须使用的参数。例如,Cap'n Proto需要模式文件的路径和根schema.capnp:Message对象的名称。
- kafka_num_consumers–每张桌子的消费者数量。默认值:1。如果一个使用者的吞吐量不足,请指定更多使用者。使用者的总数不应超过该主题中的分区数,因为每个分区只能分配一个使用者。
- kafka_max_block_size-轮询的最大批处理大小(以消息为单位)(默认值:)max_block_size。
- kafka_skip_broken_messages– Kafka消息解析器对每个块的架构不兼容消息的容忍度。默认值:0。如果是,kafka_skip_broken_messages = N则引擎会跳过N条无法解析的Kafka消息(一条消息等于一行数据)。
- kafka_commit_every_batch-写入整个块后,提交每个消耗和处理的批次,而不是单个提交(默认值:)0。
数据有序存储
ClickHouse支持在建表时,指定将数据按照某些列进行sort by。排序后,保证了相同sort key的数据在磁盘上连续存储,且有序摆放。在进行等值、范围查询时,where条件命中的数据都紧密存储在一个或若干个连续的Block中,而不是分散的存储在任意多个Block, 大幅减少需要IO的block数量。另外,连续IO也能够充分利用操作系统page cache的预取能力,减少page fault。
主键索引
ClickHouse支持主键索引,它将每列数据按照index granularity(默认8192行)进行划分,每个index granularity的开头第一行被称为一个mark行。主键索引存储该mark行对应的primary key的值。
对于where条件中含有primary key的查询,通过对主键索引进行二分查找,能够直接定位到对应的index granularity,避免了全表扫描从而加速查询。但是值得注意的是:ClickHouse的主键索引与MySQL等数据库不同,它并不用于去重,即便primary key相同的行,也可以同时存在于数据库中。要想实现去重效果,需要结合具体的表引擎ReplacingMergeTree、CollapsingMergeTreeVersionedCollapsingMergeTree实现。
稀疏索引
ClickHouse支持对任意列创建任意数量的稀疏索引。其中被索引的value可以是任意的合法SQL Expression,并不仅仅局限于对column value本身进行索引。之所以叫稀疏索引,是因为它本质上是对一个完整index granularity(默认8192行)的统计信息,并不会具体记录每一行在文件中的位置。目前支持的稀疏索引类型包括:
- minmax: 以index granularity为单位,存储指定表达式计算后的min、max值;在等值和范围查询中能够帮助快速跳过不满足要求的块,减少IO。
- set(max_rows):以index granularity为单位,存储指定表达式的distinct value集合,用于快速判断等值查询是否命中该块,减少IO。
- ngrambf_v1(n, size_of_bloom_filter_in_bytes, number_of_hash_functions, random_seed):将string进行ngram分词后,构建bloom filter,能够优化等值、like、in等查询条件。
- tokenbf_v1(size_of_bloom_filter_in_bytes, number_of_hash_functions, random_seed): 与ngrambf_v1类似,区别是不使用ngram进行分词,而是通过标点符号进行词语分割。
- bloom_filter([false_positive]):对指定列构建bloom filter,用于加速等值、like、in等查询条件的执行。
数据Sharding
ClickHouse支持单机模式,也支持分布式集群模式。在分布式模式下,ClickHouse会将数据分为多个分片,并且分布到不同节点上。不同的分片策略在应对不同的SQL Pattern时,各有优势。ClickHouse提供了丰富的sharding策略,让业务可以根据实际需求选用。
1) random随机分片:写入数据会被随机分发到分布式集群中的某个节点上。
2) constant固定分片:写入数据会被分发到固定一个节点上。
3)column value分片:按照某一列的值进行hash分片。
4)自定义表达式分片:指定任意合法表达式,根据表达式被计算后的值进行hash分片。
数据分片,让ClickHouse可以充分利用整个集群的大规模并行计算能力,快速返回查询结果。
更重要的是,多样化的分片功能,为业务优化打开了想象空间。比如在hash sharding的情况下,JOIN计算能够避免数据shuffle,直接在本地进行local join; 支持自定义sharding,可以为不同业务和SQL Pattern定制最适合的分片策略;利用自定义sharding功能,通过设置合理的sharding expression可以解决分片间数据倾斜问题等。
另外,sharding机制使得ClickHouse可以横向线性拓展,构建大规模分布式集群,从而具备处理海量数据的能力。
数据Partitioning
ClickHouse支持PARTITION BY子句,在建表时可以指定按照任意合法表达式进行数据分区操作,比如通过toYYYYMM()将数据按月进行分区、toMonday()将数据按照周几进行分区、对Enum类型的列直接每种取值作为一个分区等。
数据Partition在ClickHouse中主要有两方面应用:
- 在partition key上进行分区裁剪,只查询必要的数据。灵活的partition expression设置,使得可以根据SQL Pattern进行分区设置,最大化的贴合业务特点。
- 对partition进行TTL管理,淘汰过期的分区数据。
数据TTL
在分析场景中,数据的价值随着时间流逝而不断降低,多数业务出于成本考虑只会保留最近几个月的数据,ClickHouse通过TTL提供了数据生命周期管理的能力。
ClickHouse支持几种不同粒度的TTL:
1) 列级别TTL:当一列中的部分数据过期后,会被替换成默认值;当全列数据都过期后,会删除该列。
2)行级别TTL:当某一行过期后,会直接删除该行。
3)分区级别TTL:当分区过期后,会直接删除该分区。
高吞吐写入能力
ClickHouse采用类LSM Tree的结构,数据写入后定期在后台Compaction。通过类LSM tree的结构,ClickHouse在数据导入时全部是顺序append写,写入后数据段不可更改,在后台compaction时也是多个段merge sort后顺序写回磁盘。顺序写的特性,充分利用了磁盘的吞吐能力,即便在HDD上也有着优异的写入性能。建议每次写入不少于1000行的批量写入,或每秒不超过一个写入请求。当使用tab-separated格式将一份数据写入到 MergeTree表中时,写入速度大约为50到200MB/s。如果您写入的数据每行为1Kb,那么写入的速度为50,000到 200,000行每秒。如果您的行更小,那么写入速度将更高。为了提高写入性能,您可以使用多个INSERT进行并行写入,这 将带来线性的性能提升。
有限支持delete、update
在分析场景中,删除、更新操作并不是核心需求。ClickHouse没有直接支持delete、update操作,而是变相支持了mutation操作,语法为alter table delete where filter_expr
,alter table update col=val where filter_expr
。
目前主要限制为删除、更新操作为异步操作,需要后台compation之后才能生效。
主备同步
ClickHouse通过主备复制提供了高可用能力,主备架构下支持无缝升级等运维操作。而且相比于其他系统它的实现有着自己的特色:
1)默认配置下,任何副本都处于active模式,可以对外提供查询服务;
2)可以任意配置副本个数,副本数量可以从0个到任意多个;
3)不同shard可以配置不提供副本个数,用于解决单个shard的查询热点问题
Clickhouse 底层存储原理
一 数据目录
Data目录 :数据存储目录,数据按照part分成多个文件夹,每个文件夹下存储相应数据和对应的元信息文件
Metadata :表定义语句,存储所有表的建表语句
二 基本原理
记录方式:每隔8192行数据,是1个block,主键会每隔8192,取一行主键列的数据,同时记录这是第几个block
查找过程:如果有索引,就通过索引定位到是哪个block,然后找到这个block对应的mrk文件,mrk文件里记录的是某个block的数据集,在整列bin文件的哪个物理偏移位,加载数据到内存,之后并行化过滤
全主键 如 where x='3' and y='c'
1. 判断,只需要扫描block2,3(定位block)
2. 使用mrk文件,定位数据的偏移量
3. 加载到内存过滤并返回
非主键
where z='m'
等效于 where x = any and y = any and z = 'm'
全表扫描