导读 ClickHouse 以其卓越的实时数据分析能力在金融分析、网络市场分析以及用户行为分析等前沿领域崭露头角,广受行业认可和应用。本文将深入探讨阿里云企业版 ClickHouse 服务如何为用户带来云端便利与高效体验。通过实时动态调整集群规模和秒级资源扩展等云特性,用户能够根据系统负载自动优化查询,显著提升查询速度,同时降低硬件资源消耗。此外还将介绍ClickHouse 在云原生架构下的最新改进,特别是 Keeper 和 Shared Merge Tree 等先进技术的引入,这些不仅增强了 ClickHouse 在云平台服务中的稳定性和高效性,更为用户提供了更加灵活、可靠的数据处理体验。
今天的介绍会围绕下面两点展开:1. ClickHouse 简介
2. ClickHouse 企业版技术解析
分享嘉宾|Auxten ClickHouse director of software development, ClickHouse Inc.
编辑整理|余凡
内容校对|李瑶
出品社区|DataFun
ClickHouse 简介
ClickHouse 在多个行业中展现出了广泛的适用性。无论是构建灵活可扩展的 SaaS 服务,还是打造秒级响应的信贷实时数据仓库,亦或作为可观测性平台的坚实后盾,它都能提供可靠的数据支持。最新兴的 AI 应用同样借助 ClickHouse 提供数据支撑,展现了其在多种使用场景下的卓越性能。
在全球范围内,各行各业的领军企业都纷纷选择采用 ClickHouse。
无论是在金融分析、网络市场分析,还是用户行为分析、商业分析和可观性分析,甚至是网络安全监控和 IoT 领域的网络流量分析领域,ClickHouse 都得到了广泛的应用和认可。卓越的性能和灵活的适应性,使其成为了各行各业数据处理的强大工具。
ClickHouse 拥有一些独特的优势,作为实时分析数据库行业的领导者,它始终能够提供高效的数据分析和导入功能。
首先,ClickHouse 支持轻量级且极为快速的查询。无论是针对海量数据的查询聚合,还是进行复杂的自定义计算,ClickHouse 都能迅速完成。上图右侧展示了一些 Benchmark 数据,在 ClickHouse 官方网站上也可以看到,与其他方案相比,其性能优势一目了然。其次,ClickHouse 通过采用先进的数据压缩技术,实现了存储资源的最大化利用。与其他替代方案相比,ClickHouse 能够平均提高存储效率十到一百倍,从而显著降低了企业的存储成本。此外,ClickHouse 的易用性也是其一大亮点。它提供了丰富的数据查询接口,无论数据存储在常见的 S3 中,还是 Snowflake、Hudi 等数据源中,ClickHouse 都能通过表引擎的抽象进行自助式的数据引入和直接查询。同时,ClickHouse 还支持标准的 SQL 语法,这使得用户能够轻松上手,无需复杂的培训和学习。
ClickHouse Cloud 相较于自建 ClickHouse 具有以下显著优势:- Flexible。用户无需投入管理成本,即可享受全托管的数据库服务,降低了维护的复杂性,用户还能够专注于业务的核心需求。
- Feature-rich。拥有广泛的生态系统支持,能够与各种大数据工具和数据库无缝集成,提供更加灵活和高效的数据处理体验。
- Easy
to Use。ClickHouse Cloud 提供了分钟级的快速服务部署。可以在平台上轻松试用,并自助优化性能和使用体验,无需等待冗长的部署时间。
- Scalable。支持快速自动扩展,以满足不断增长的业务需求。
- Reliable。提供了高可靠性保障,确保您的数据始终安全可用。
- PAYG。采用基于实际资源使用的计费模式。这意味着只需为实际使用的资源付费,无需为可能发生的查询服务速度或长期占用的硬件资源支付额外费用。这种灵活的计费方式能够更加高效地管理成本,真正实现按需付费。
ClickHouse 企业版技术解析
作为全球云战略的重要一步,ClickHouse 选择与阿里云合作,独家推出ClickHouse 企业版。
阿里云作为国内领先的云服务提供商,其 ClickHouse 企业版拥有诸多卓越的云端特性。首先,它能够根据实际需求动态调整集群规模,无论是扩展还是缩减都能实现秒级响应。这些操作都基于系统负载信息的实时监控,自动进行集群的扩展与缩减,以优化查询速度并降低硬件资源消耗。无论是 SELECT 还是 INSERT 操作,ClickHouse 企业版都能根据负载情况进行智能的扩展与缩减。与原始的 ClickHouse 社区版相比,无需手动执行复杂的分片(sharding)和再分片(resharding)操作,大大简化了管理流程。此外,ClickHouse 企业版支持针对复杂查询负载的 scaling。同时,还支持轻量级的 UPDATE 和 DELETE 操作,无需执行昂贵的优化操作即可实时查询到相关的更新和删除结果。困扰开源版已久的最终一致性的问题也在企业版中得到了解决。- 引入了最新的表引擎,SharedMergeTree
SharedMergeTree 是云原生 ClickHouse 架构的重要部分之一。为了提升系统的稳定性和性能,我们采用 C++ 重写的 ClickHouse Keeper 替代了原先资源消耗较高且性能较低的 Java 版 ZooKeeper。在数据查询方面,实现了 SELECT 语句的并行扩展,使得查询能够跨多个副本并行执行。这不仅大大提升了查询速度,还提高了系统的并发处理能力。同时,基于 ZooKeeper 对元数据管理进行了优化,实现了元数据的便捷同步,无需在节点之间进行复杂的元数据复制和同步操作,从而提高了系统的一致性。在数据写入方面,我们采用了先进的“打包”优化策略,即使用 packed part
format进行批量写入。这一优化策略显著降低了写入成本和延迟,提高了系统的整体性能。后续引入分布式缓存(distribute cache)机制,以充分利用缓存资源,避免 cache 失效的问题,从而提供更高效的数据查询服务。通过 SharedMergeTree 引擎,能够自动地将 SELECT 语句分配到不同的 workers工作节点进行并行加速。这一特性将进一步提升 ClickHouse 在复杂查询场景下的性能表现,确保用户能够迅速、准确地获取所需数据。
SharedMergeTree 是ClickHouse Cloud 平台的新一代表引擎,它广泛用作ReplicatedMergeTree 的替代品,并在其基础上实现了显著的性能提升。作为 ClickHouse 在云平台服务的默认表引擎,SharedMergeTree 为用户提供了更加便捷和高效的数据处理体验。基于原有的建表语句,用户无需进行复杂的修改,即可自动转化为 SharedMergeTree 家族的表引擎。从最初的 MergeTree 引擎开始,逐步升级到 ReplicatedMergeTree 家族,再到现在的 SharedMergeTree,每一次升级都带来了性能和应用性的飞跃。在 SharedMergeTree 的基础上,还衍生出了多个变种表引擎,如 SharedReplacingMergeTree、SharedSummingMergeTree、SharedAggregatingMergeTree 等,这些变种表引擎针对不同的数据处理场景提供了更加精细化的支持。这些变种表引擎在 ClickHouse Cloud 平台上都可以直接使用,用户可以根据自身的业务需求和数据特点选择最适合的表引擎。无论是实时数据分析、数据仓库构建还是数据湖探索,SharedMergeTree 及其变种表引擎都能够为用户提供高效、稳定的数据处理服务。
一个理想的云存储引擎应具备如下一些特性和优势:首先是无状态的计算节点,这意味着它不依赖于本地的文件系统,能够实现秒级的扩展,轻松实现存算分离;其次,计算节点可以动态地添加或删除,无需复杂的配置或感知过程;此外,理想的云存储引擎应当具备透明的分片(sharding)机制,用户无需关心数据如何分布和分片,系统能够自动处理这些复杂的事务,所有计算节点都将指向一个基于对象存储的无限扩展的副本,这意味着无论数据量如何增长,系统都能够保持高效的处理能力。同时,这样的云存储引擎还应具备极高的吞吐量,能够处理大量的并发请求和数据传输,确保用户能够快速地获取所需的数据。SharedMergeTree 正是实现这样一款理想的云存储引擎。
首先,SharedMergeTree 实现了计算节点(Compute Node)和元数据管理节点(Keeper Node)的分离。Keeper Node 负责集中管理元数据,确保数据的一致性和可靠性;而 Compute
Node 则专注于相关的同步和感知,执行各种计算任务。在这种架构下,Compute Node 需要进行与 Keeper Node 的同步和感知操作,以确保它们能够准确地访问和修改数据。Keeper
Node 中的元数据提供了数据的组织和访问信息,使得 Compute Node 能够高效地处理数据。在 SharedMergeTree 中,数据合并(Merge)是一个重要的操作,用于优化数据的存储和查询性能。Merge 操作会按照特定的规则将多个数据片段合并成一个更大的片段,减少数据的碎片化并提高查询效率。这个过程会在后台自动进行,无需用户干预。在存储方面,SharedMergeTree 采用了对象存储(Object Storage)作为数据存储的基础设施。对象存储以其高可靠性、高性能和无限扩展性而著称,特别是在云环境中,它能够满足大数据应用对数据存储的高要求。通过使用对象存储,SharedMergeTree 能够提供极高的数据可靠性和性能,特别是在数据吞吐方面表现出色。
SharedMergeTree 是设计用于在共享存储上运行的,它支持标准的 S3 协议以及兼容 S3 协议的存储系统,并且也能在 HDFS 上运行。为了管理元数据,采用了 ClickHouse 定制版的 HouseKeeper 服务,来负责元数据的存储和协调。由于没有本地的元数据,并不依赖本地的文件系统如块存储(如 EBS),而是完全基于共享存储进行,这大大降低了成本,并提升了系统的可扩展性。由于没有本地元数据,SharedMergeTree 不再需要像传统架构那样通过文件系统的块存储来提供服务。此外也没有采用传统的 replica 机制中通过 parts replication log 来进行节点间的同步,从而避免了节点间冗余的通信,进一步提高了系统的一致性和性能。
关于元数据的存储,数据块的元数据被集中存储在 Keeper 中,实现了数据的高度一致性和系统的可靠性。基于这一设计,在 Keeper 之上封装了一层类似于本地文件系统的接口,为用户提供了直观易用的对象存储服务。该接口支持文件和目录的读取与写入操作,同时涵盖了创建、删除、重命名以及硬链接等文件管理功能。这些操作都是在 Keeper 内部进行了元数据的抽象存储,从而简化了用户的操作体验,无需关注底层实现的复杂性。此外,Keeper 还实现了批量更新的事务机制,该机制极大地简化了数据块的管理流程。通过这一机制,写入操作的原子性得以保证,从而确保了写入操作的完整性和一致性。这种设计不仅提高了系统的效率,还增强了系统的可维护性。
在此引入了一个全新的磁盘类型——S3WithKeeper
,它封装了与存储节点和元数据管理紧密相关的功能。关于相关的存储和节点细节,文档中已有详尽的阐述,因此在此不再赘述。Object metadata 主要存储了数据块的元数据,包括块的 ID、引用计数(ref-count)、对象类型(file or dir)、create timestamp 和 last update timestamp 等信息。
上图展示了 Metadata Storage 在 Keeper 上的文件组织形式。接下来介绍一下 SharedMergeTree 在插入数据过程中的主要流程。
- 首先在内存中预处理数据块,包括处理缺失值、排序,并根据分区键对数据进行分割。
- 并在 Keeper 上生成一个唯一的 block number。
- 随后,将这个与 block number 关联的数据块 blobs 写入云存储的日志(log)中。
- 紧接着,在 Keeper 中准备一个文件系统事务的标记。
- 之后,将数据部分(data part)写入到一个虚拟分区(virtual part)中,以便进行事务相关的操作,完成数据部分的写入后,进行事务相关的操作,将这个数据部分标记为生效。
- 最后,检查复制填充(replication filling)的结果,以确保数据的完整性和一致性。如果发现问题,在事务中重新进行写入操作。
首先进行写入,在 memory 里进行 block data 准备,然后计算 128 位的哈希值。
然后,在 Keeper 中准备 block
number 相关的事务,包括创建一些临时节点和持久化节点。
当 Keeper 完成相关准备后,它会通知 ClickHouse 的计算节点哪些数据块已经准备好(ready)。
接着,我们在 block number 处生成一个新的节点,并根据这个节点对数据块进行命名。最后,将命名后的数据块写入到 SharedMergeTree 的存储网络(S3)中。
之后在云存储(如 S3)上就可以看到新增了许多数据块。但此时这些数据块在 SharedMergeTree 中尚未被标记为可见。在 transaction 最终生效之前,需要完成一系列预备步骤。这些步骤涉及在由 Zookeeper 管理的 transaction 文件系统中进行相应的准备。将 insert 操作添加到 Zookeeper 的一个预备节点中。一旦这个节点成功创建,Zookeeper 会进行多个节点的同步,以确保数据的一致性和稳定性。随后,Zookeeper 会发出一个响应,以确认 transaction 的预备阶段已完成。
此外,还会关注一些与 block hash、work part 节点以及 fs(文件系统)相关的节点。特别是 fs 相关的节点,它们与当前文件系统上的本地文件非常相似。当 compute node 收到 response
OK 的确认后,会通知系统 insert 查询已经完成。此后,无论是通过其他节点还是同一个节点进行后续的数据操作时,系统都能够通过 Keeper 协调服务检索到相关的元数据信息,从而获取到最新的数据块修改结果,包括刚刚完成的 insert 操作所带来的数据变更。
接着,系统中的其他节点可能会启动一个 parts discovery 的过程,这是为了发现新的数据块或更新后的数据块,以确保所有节点都同步到最新的数据状态。其他节点会进行 parts 的 discovery,此时后台进程会更新 part set。同时,系统会设置对 Keeper 中 virtual_parts 节点子节点的监视(watch)。一旦 virtual_parts 相关的任何更改发生,系统都会实时推送这些更改。即使推送因某些原因暂时不生效,系统也会进行定时的更新操作,以确保数据的一致性和准确性。这些操作对用户来说都是无感知的。随后,读取所有与 parts 相关的信息,并从 Keeper 的文件系统中获取 parts 的元数据。在获取到这些信息后,就可以在其他节点上顺利完成 parts 的discovery,从而确保整个系统中数据的同步性和一致性。
首先,在插入过程中,数据会被写入 Keeper 和 S3。
写入完成后,Keeper 中的 work
parts 节点会发生变化,并触发对更改的监视(watch)。
一旦其他节点接收到这个通知,它们会获取 virtual_parts 的子节点信息,从而完成相关 parts 的 discovery。当进行查询时,本地的 metadata 缓存会生效,显著提升查询速度。
此外,本地 parts 的加载过程也相对轻量,因为大部分繁重的操作已在写入的主节点 S3 上完成。
接下来,会在文件系统(fs)上获取相关的 metadata。
包括 sharedMergeTree 相关的 checksum 和列信息。这些信息将被加载到本地,从而完成整个同步过程。至此,整个数据同步流程已顺利完成。
当然,我们同样会关注列式存储的维护。更新完成后,会执行后台的合并(merge)操作,以加速数据块的压缩(compaction),从而提高后续查询的效率。与之前的 mergetree 家族逻辑类似,通过 merge 规则来选择需要合并的 parts,并将这些 parts 读取到内存中进行处理。在正式合并之前,会在 Keeper 中广播 merge 意图,通知其他节点即将进行的合并操作。首先,在 Keeper 的 virtual_parts 中创建一个 future parts 节点,并在相应的 children parts 上加上锁,以防止在合并过程中被其他节点修改。这些加锁操作确保了数据的一致性和合并的原子性。接下来,本地 parts merge 会进行一系列的处理,包括数据的合并、排序和压缩等。处理完成后,合并好的数据块会被写入到云存储的日志(log)中,以确保数据的持久性和可恢复性。最后,将 future parts 转换为 current parts,并删除之前设置的锁。至此,整个合并过程结束,更新后的数据已经可供查询和分析使用。这种处理方式不仅提高了数据的查询效率,还确保了数据的一致性和可靠性。
首先,我们识别到需要将“123_124”这两个节点进行合并,并准备将它们 merge 成“123_124_1”。
在 Keeper 上设置一个 merge
intention(合并意图)。
在成功加锁之后,开始从 S3 上读取相关的数据块。这些数据块准备进行合并操作的数据源。通过读取这些块,可以获取到需要合并的数据,并进行后续的合并处理。这个过程确保了数据的准确性和完整性,为后续的合并操作提供了可靠的数据基础。
接下来,将进入事务(transaction)的准备阶段。在这个阶段,将进行必要的准备工作,以确保合并操作能够在一个安全、可靠的环境中执行。这可能包括检查数据的完整性、分配必要的资源、以及与其他系统组件进行协调等。通过精心准备,可以提高事务的成功率,减少潜在的风险和错误。一旦准备工作完成,就可以正式进入事务的执行阶段,开始读取 S3 上的数据块并进行合并操作。
在完成相关的合并(merge)操作之后,会在系统中记录这次 merge 的 operation。这个记录不仅标识了合并操作的成功完成,还包含了合并操作的详细信息,如合并前后的数据块、合并的时间戳等。这个 merge operation 的记录对于后续的数据管理、查询优化以及系统监控都具有重要意义。
在 merge 操作完成后,将删除相关的本地 outdated parts(过时数据块),并生成一个新的、更新后的 parts。随后,进行同步操作,以确保新生成的 parts 在系统中得到更新和识别。这样,整个合并过程就完成了。SharedMergeTree 具有如下几大优势:
- 首先,它支持成百上千的副本数量,通过基于 OSS(对象存储服务)的配置,可以轻松地将副本数量提升到极高水平,从而极大地提升系统的吞吐能力,以应对高并发的操作需求。
- 其次,SharedMergeTree 在插入操作上也具有极高的性能提升。通过在不同副本上的并发插入,实现数据的分布式写入,有效避免了早期版本中每个 replica(副本)在接收到插入日志后都需要进行本地数据操作的高负载情况。
- SharedMergeTree 在成本优化方面也有出色表现。它无需在虚拟机上添加本地卷,保证了计算节点的无状态化。节点间仅通过缓存进行交互,且这些缓存不是强依赖的,即使发生漂移或节点失效,也无需人工介入,确保了系统的稳定性和可靠性。
- 在高负载任务完成后,SharedMergeTree 能够快速缩减副本数量,极大地降低了存储成本,使账单更加友好。
- 此外,SharedMergeTree 还大大改善了数据一致性问题。通过其内部机制,确保各副本之间保持同步,避免了读取到旧数据或数据不一致的情况。
- 由于实现了无状态化,SharedMergeTree 的初始化和集群扩展都变得非常迅速和简单,无论是快速启动新节点还是关闭现有节点,都能够快速完成,无需复杂的配置和人工操作。
在 SharedMergeTree 中,由于相关副本的元数据在 Keeper 上得到了统一维护,这意味着我们无需在不同节点上分别同步和维护元数据。这一特性有效地避免了以往需要在 replicate matrix 的每个节点上都完整复制元数据的做法。这不仅减少了数据冗余,还大大提高了系统效率。更重要的是,它消除了过去在副本间频繁复制庞大的日志(log)文件的繁重任务,从而显著减轻了系统的负担。这种无状态化的设计使得 SharedMergeTree 在扩展、管理和维护方面都变得更加简单和高效。
由于 SharedMergeTree 的无状态设计,成功地消除了过去副本之间繁重的日志文件复制过程。经过这些优化,能够实现服务的快速启动。从线上部署经验来看,对于大型和重型数据表,由于数据的负担主要转移到了 Keeper 节点上,整个启动过程可以在一秒内完成。此外,在扩展副本数的过程中,从 10 个副本扩展到 100 个副本仅需 0.8 秒,这一表现充分证明了 SharedMergeTree 的高效性和可扩展性。
通过实际测试,在 Wikistat 数据集上展示了 SharedMergeTree 的卓越性能。具体而言,每秒可以处理高达 7500 万行的数据,而在整体插入 3510 亿条数据时,仅需 5000 秒即可完成。更为令人印象深刻的是,在由 100 个动态分配(skill out)的节点组成的集群中,能够并发执行高达 1500 次的 merge 操作。对于这样的小型工作负载,甚至无需启动多个副本或节点来进行负载均衡。凭借SharedMergeTree 的高效性和可扩展性,能够直接进行资源的快速调配(scale up),轻松应对各种数据处理需求。以上就是本次分享的内容。欢迎大家关注 ClickHouse 的官网和官方公众号获取更多信息或进行试用。
分享嘉宾
INTRODUCTION
Auxten
ClickHouse
director of software development, ClickHouse Inc.
ClickHouse Core team 的 Technical Director,目前负责ClickHouse 中国区的技术团队。
点个在看你最好看
SPRING HAS ARRIVED