1. 什么是 ClickHouse
是 俄罗斯搜索巨头 公司开源的一个极具"战斗力"的实时数据分析数据库,是面向 OLAP 的分布式列式DBMS,圈内人戏称为“喀秋莎数据库”。有一个简称 ,与Hadoop、Spark这些巨无霸组件相比,ClickHouse很轻量级。
在的面向行的DBMS中,数据按以下顺序存储:
换句话说,与一行相关的所有值都物理存储在相邻的位置。面向行的 DBMS 的示例是 MySQL,Postgres 和 MS SQL Server。在面向列的DBMS中,数据存储如下:
这些示例仅显示数据的排列顺序。不同列的值分别存储,同一列的数据一起存储。面向列的 DBMS 的示例有:,(Actian Matrix和Amazon Redshift),,,,,(VectorWise和Actian Vector),,,,,和。
不同的存储顺序适合于不同的应用场景。数据访问场景是指执行什么样查询,查询频率,每种类型的查询读取多少数据 - 行,列和字节;读取和更新数据之间的关系;数据的大小以及它在本地的使用情况;是否使用事务,以及它们是如何隔离的;数据副本和逻辑完整性;每种类型的查询的延迟和吞吐量等。
系统上的负载越高,场景化的系统定制就越重要,定制化就越具体。没有一个系统能够适用于不同的应用场景。如果一个系统可以适应多种场景,那么在高负载情况下,系统处理所有场景表现都会很差,或者仅其中一种场景表现良好。
对于OLAP(联机分析处理)场景的主要特点:
- 绝大多数请求都是读请求。
- 更新时每次更新相当大的批次(> 1000行),而不是更新一行;或者根本不更新。
- 数据添加到数据库后基本不怎么修改。
- 对于读请求,会从数据库中提取大量的数据,但只读取一小部分列(一个小的子集)。
- 表意味着它包含很多列。
- 查询相对较少(每台服务器通常只有数百个查询或更少)。
- 对于简单查询,允许大约50ms的延迟。
- 列值比较小 - 数字和短字符串(例如,每个URL 60个字节)。
- 处理单个查询时需要高吞吐量(每台服务器每秒高达数十亿行)。
- 不需要事务。
- 对数据一致性要求低
- 每个查询都有一个大表,其他所有表都是小表。
- 查询结果显著小于源数据。也就是说,数据被过滤或聚合。结果可以放在单个服务器的内存中。
很容易看出,OLAP场景与其他常见场景(如OLTP或Key-Value访问)有很大不同。所以,如果你想获得不错的表现,尝试使用OLTP或Key-Value数据库来处理分析查询是没有任何意义的。例如,如果你尝试使用 MongoDB 或 Redis 进行分析,与OLAP数据库相比,你的性能会很差。
2. 为什么面向列的数据库更适合 OLAP 场景
面向列的数据库更适合于OLAP场景:对于大多数查询,处理速度至少提高了100倍。原因在下面详细解释,但事实上更容易在视觉上展示:
面向行的数据库:
面向列的数据库:
看到不同了?
2.1 输入/输出
- 对于分析查询,只需要读取少量的列。在面向列的数据库中,你可以只读取所需的数据。例如,如果你需要100列中的5列数据,则I/O可能会减少20倍。
- 由于数据是以数据包的形式读取的,因此压缩比较容易。列中的数据也更容易压缩。这进一步减少了I/O量。
- 由于I/O的减少,更多的数据可以装载进系统缓存中。
例如,查询’统计每个广告平台的记录数量’需要读取一个’广告平台ID’列,未压缩时占用1个字节。如果大多数流量不是来自广告平台,那么你可以期望至少有10倍的压缩比。当使用快速压缩算法时,数据解压缩速度可以达到每秒解压缩至少几千兆字节的未压缩数据。换句话说,这个查询可以在一台服务器上以每秒大约几十亿行的速度处理。这个速度实际上是在实践中是容易实现的。
Example:
输出:
看一下花费的时间:
2.2 CPU
由于执行查询需要处理大量的行,因此它有助于为整个向量而不是单独的行指调度所有操作,或者实现查询引擎,以便没有调度成本。如果你不这样做,任何半象限的磁盘子系统,查询解释器不可避免地中断CPU。将数据存储在列中并在可能的情况下按列处理是有意义的。
有两种方法可以做到这一点:
- 向量引擎。所有的操作都是以向量形式写入,而不是单独的值。这意味着你不需要频繁调用操作,调度成本可以忽略不计。操作代码包含一个优化的内部循环。
- 代码生成。为查询生成的代码具有所有的间接调用。
这不是可以在’普通’'数据库中完成的,因为运行简单查询没有任何意义。但是,也有例外。例如,MemSQL 在处理SQL查询时使用代码生成来减少延迟。(为了比较,分析性DBMS需要优化吞吐量,而不是延迟。)
请注意,为了提高CPU效率,查询语言必须是声明式的(SQL或MDX),或者至少是一个向量。查询应该只包含隐式循环,以便优化。
3. ClickHouse特性
3.1 真正面向列的DBMS
在真正面向列的 DBMS 中,没有任何多余的数据与值一起存储。如果要避免在数值旁边存储长度,必须支持定长数值。例如,十亿个 UInt8 类型的值实际上应该消费大约1GB的未压缩磁盘空间,否则这将极大地影响CPU的使用。因为解压缩的速度(CPU使用率)主要取决于未压缩的数据量,所以即使在未压缩的情况下,紧凑地存储数据(没有任何多余数据)也是非常重要的。
值得注意的是因为有些系统可以单独存储不同列的值,但由于它们针对其他场景进行了优化,因此无法有效地处理分析查询。例如,,,和 。在这些系统中,可以获得大约每秒十万行的吞吐量,但达不到每秒数亿行。
同样需要值得注意的是, 是一个数据库管理系统,而不是一个数据库。 允许在运行时创建表和数据库,加载数据和运行查询,而无需重新配置和重新启动服务器。
3.2 数据压缩
一些面向列的DBMS(和 )不使用数据压缩。但是,数据压缩确实提升了性能。
3.3 磁盘存储数据
通过主键对物理数据进行排序的数据可以为其特定值或值范围提取数据,具有低延迟,低于几十毫秒。很多面向列的 DBMS(如 和 )只能在 RAM 中工作。这种方法需要比实际实时分析需要的硬件预算更大。 设计用于常规硬盘驱动器,这意味着每GB数据存储的成本很低,但SSD和额外的RAM也可以充分使用(如果可用)。
3.4 多核上的并行处理
大型查询自然的并行化处理,获取当前服务器上可用的所有必要资源。
3.5 多服务器上的分布式处理
上面提到的列式DBMS几乎都不支持分布式查询处理。在 中,数据可以驻留在不同的分片上。每个分片都可以是用于容错的一组副本。在所有分片上并行处理查询。这对用户来说是透明的。
3.6 SQL支持
支持基于 SQL 的声明性查询语言,在许多情况下与 SQL 标准相同。支持的查询包括 ,,, 和 子句中的子查询以及标量子查询。不支持特殊的子查询和窗口函数。
3.7 矢量引擎
数据不仅按列存储,并且由向量(列的一部分)处理。这使我们可以实现高CPU效率。
3.8 实时数据更新
支持具有主键的表。为了快速执行主键范围的查询,使用合并树对数据进行升序排序。因此,可以不断将数据添加到表中。摄取新数据时不会进行锁定。
3.9 索引
通过主键对物理数据进行物理排序,提取特定值或范围的数据延迟比较低(小于几十毫秒)。
3.10 适用于在线查询
低延迟意味着可以在没有延迟的情况下处理查询,而无需提前准备答案,在加载用户界面页面的同一时间会生成。换句话说,在线处理。
3.11 支持近似计算
提供了各种性能准确度的方法:
- 聚合函数,用于近似计算不同值个数,中位数和分位数等。
- 基于一部分数据(样本)运行查询并获得近似结果。在这种情况下,从磁盘中检索的数据按比例减少。
- 为有限数量的随机 key 运行聚合(不是所有的 key)。数据按 key 分发的一定条件下,这在使用较少资源的同时提供了相当准确的结果。
3.12 数据复制和数据完整性支持
使用异步多主复制。写入任何可用副本后,数据在后台分发到所有剩余副本中。系统在不同的副本上维护相同的数据。大多数情况下故障后会自动执行恢复,在复杂情况下会半自动执行。
4. ClickHouse劣势
- 不支持事务。
- 缺乏以高速率和低延迟修改或删除已插入数据的能力。可以用批次删除和更新来清理或修改数据。
- 稀疏索引使得 不适合通过其键检索单行的单点查询。
5. ClickHouse性能
根据 的内部测试结果, 在可用于测试的同类系统中获得最好的性能(长查询的最高吞吐量和短查询的最低延迟)。你可以在单独的页面上查看测试结果。
5.1 单个大型查询的吞吐量
吞吐量可以通过每秒的行数或每秒的兆字节数来度量。如果数据放在页面缓存中,在使用现在硬件的单台服务上不太复杂的查询可以实现大约2-10 GB/s未压缩数据的处理速度(对于最简单的情况,速度可能达到30GB/s)。如果数据未放在页面缓存中,速度则取决于磁盘子系统和数据压缩比。例如,如果磁盘子系统允许以400MB/s的速度读取数据,数据压缩比为3,则速度大约为1.2GB/s。如果要以每秒处理的行数进行度量,需要将速度(以字节/秒为单位)除以查询中使用的列的总大小。例如,如果提取10个字节的列,则速度将为每秒约1亿至2亿行。
对于分布式处理,处理速度几乎呈线性增加,但前提是聚合或排序产生的行数不是太大。
5.2 处理短查询时的延迟
如果查询使用主键,不需要处理太多行(数十万),并且不使用太多列,如果数据放在页面缓存中,那么延迟会不到50毫秒(在最佳情况下为几位毫秒)。否则,延迟需要根据寻址次数计算的。如果使用旋转驱动器,对于未过载的系统,延迟由以下公式计算:
5.3 处理大量短查询时的吞吐量
在相同的条件下, 可以在一台服务器上每秒处理几百个查询(在最好的情况下最多可以达到几千个)。由于此场景不适用于分析DBMS,因此我们建议每秒最多100个查询。