引言
ByConity 是一款字节跳动开源的云原生数仓引擎。它的一个重要优势是采用存储计算分离的架构,实现了读写分离和弹性扩缩容。这种架构确保读操作和写操作不会相互影响,使得计算资源和存储资源解耦,两者可以按需的且独立的扩缩容,确保资源高效利用,同时保证数据读写的强一致性。此外,ByConity 支持多租户资源隔离功能,保证不同租户之间不会互相影响,更加适合多租户环境,同时 ByConity 采用主流的 OLAP 引擎优化,提供更加优异的读写性能。
ByConity 技术背景
ClickHouse 是一个开源的列式数据库管理系统,它采用 Shared-Nothing 的架构,这种架构有以下典型特点:
每个节点独立管理一部分数据
节点之间没有数据共享
存储和计算紧耦合
它的查询执行大致分为两个阶段。第一阶段是每个节点独立处理它管理的数据分片,负责 I/O、查询和计算。第二阶段是 Coordinator 节点将第一阶段每个节点计算的结果进行默认汇总,然后返回给客户端。这种架构可以实现高性能和可扩展性,特别适合固定业务规模的情况下进行高并发查询。同时,由于节点之间无数据共享,扩展性也比较容易实现,可以实现线性扩展。
图 1 Clickhouse 架构
但在实际使用过程中,我们发现如下问题:
扩缩容代价高:对于快速发展的业务,需要频繁进行扩容操作,而数据的 reshuffle 以及数据搬迁会影响实际读写操作
多租户之间可能会互相影响:不同业务之间可能会有不同的业务特征,导致某些业务在特定时间点上占用了整个集群的资源池,影响其他租户
读写操作受到影响:每个节点都必须负责一部分读写任务,当节点资源使用较多时,查询性能可能会受到影响
资源使用情况会存在浪费:特别是在多个中小型业务共用同一个集群的情况下,可能会存在资源预估不足或者其他情况导致资源浪费
ByConity 架构介绍
基于以上使用过程中发现的问题,我们在开源的 ClickHouse 架构基础上进行了升级,引入了计算与存储分离的架构,将原本计算和存储分别在每个节点本地管理的架构转换为在分布式存储上统一管理整个集群内所有数据的架构,使得每个计算节点成为一个无状态的单纯计算节点,并利用分布式存储的扩展能力和计算节点的无状态特性实现动态的扩缩容。此外,该架构还支持多租户隔离和读写任务的分离。实现了以下优势:
高弹性、高扩展性:计算和存储独立扩缩容
多租户隔离:不同租户使用不同计算组
读写分离:读写使用不同的计算资源
存储计算分离架构
从总体架构上讲,ByConity 的存储计算分离架构如图 3 所示,主要分为三层:共享服务层、计算层和云存储层。
共享服务层:主要组件是 Cloud Service,是所有查询的入口,会对查询进行解析和优化,生成 Query Plan 然后下发给计算层处理。它同时相当于一个协调者的角色,负责一些服务、组件和事务的管理,也包含元数据的管理--Metadata Storage。
计算层:主要是计算资源组--Virtual Warehouse(简称 VW) ,计算资源组件可以动态启停,包括一个 Read VW 和一个 Writer VW。在 Read VW 中,每个 Worker 节点都有一个 Optimizer 和 Runtime 模块,以及一个 Disk Cache 模块来缓存部分数据以减少对分布式存储远端系统的访问,热点数据存储在 Disk Cache 模块中。在 Writer VW 中,数据会先以 ClickHouse 格式写入本地磁盘--Local Disk,然后再批量写入分布式存储,以提高写入性能。
云存储层:是分布式统一存储系统,ByConity 所有的数据都存储在这一层,在计算层进行查询时,会从云存储层中读取数据。云存储层的具体实现可以采用各种云存储服务,如 HDFS、S3 等。
除此之外,ByConity 还包括一些共享的服务组件,如 TSO、Daemon Manager、Resource Manager、后台任务和服务发现等组件。
图 2 ByConity 存储计算分离架构
ByConity 架构优势
资源隔离
在大数据场景下,资源隔离是非常重要的,它可以提高系统的资源利用率、性能、安全性、可靠性和灵活性,帮助企业解决诸多业务痛点问题,如:
避免不同租户之间的资源冲突,提高数据安全性和租户使用体验;
根据不同的业务场景需求分配不同的资源,提高资源利用率和性能;
提高数据安全性,通过隔离不同的数据处理任务或数据库实例,限制访问权限;
实现弹性扩缩容,根据不同的业务和数据量需求动态调整资源分配;
提高系统的可靠性和稳定性,隔离故障,避免对整个系统造成影响。
但 ClickHouse 并没有对资源隔离做专门的设计,它是通过集群和 replication 的配置、load_balanding 策略,以及指定本地表的写入做到一定的读写分离。对于冷热分离,ClickHouse 是通过配置存储策略和使用 TTL、TO DISK、TO VOLUME 等技术实现。
ByConity 通过存储计算分离的架构设计很好的实现了资源隔离,它引入了计算组(Virtual Warehouse 简称:VW)的概念,Virtual Warehouse 是计算资源的虚拟组织,可以将计算资源按需划分为多个虚拟集群,在不同租户之间提供物理资源隔离。同时,把原本与计算资源耦合的存储统一到分布式存储管理后,计算资源与存储资源是完全解耦的且无状态的,从而计算节点主要承担的是计算任务,这些任务可以是数据写入、用户查询,也可以是一些后台任务。因此 ByConity 通过部署和使用 Virtual Warehouse 来实现多级资源隔离:
图 3 ByConity 计算组
租户隔离:ByConity 的 Virtual Warehouse 是无状态的,可以根据不同的业务和场景进行按需的创建,且每个 Virtual Warehouse 是独占系统资源,所以会很轻松的实现多租户的隔离。当然,为了提高资源利用率,ByConity 也支持 Virtal Warehouse 之间的资源租借,实现资源共享。
计算资源隔离:Virtual Warehouse 使得计算资源做到物理层面的隔离,且每个 Virtual Warehouse 可以包含多个 worker,可以被灵活创建。
读写分离
除了资源隔离外,我们还希望达到读写的分离,读写的分离是将读操作和写操作分别处理。因为在实际业务中,读操作和写操作对硬件资源的要求,以及时间的要求是不同的,所以我们希望用不同的硬件资源去执行读操作和写操作,避免读写互相影响,影响系统性能和浪费资源,具体好处如下:
降低存储成本:将读操作和写操作引导到不同的存储节点上,避免不必要的数据复制和冗余。
提高查询效率:将读操作引导到专门的读节点上,减少查询等待时间,从而提高数据处理效率和用户体验。
降低网络成本:将读操作引导到离用户更近的读节点上,减少数据传输的距离和网络成本。
提高系统可用性:将读操作和写操作分别引导到不同的节点上,当某个节点发生故障时,只会影响到该节点上的读或写操作,不会影响到整个系统的可用性
ByConity 可以通过 Virtual Warehouse 实现读写分离,用户可以通过指定读操作和写操作使用哪个 Virtual Warehouse,系统会自动将不同的读写请求转发。例如 Insert 操作使用专门用于写入的计算组,Select 操作使用专门用于读取的计算组,读写作业之间不会相互影响。但由于 ByConity 使用了统一的分布式存储,必然会存在性能的问题,这里 ByConity 是通过本地缓存(Local Cache)去解决的。
本地缓存
对于大容量的本地缓存管理,ByConity 使用 Bucket-LRU 算法,它是在 LRU 算法的基础上进行了优化,会将缓存中的数据块分为多个桶,每个桶中保存一定数量的数据块,并对每个桶使用 LRU 算法进行淘汰。这样可以将缓存中的数据块分散到多个桶中,从而降低了缓存淘汰的频率,同时也减少了 LRU 算法的开销。即使在计算组扩缩容等导致网络拓扑发生变化时,也依然使用这种机制,避免数据的 reshuffling。
图 4 ByConity 的缓存力度 Segment
针对缓存的粒度,ByConity 引入 Segment 概念,如图 4,它是一个介于文件为单位和压缩块为单位的一个缓存粒度,大小可配置,并且适合文件存储。Segment 是由多个 Mark 组成的,每个 Mark 包含多个压缩块。当查询需要读取某个 Segment 时,可以先检查缓存中是否存在该 Segment,如果存在,则直接从缓存中读取数据,否则需要从磁盘读取数据。在实际使用中,需要根据数据的特点和需求选择合适的缓存策略,并进行优化和调整,以达到最优的性能和效果。ByConity 的缓存策略有以下几种:
按照 Segment 的访问频次:根据数据访问的频次来判断哪些数据是热点数据,并进行缓存。
按照 Segment 的访问范围:有些数据虽然访问频次不高,但是其查询范围较大,也需要被缓存。
根据数据更新时间来决定热点数据:对于实时表等场景,新过来的数据往往是热点数据,需要被缓存。
基于统计信息来优化缓存策略(开发中):根据统计信息来决定哪些数据是热点数据,需要被缓存。
ByConity 的本地缓存的主要目的是在分布式存储中,通过使用较少成本(容量)的本地高速缓存盘,来缓存数据以减少网络读取带来的性能延迟增加问题。本地缓存的使用可以提高数据的访问速度和响应速度,从而减少对网络的依赖,降低系统的延迟,提高系统的性能和稳定性。
无感扩缩容
随着业务数据翻倍增长,必须通过不断的扩容支撑业务发展,但在 ClickHouse 的基础上扩容的成本很高,这是因为 ClickHouse 从架构上没有专门考虑扩缩容,导致需要运维同学手动或者通过自动化脚本去创建新的 ClickHouse 节点和迁移副本数据来完成扩容,因此存在时间成本和迁移结果校验的问题。再者,在扩容中需要将新的分片部署到新的节点上,这会导致数据不再均衡,需要通过数据再均衡来解决。
ByConity 的存储计算分离架构可以天然解决这个问题,能实现业务无感的扩缩容。ByConity 扩容分为两种:一种是纵向扩容,即调整 Worker 的 CPU 核数和内存大小;另一种是横向扩容,通过增、减 Worker 的数量,提升系统并发能力。这些依然通过 ByConity 的 Virtual Warehouse 和 Worker 去实现:
一方面资源管理器(Resource Manager)负责对计算资源进行统一的管理和调度,能够收集各个计算组的性能数据,资源使用量,为读写任务和后台任务动态分配资源并进行扩缩容,提高资源使用率。ByConity 的组件都已经容器化,通过调整 Kubernets 的 replica 数量可以非常方便的对指定的计算组进行扩缩容。除此之外,还可以结合计算组资源使用量,通过设置 kubernets 的扩缩容阈值实现动态扩缩容。
另一方面,ByConity 的元数据和数据存储在远端,计算节点的无状态化使扩缩容变得十分轻量,只需等计算实例启动完成,即可立即服务,无需额外的数据迁移开销,实现实时扩缩容。
ByConity 使用场景
ByConity 使用大量成熟 OLAP 技术,例如列存引擎、MPP 执行、智能查询优化、向量化执行、Codegen、Indexing、数据压缩,主要用于 OLAP 查询和计算场景。在实时数据接入、大宽表聚合查询、海量数据下复杂分析计算、多表关联查询场景下有非常好的性能。
表 1
总结和展望
总结来说 ByConity 使用了存储计算分离的架构,且在多个方面进行了优化和升级,具有以下的优势和特性:
资源隔离:对不同的租户进行资源的隔离,租户之间不会受到相互影响。
读写分离:计算资源和存储资源解耦,确保读操作和写操作不会相互影响。
弹性扩缩容:支持弹性的扩缩容,能够实时、按需的对计算资源进行扩缩容,保证资源的高效利用。
数据强一致:数据读写的强一致性,确保数据始终是最新的,读写之间没有不一致。
高性能:采用主流的 OLAP 引擎优化,例如列存、向量化执行、MPP 执行、查询优化等提供优异的读写性能。
图 5 是接下来 ByConity 未来发展规划图,未来我们计划从以下几个方面进行优化和改进:
图 5 ByConity 未来发展规划
支持分布式 Disk Cache,一方面解决节点重启导致 Local Cache 丢失的问题,另一方面希望达到全局最优的 Cache 机制,提升 Cache 命中率。
支持外表服务,例如 Hudi 和 Iceberg 等
支持存储服务化,在 ByConity 存储数据层上,利用 Spark 引擎或 Presto 引擎的计算能力执行一些 ETL 任务等
加入我们
ByConity 社区拥有大量的用户,同时是一个非常开放的社区,邀请大家和我们一起讨论共建,在 Github 上建立了 issue:https://github.com/ByConity/ByConity/issues/2
延伸阅读:
字节跳动开源ByConity:基于ClickHouse的存算分离架构云原生数仓
评论