Skip to main content

ClickHouse是什么?

ClickHouse® 是一款面向列的高性能 SQL 数据库管理系统(DBMS),用于在线分析处理(OLAP)。它可作为开源软件云服务提供。

什么是OLAP?

OLAP 场景需要在庞大数据集上进行实时响应,用于复杂的分析查询,具有以下特点:

  • 数据集可能非常庞大 - 亿万或万亿行
  • 数据组织成包含许多列的表
  • 每个特定查询只选择少数列
  • 结果必须在毫秒或秒内返回

列式与行式数据库

在行式DBMS中,数据按行存储,与行相关的所有值物理上相邻存储。

在列式DBMS中,数据按列存储,相同列的值存储在一起。

为什么列式数据库在OLAP场景中更适用

列式数据库更适合OLAP场景:它们在处理大多数查询时至少快100倍。下面详细解释了原因,但事实更容易通过图示来展示:

行式DBMS

行式

列式DBMS

列式

看到区别了吗?

本文的其余部分解释了为什么列式数据库适用于这些场景,以及为什么ClickHouse特别优于其他同类产品。

为什么ClickHouse如此快?

ClickHouse充分利用所有可用的系统资源,以尽可能快的速度处理每个分析查询。这得益于分析能力和对实现最快OLAP数据库所需的低级细节的独特结合。

深入了解此主题的有用文章包括:

在实时处理分析查询

在行式DBMS中,数据按以下顺序存储:

RowWatchIDJavaEnableTitleGoodEventEventTime
#0893543506621Investor Relations12016-05-18 05:19:20
#1903295099580Contact us12016-05-18 08:10:20
#2899537060541Mission12016-05-18 07:38:00
#N

换句话说,与行相关的所有值物理上相邻存储。

行式DBMS的例子包括MySQL、Postgres和MS SQL Server。

在列式DBMS中,数据存储如下:

Row:#0#1#2#N
WatchID:893543506629032950995889953706054
JavaEnable:101
Title:Investor RelationsContact usMission
GoodEvent:111
EventTime:2016-05-18 05:19:202016-05-18 08:10:202016-05-18 07:38:00

这些例子只显示了数据排列的顺序。不同列的值是分开存储的,相同列的数据是一起存储的。

列式DBMS的例子包括Vertica、Paraccel(Actian Matrix和Amazon Redshift)、Sybase IQ、Exasol、Infobright、InfiniDB、MonetDB(VectorWise和Actian Vector)、LucidDB、SAP HANA、Google Dremel、Google PowerDrill、Druid和kdb+。

不同的数据存储顺序适用于不同的场景。数据访问场景指的是进行了哪些查询、查询频率和比例、每种查询读取多少数据(行、列和字节)、读取和更新数据之间的关系、数据的工作大小以及本地使用情况、是否使用事务以及它们的隔离程度、数据复制和逻辑完整性的要求、每种查询的延迟和吞吐量要求等。

系统的负载越高,就越重要地根据使用场景的要求定制系统设置,这种定制变得越来越精细。没有一个系统能够完全适用于显著不同的场景。如果一个系统能够适应广泛的场景,那么在高负载下,系统将同样糟糕地处理所有场景,或者只能很好地处理一种或几种可能的场景。

OLAP场景的关键特性

  • 表是“宽”的,意味着它们包含大量列。
  • 数据集很大,处理单个查询时需要高吞吐量(每个服务器每秒高达数十亿行)。
  • 列值相当小:数字和短字符串(例如,每个URL 60字节)。
  • 查询提取大量行,但只是列的一个小子集。
  • 对于简单查询,允许的延迟约为50毫秒。
  • 每个查询有一个大表;除了一个表外,所有表都很小。
  • 查询结果比源数据显著小。换句话说,数据被过滤或聚合,因此结果适合单个服务器的RAM。
  • 查询相对较少(通常每秒每个服务器或更少的查询数百次)。
  • 插入以相当大的批量(> 1000行)进行,而不是单行。
  • 事务是不必要的。

很容易看出,OLAP场景与其他流行的场景(如OLTP或键值访问)非常不同。因此,如果要获得良好的性能,尝试使用OLTP或键值DB来处理分析查询是没有意义的。例如,如果尝试使用MongoDB或Redis进行分析,与OLAP数据库相比,性能会非常差。

输入/输出

  1. 对于分析查询,只需要读取少量表列。在列式数据库中,您可以只读取所需的数据。例如,如果您需要100列中的5列,您可以期望I/O减少20倍。
  2. 由于数据是以数据包的形式读取的,因此更容易压缩。列中的数据也更容易压缩。这进一步减少了I/O量。
  3. 由于I/O减少,更多数据适合系统缓存。

CPU

由于执行查询需要处理大量行,因此有助于将所有操作分派给整个向量,而不是单独的行,或者实现查询引擎,以便几乎没有分派成本。如果不这样做,那么在任何半好的磁盘子系统中,查询解释器都会不可避免地使CPU停滞。在可能的情况下,存储数据并按列处理数据是有意义的。

有两种方法可以做到这一点:

  1. 向量引擎。所有操作都是针对向量而不是单独的值编写的。这意味着您不需要经常调用操作,分派成本可以忽略不计。操作代码包含优化的内部循环。
  2. 代码生成。为查询生成的代码中包含所有间接调用。

这在行式数据库中没有做,因为在运行简单查询时没有意义。但是,也有例外。例如,MemSQL使用代码生成来减少处理SQL查询时的延迟。(作为比较,分析DBMS需要优化吞吐量,而不是延迟。)

请注意,为了提高CPU效率,查询语言必须是声明性的(SQL或MDX),或者至少是向量(J、K)。查询应该只包含隐式循环,以便进行优化。