Skip to main content
 Web开发网 » 操作系统 » linux系统

每秒可读取2亿行、写入20万行数据,深入ClickHouse的高性能

2021年10月17日12450百度已收录

大家好,我是阿迈达,有趣的互联网软件工程师。专业角度分析技术原理,幽默的态度解读科技互联网资讯。

(此处已添加圈子卡片,请到今日头条客户端查看)什么是ClickHouseClickHouse最初是为 YandexMetrica 世界第二大Web分析平台 而开发的。多年来一直作为该系统的核心组件被该系统持续使用着。目前为止,该系统在ClickHouse中有超过13万亿条记录,并且每天超过200多亿个事件被处理。它允许直接从原始数据中动态查询并生成报告。

Yandex.Metrica基于用户定义的字段,对实时访问、连接会话,生成实时的统计报表。这种需求往往需要复杂聚合方式,比如对访问用户进行去重。构建报表的数据,是实时接收存储的新数据。

截至2014年4月,Yandex.Metrica每天跟踪大约120亿个事件(用户的点击和浏览)。为了可以创建自定义的报表,我们必须存储全部这些事件。同时,这些查询可能需要在几百毫秒内扫描数百万行的数据,或在几秒内扫描数亿行的数据。

联机分析(OLAP)的列式数据库管理系统ClickHouse是一个用于联机分析(OLAP)的列式数据库管理系统(DBMS)。

在传统的行式数据库系统中,数据按如下顺序存储:

每秒可读取2亿行、写入20万行数据,深入ClickHouse的高性能  ClickHouse 第1张

处于同一行中的数据总是被物理的存储在一起。常见的行式数据库系统有: MySQL、Postgres和MS SQL Server。

在列式数据库系统中,数据按如下的顺序存储:

每秒可读取2亿行、写入20万行数据,深入ClickHouse的高性能  ClickHouse 第2张

该示例中只展示了数据在列式数据库中数据的排列顺序。对于存储而言,列式数据库总是将同一列的数据存储在一起,不同列的数据也总是分开存储。

常见的列式数据库有: 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场景的关键特征大多数是读请求数据总是以相当大的批(> 1000 rows)进行写入不修改已添加的数据每次查询都从数据库中读取大量的行,但是同时又仅需要少量的列宽表,即每个表包含着大量的列较少的查询(通常每台服务器每秒数百个查询或更少)对于简单查询,允许延迟大约50毫秒列中的数据相对较小: 数字和短字符串(例如,每个URL 60个字节)处理单个查询时需要高吞吐量(每个服务器每秒高达数十亿行)事务不是必须的对数据一致性要求低每一个查询除了一个大表外都很小查询结果明显小于源数据,换句话说,数据被过滤或聚合后能够被盛放在单台服务器的内存中很容易可以看出,OLAP场景与其他流行场景(例如,OLTP或K/V)有很大的不同, 因此想要使用OLTP或Key-Value数据库去高效的处理分析查询是没有意义的,例如,使用OLAP数据库去处理分析请求通常要优于使用MongoDB或Redis去处理分析请求。

列式数据库更适合OLAP场景的原因列式数据库更适合于OLAP场景(对于大多数查询而言,处理速度至少提高了100倍),下面详细解释了原因(通过图片更有利于直观理解):

行式

每秒可读取2亿行、写入20万行数据,深入ClickHouse的高性能  ClickHouse 第3张

列式

每秒可读取2亿行、写入20万行数据,深入ClickHouse的高性能  ClickHouse 第4张

看到差别了么?下面将详细介绍为什么会发生这种情况。

输入/输出针对分析类查询,通常只需要读取表的一小部分列。在列式数据库中你可以只读取你需要的数据。例如,如果只需要读取100列中的5列,这将帮助你最少减少20倍的I/O消耗。由于数据总是打包成批量读取的,所以压缩是非常容易的。同时数据按列分别存储这也更容易压缩。这进一步降低了I/O的体积。由于I/O的降低,这将帮助更多的数据被系统缓存。例如,查询«统计每个广告平台的记录数量»需要读取«广告平台ID»这一列,它在未压缩的情况下需要1个字节进行存储。如果大部分流量不是来自广告平台,那么这一列至少可以以十倍的压缩率被压缩。当采用快速压缩算法,它的解压速度最少在十亿字节(未压缩数据)每秒。换句话说,这个查询可以在单个服务器上以每秒大约几十亿行的速度进行处理。这实际上是当前实现的速度。

CPU由于执行一个查询需要处理大量的行,因此在整个向量上执行所有操作将比在每一行上执行所有操作更加高效。同时这将有助于实现一个几乎没有调用成本的查询引擎。如果你不这样做,使用任何一个机械硬盘,查询引擎都不可避免的停止CPU进行等待。所以,在数据按列存储并且按列执行是很有意义的。

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

向量引擎:所有的操作都是为向量而不是为单个值编写的。这意味着多个操作之间的不再需要频繁的调用,并且调用的成本基本可以忽略不计。操作代码包含一个优化的内部循环。代码生成:生成一段代码,包含查询中的所有操作。这是不应该在一个通用数据库中实现的,因为这在运行简单查询时是没有意义的。但是也有例外,例如,MemSQL使用代码生成来减少处理SQL查询的延迟(只是为了比较,分析型数据库通常需要优化的是吞吐而不是延迟)。

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

评论列表暂无评论
发表评论
微信