简介
ClickHouse是一个用于联机分析(OLAP)的列式数据库管理系统(DBMS)。
列式数据库总是将同一列的数据存储在一起,不同列的数据也总是分开存储。
支持的特性
- 真正的列式数据库管理系
- 数据压缩
- 数据的磁盘存储
- 多核心并行处理
- 多服务器分布式处理:数据可以保存在不同的shard上,每一个shard都由一组用于容错的replica组成,查询可以并行的在所有shard上进行处理。
- 支持SQL
- 向量引擎: 为了高效的使用CPU,数据不仅仅按列存储,同时还按向量(列的一部分)进行处理。
- 实时的数据更新
- 索引
- 适合在线查询
- 支持近似计算
- 支持数据复制和数据完整性: ClickHouse使用异步的多主复制技术。当数据被写入任何一个可用副本后,系统会在后台将数据分发给其他副本,以保证系统在不同副本上保持相同的数据。在大多数情况下ClickHouse能在故障后自动恢复,在一些复杂的情况下需要少量的手动恢复。
不支持的特性
- 没有完整的事物支持。
- 缺少高频率,低延迟的修改或删除已存在数据的能力。
- 稀疏索引使得ClickHouse不适合通过其键检索单行的点查询。
性能
- 读性能: 如果数据被放置在page cache中,则一个不太复杂的查询在单个服务器上大约能够以2-10GB/s(未压缩)的速度进行处理(对于简单的查询,速度可以达到30GB/s)。如果数据没有在page cache中的话,那么速度将取决于你的磁盘系统和数据的压缩率。例如,如果一个磁盘允许以x MB/s的速度读取数据,并且数据压缩率是y,则数据的处理速度为x * y MB/s。
- 延迟: 在数据被page cache缓存的情况下,一个查询的延迟应该小于50毫秒(在最佳的情况下应该小于10毫秒)。 否则,延迟取决于数据的查找次数。查询所需要的延迟可以通过以下公式计算得知: 查找时间(10 ms) * 查询的列的数量 * 查询的数据块的数量。
- 写性能:我们建议每次写入不少于1000行的批量写入,或每秒不超过一个写入请求。当使用tab-separated格式将一份数据写入到MergeTree表中时,写入速度大约为50到200MB/s。为了提高写入性能,可以使用多个INSERT进行并行写入,这将带来线性的性能提升。
架构
ClickHouse是一个完全面向列式的分布式数据库。数据通过列存储,在查询过程中,数据通过数组来处理(向量或者列Chunk)。当进行查询时,操作被转发到数组上,而不是在特定的值上。因此被称为”向量化查询执行”,相对于实际的数据处理成本,向量化处理具有更低的转发成本。
- Columns(列):为了表示内存中的列(列的 chunks),IColumn将被使用。这个接口提供了一些辅助方法来实现不同的关系操作符。几乎所有的操作符都是非更改的:他们不能更改原有的列,但是创建一个新的更新的列。不同的IColumn实现(ColumnUInt8,ColumnString等)负责列的内存布局。内存布局通常是一个连续的数组。
- Data Types(数据类型):IDataType 负责序列化和反序列化: 读写这个列的值或者以二进制或文本的方式的值.IDataType 直接与表中的数据类型一致。IDataType 仅存储元数据。
- Block(数据块):一个数据块是一个容器,代表了内存中一个表的子集。它也是三元组的集合:(IColumn,IDataType,columnname). 在查询执行过程中, 数据通过数据块来处理. 如果你有一个数据块, 我们有数据(在IColumn对象中), 我们有这个数据的类型(在IDataType中) 告诉我们怎样处理此列,同时我们有此列名称。
- Block Streams(数据块流):数据块流用于处理数据。我们使用数据块的数据流从某处读取数据,执行数据转换或者写入数据到某处。IBlockInputStream 有一个read方法获取下一个数据块。IBlockOutputStream 有一个write方法发送数据块到某处。
- I/O:对于面向字节的输入/输出。有 ReadBuffer 和 WriteBuffer 抽象类。
- Tables:表通过IStorage接口来表示。对此接口不同的实现成为不同的表引擎。最重要的IStorage 方法是读和写操。
- Functions:普通函数并不能改变行的数量 – 他们单独处理每个行。事实上,对于每个行,函数不能被调用,但是对于数据块的数据可实现向量化查询执行。
- Aggregate Functions:聚合函数是状态函数。 他们积累传递的值到某个状态, 允许你从这个状态获得结果。他们用IAggregateFunction来管理。
表引擎
对于大多数正式的任务,应该使用MergeTree族中的引擎。
Clickhouse 中最强大的表引擎当属 MergeTree (合并树)引擎及该系列(*MergeTree)中的其他引擎。
TinyLog
最简单的表引擎,用于将数据存储在磁盘上。每列都存储在单独的压缩文件中。写入时,数据将附加到文件末尾。不支持索引。
并发数据访问不受任何限制: - 如果同时从表中读取并在不同的查询中写入,则读取操作将抛出异常 - 如果同时写入多个查询中的表,则数据将被破坏。
这种表引擎的典型用法是 write-once:首先只写入一次数据,然后根据需要多次读取。查询在单个流中执行。换句话说,此引擎适用于相对较小的表(建议最多1,000,000行)。
Memory
Memory 引擎以未压缩的形式将数据存储在内存中。数据完全以读取时获得的形式存储。换句话说,从这张表中读取是很轻松的。并发数据访问是同步的。锁范围小:读写操作不会相互阻塞。不支持索引。阅读是并行化的。在简单查询上达到最大生产率(超过10 GB /秒),因为没有磁盘读取,不需要解压缩或反序列化数据。(值得注意的是,在许多情况下,与 MergeTree 引擎的性能几乎一样高)。重新启动服务器时,表中的数据消失,表将变为空。通常,使用此表引擎是不合理的。
Merge
Merge 引擎本身不存储数据,但可用于同时从任意多个其他的表中读取数据。 读是自动并行的,不支持写入。读取时,那些被真正读取到数据的表的索引(如果有的话)会被使用。 Merge 引擎的参数:一个数据库名和一个用于匹配表名的正则表达式。
Distributed
分布式引擎本身不存储数据, 但可以在多个服务器上进行分布式查询。 读是自动并行的。读取时,远程服务器表的索引(如果有的话)会被使用。 分布式引擎参数:服务器配置文件中的集群名,远程数据库名,远程表名,数据分片键(可选)。远程服务器不仅用于读取数据,还会对尽可能数据做部分处理。分片是指包含数据不同部分的服务器(要读取所有数据,必须访问所有分片)。 副本是存储复制数据的服务器(要读取所有数据,访问任一副本上的数据即可)。配置了副本,读取操作会从每个分片里选择一个可用的副本。可配置负载平衡算法(挑选副本的方式)。 如果跟服务器的连接不可用,则在尝试短超时的重连。如果重连失败,则选择下一个副本,依此类推。如果跟所有副本的连接尝试都失败,则尝试用相同的方式再重复几次。 该机制有利于系统可用性,但不保证完全容错:如有远程服务器能够接受连接,但无法正常工作或状况不佳。
Null
当写入 Null 类型的表时,将忽略数据。从 Null 类型的表中读取时,返回空。
但是,可以在 Null 类型的表上创建物化视图。写入表的数据将转发到视图中。
MergeTree
MergeTree 引擎系列的基本理念如下:当有巨量数据要插入到表中时,需要高效地一批批写入数据片段,并希望这些数据片段在后台按照一定规则合并。
特点
- 存储的数据按主键排序。
- 允许使用分区(在指定了主键的情况下)。查询中指定了分区键时 ClickHouse 会自动截取分区数据。这也有效增加了查询性能。
- 支持数据副本。
- 支持数据采样。
存储
表由按主键排序的数据片段组成。
当数据被插入到表中时,会分成数据片段并按主键的字典序排序。
不同分区的数据会被分成不同的片段,ClickHouse 在后台合并数据片段以便更高效存储。不会合并来自不同分区的数据片段。这个合并机制并不保证相同主键的所有行都会合并到同一个数据片段中。
主键与排序键
- ClickHouse 不要求主键惟一。所以,你可以插入多条具有相同主键的行。
- 主键中列的数量并没有明确的限制。
- 长的主键会对插入性能和内存消耗有负面影响。
- 默认情况下主键跟排序键相同。指定一个跟排序键(用于排序数据片段中行的表达式) 不一样的主键(用于计算写到索引文件的每个标记值的表达式)是可以的。 这种情况下,主键表达式元组必须是排序键表达式元组的一个前缀。
网友评论