云原生Clickhouse优势概述
以Clickhouse为基础,借鉴Snowflake等系统的设计思路,打造一款高性能的云原生OLAP系统,为用户提供多场景下的一站式的数据分析平台。
Clickhouse是一款性能十分强悍的OLAP引擎,凭借优秀的性能在用户行为分析、ABTest、在线报表等多个领域大放异彩,但是目前Clickhouse在易用性、稳定性、可维护性、功能特性等方面都还有较多不足,主要体现在以下几方面:
Snowflake 是当下最火的云原生数仓,它SaaS化的设计理念极大的提升了数据开发者的使用体验,Clickhouse高性能的存储引擎和计算算子是一个非常优秀的底座,我们希望以Clickhouse为基础,借鉴Snowflake的设计思路,打造一款高性能的云原生OLAP系统,为用户提供多场景下的一站式的数据分析平台,包括:
存算分离正在数据库领域掀起一场变革,不论OLTP还是OLAP系统都在拥抱存算分离。通过对Clickhouse的源码进行阅读分析,发现在Clickhouse中一旦实现了存算分离,那么稳定性、可维护性以及成本等方面的问题都会迎刃可解。Clickhouse虽然在分布式集群管理方面很弱,但是这也意味着在改造存算分离架构时的负担很少,成为了一个巨大的优势,所以我们把存算分离作为第一个目标,希望通过引入存算分离技术,并且围绕存算分离技术对分布式集群管理等各个方面进行改造,为用户提供一个更好的Clickhouse。
云原生Clickhouse的架构设计如下图,具体包括三层:
(云原生Clickhouse架构设计图)
集群管理层:分布式集群的大脑,它主要包含基于分布式一致性协议实现的元数据管理服务、多集群共享的分布式任务调度服务;
计算层:用户通过创建的计算集群来实际使用分析服务,每个计算集群由多个节点组成,用户的查询任务在一个计算集群上的节点里完成,同一个用户的多个计算集群可共享集群管理层;
存储层:基于共享存储实现,用户的所有数据都存放在共享存储内,可以被多个计算集群访问,同时它提供了廉价、按需、无限扩展的存储能力;
数据流
控制流
基于共享存储的存算分离机制
2.1 易使用,易运维
过去在运维一个Clickhouse集群时,很头疼的一点就是集群管理,例如,我们要向集群中增加一个节点,需要以下操作:
在新架构下,Master维护了全局统一的元数据信息,使得我们可以通过一条条简单的SQL命令来自动化集群管理, 例如下面这个命令就是向集群中增加一个副本节点:
ALTER CLUSTER cluster_name ADD BACKEND 'ip:port' TO SHARD 2;
这个命令首先会修改Master统一管理的元数据,然后新增的Clickhouse Node会从Master上同步相关元数据,更新本地配置,最终用户可以在Clickhouse上运行以下命令来获取集群的节点信息,同时当前Clickhouse的各种查询语句也可以继续使用集群的拓扑结构来构建复杂的查询:
SELECT * FROM SYSTEM.CLUSTERS;
此外,用户不需要再使用ReplicatedMergeTree引擎,不需要关注元数据在Zookeeper上的存储路径,只需要普通的MergeTree引擎即可,系统内部会自动为每个表分配唯一的数据存储路径,并且使互为副本的多个表共用存储空间。
CREATE TABLE t1 (
partition_col_1 String,
tc1 int,
tc2 int)
ENGINE=MergeTree()
PARTITION BY partition_col_1
ORDER BY tc1;
2.2 统一共享的分布式任务调度服务
(分布式DDL任务执行流程图)
在引入Master Node后,分布式DDL任务的执行流程如上图所示,下面以Create Table为例介绍一下具体的流程:
基于Master的分布式架构还具备以下特点:
高可用:Master Node自身多副本,多副本之间通过一致性协议保证高可用。同时,我们考虑了Master Node和ClickHouse Node间的松耦合设计,即使Master Node全部故障,也不影响存量业务的普通读写操作,仅限制新DDL操作的执行。
并发控制:Master Node能够区分对不同Table的DDL请求,可以控制不同请求的并发级别,比如对于Alter是顺序执行,对于Mutation可以并发执行。
回滚机制:DDL Task不一定能够在所有的Clickhouse Node上全部成功,部分成功是常态;过去Clickhouse 在这种情况下会出现各个Clickhouse Node的状态不一致,我们引入了回滚机制,如果任意一个任务失败,整个Job就会失败,保持各个Clickhouse Node的状态一致.
垃圾清理机制:Clickhouse Node自身会定时的跟Master Node做状态同步,清理本地的垃圾Table或者数据目录。
资料领取直通车:Golang云原生最新资料+视频学习路线
Go语言学习地址:Golang DevOps项目实战
2.3 基于存算分离架构的多副本
多副本技术是分布式系统底层存储的核心的机制,任何一个分布式系统都有大量的代码在处理多副本, Clickhouse 面临的很多问题也是由于多副本引起的。当前Clickhouse的多副本机制如下所示:
(当前Clickhouse多副本机制)
当前架构有以下缺陷:
使用了共享存储之后,我们支持多读多写的模型,多副本管理的架构如下:
方案概要:
为了能够让所有的副本都可以提供读写服务,基于Commit Log,我们增加了冲突处理机制,思路如下图所示:
(冲突处理机制思路图)
在存算分离架构下,成本相对于原来有大幅度降低:
同时,多读多写的模型消除了传统主从副本在故障时的复杂切换逻辑,任意节点挂掉,其他副本都可以轻松接管读写,大幅提高系统可用性。
2.4 高可用无状态的数据服务层
Clickhouse社区目前有基于S3的多副本机制,也能够降低多副本带来的存储成本,但是这种方案目前有以下问题:
我们的改造思路是把所有的元数据都与本地存储剥离,使得基于Clickhouse的计算层彻底无状态:
在存算分离模式下,多副本的目标已经从保证数据的可靠性转变为保证服务的可用性,通过把每个副本本地的状态消除,可以任意增加副本的数目,提升服务的可用性而不需要付出存储成本;另外还可以做到查询级别的调度,根据每个节点的健康状况和负载情况,把查询调度到合适的节点上来执行,而不需要复杂的多副本同步,服务整体的可用性大幅度提升。
2.5 秒级的弹性伸缩能力
在数据服务层完全无状态后,除了高可用之外,带来的一个巨大的优势是弹性,节点的加入和退出不需要复杂的数据同步机制,可以在秒级完成集群的伸缩:
集群能够在秒级完成扩缩容有以下优势:
2.6 持续兼容开源生态
在改造Clickhouse的过程中,我们跟很多不同的团队做了一些交流,发现一个非常大的问题就是大家对Clickhouse做了大量的修改,然而由于各种原因这部分代码没能合并到社区,最终跟社区分叉了,所以出现了XXX版本Clickhouse;这一幕在过去10年的大数据历程中反复出现,我们都见过XXX公司内部版本的HDFS、HBase、Kafka等,而这些所谓的自研版本几乎都失败了。Clickhouse的功能迭代速度是很快的,比如最近社区推出的LLVM表达式优化、异步的Pipeline执行、zOrder等,都是非常重要的feature,对性能提升十分明显,一旦分叉这些功能就都用不上了,所以我们坚信兼容Clickhouse社区虽然会慢点,我们的设计会复杂点,但是会走的更远。
对于一个云服务而言,我们面对着各种各样的用户,很多用户的诉求是我们提供的Clickhouse能够跟随社区的版本升级。所以我们在项目伊始就定下了一个原则----尽量少的侵入Clickhouse,能够跟进开源社区Clickhouse进行持续升级。
(模块结构图)
模块结构如上图所示,在我们的架构中,Clickhouse实际是一个单机的库,所以虽然我们实现了复杂的控制流和存算分离的功能,但是通过精巧的设计,基本上对Clickhouse没有侵入,改动了极少的代码,这使得后续的版本升级更加方便,能够随时合并Clickhouse社区的最新功能。
通过把Clickhouse进行云原生改造,与现状相比有以下优势:
• 简单、易维护:通过简单易用的集群管理、统一共享的分布式任务调度服务,可以大幅降低运维人员的运营压力,降低用户的使用门槛,运维同学可以一键完成集群伸缩、用户可以统一入口简单完成DDL操作等,整个系统更为简单易维护。
• 高可用、可扩展:整个系统的架构设计有充分的可用性考虑,各组件的容灾能力都颇具创新,后续也会继续增强;此外,消除了Zookeeper这个明显的中心化瓶颈后,系统支持的数据量可无限扩展,表数量仅受限于Master Node内存,在压力测试中,能够支持500万以上的Table,可覆盖绝大部分需求。
• 低成本:通过存算分离技术,存储成本至少降低了50%,消除了副本间的冗余写入开销,也去除了Zookeeper带来的附加成本;多读多写的模型保障了各个副本之间实时强一致,用户不需要在导入性能和一致性等级之间做权衡;资源调度方面,用户也可以根据集群的负载情况,秒级的弹性伸缩集群资源,可用于降低成本,也可用于提升一部分场景下的查询性能。
• 兼容开源,复用超高性能:持续完整的兼容性,对外的协议、语法、数据存储格式都完全兼容Clickhouse现有的版本,用户可以很方便的迁移,也可以充分享用ClickHouse的超高性能。
未来工作