这篇文章是今天早上在上班路上开始思考的,7点20进入办公室开始写,8点40写完,只用了一个多小时,因此难免一些观点有些考虑不周,请读者见谅了。现今国产分布式数据库市场十分热闹,仅仅是交易型关系型数据库就有近百款,大家在选择数据库的时候也容易挑花了眼。
以前我也写文章讨论过,没有完美的数据库,用户应该根据其场景和应用需求来选择合适自己的数据库产品。数据库架构也是其中的一个重要的选择要素,特别是分布式数据库,不同的架构其优缺点十分明显,某些分布式数据库存在的缺陷因为架构问题,是较难解决的,因此了解某个分布式数据库属于哪种架构,有哪些优点和缺点十分重要。
分布式数据计算最主要有两种形式,一种是分区SHARDING,一种是全局一致性HASH,根据这种分布式数据计算的种类不同,衍生出存算分离和存算一体这两种分布式数据库架构。进一步再分出一系列子类别,比如对等模式,代理模式,外挂模式等。存算一体SHARDING模式的分布式数据库最为典型的是Oceanbase。下面是OB的架构图:
每个Observer是一个计算存储一体化的独立服务,带有SQL引擎,事务引擎和存储引擎,并存储一部分分片数据(分区)。OB采用的是对等模式,客户端连接到任何一个OBSERVER都可以全功能使用数据库。OBSERVER会自动产生分布式执行计划,并将算子分发到其他参与协同计算的OBSERVER上,完成分布式计算。管理整个集群的RootService只在部分Observer中存在,并全局只有一个是主的,其他都是备的。
实际上每个OBSERVER就组成了一个完整的数据库,如果所有的数据都存储于一个OBSERVER上,我们的访问方式类似于一个集中式数据库。这种架构下的数据分区采用了高可用,一个数据写入主副本(Leader)后会自动同步到N个备副本,OB采用Paxos分区选举算法来实现高可用。其备副本可以用于只读或者弱一致性读(当副本数据存在延迟时),从而更为充分的利用多副本的资源。实际上不同的分布数据库的主备副本的使用方式存在较大的差异,有些分布式数据库的备副本平时是不能提供读写的,只能用于高可用。
大家看到这里,可能觉得OB的架构不错,各种问题都考虑得比较周到了。不过还是那句话,没有完美的分布式数据库架构,SHARDING存储需要分布在各个SHARDING分区中的分区表要有一个SHARDING KEY,根据SHARDING KEY来做分片处理。SHARDING模式虽然应对一般的数据库操作是没问题了,不过如果一条复杂的查询语句中没有关于SHARDING KEY,那么就没办法根据SHARDING KEY去做分区裁剪,必须把这个算子分发到集群中存储这张表的分区的所有OBSERVER上,这种情况被称为SHARDING数据库的读放大。
采用SHARDING模式的数据库系统,你要区分其技术水平,重点要看针对读放大的措施是否完善,你的应用系统能否采用这些方案来让读放大的影响变得更小。实际上OB在这方面做了不少工作,今天篇幅问题,我们不展开讨论了。既然存算一体的SHARDING模式有这种缺陷,那么能不能采用存算分离的方案呢?大家来看TIDB的架构:
TIDB是采用存算分离的,计算引擎TIDB和存储引擎TIKV是分离的,因此TIDB不存在存算一体的SHARDING分布式数据库的这种读放大的问题。似乎TIDB要技高一筹,完美地解决了OB等SHARDING数据库的问题。不过事实上没有那么简单,就像我上大学时的老师陈道蓄先生说过的“任何好东西都是有代价的”,存算分离解决了SHARDING的读放大问题,但是又引入了一个新的问题。那就是计算节点的本地缓冲问题
。因为大型分布式计算环境下实施缓冲区融合成本极高,因此每个TIDB节点只能有本地缓冲,不能有全局缓冲。因此在TIDB的SQL引擎上是没有全局DB CACHE的,TIDB的数据缓冲只能建立在TIKV和TIFLASH上。SQL引擎->DB CACHE->存储路径->数据存储介质这种传统数据库的数据读取模式变成了SQL引擎->本地局部缓冲->存储路径(网络)->存储节点缓冲->存储介质这种模式。其效率大大地降低了。
为了解决这个问题,采用此类架构的数据库系统,就必须采用更快的存储介质,从而解决缺乏全局DB CACHE支持的问题。当然这个问题也并不是不能解决的,通过更为细致的算子下推,采用类似ORACLE的SMART SCAN等技术,可以解决大部分问题。实际上,国产分布式数据库使用的最多的架构并不是上面的这两种,而是基于SQL代理的存算分离架构,这种架构采用的数据存储方式也是SHARDING方式,不过使用了一个SQL PROXY。腾讯TDSQL、热璞HOTDB、中兴通讯GOLDENDB、百度GAIADB、京东STARDB、openGauss分布式计算框架等著名分布式数据库都是采用类似的架构。
TDSQL由调度器、网关、Agent三个核心组件加上MySQL组成,其中调度器是调度分布式计算的,内部又分为zookeeper和scheduler server;接入网关是一个SQL代理,应用连接到接入网关上进行SQL操作。代理与MySQL实例一起,构成数据节点。
多个数据节点构成服务单元SET。每个SET是一组高可用组,由一主多备组成。数据按照SET进行SHARDING分区。这种架构中的存储引擎是一个完整的数据库,大部分采用此类架构的分布式数据库采用了MYSQL作为存储引擎的核心,这是因为MYSQL管理起来比较轻量级,出问题大部分情况下隔离故障或者重启实例就可以解决。这种架构在网关(代理)层就具有分布式计算的能力,在此层分解SQL,产生分布式执行计划,然后将分解出的SQL分配给参与本次计算的所有存储节点,存储节点执行SQL,获得所需要的数据集,传输给网关,由网关进行聚合计算。这种架构的底层包含一个完整的数据库引擎,因此可以把研发的主要精力放在网关SQL引擎上,大大降低了开发难度。
不过虽然很多数据库产品都是采用了此架构,各个厂商的技术能力不同,因此做出来的产品在性能,SQL支持能力等方面都存在较大的差距。存储层采用完整的数据库引擎也是个双刃剑,如果不对数据库引擎的优化器做大的优化,则数据库引擎在性能上的弱点会带坏整个分布式数据库的性能,而去做优化又是大投入的工作。也是无法两全其美的。另外高可用方面的主从选择,切主算法的实现水平方面,也会影响此架构的数据库的用户使用体验。
最初此类架构的主从切换类似于MYSQL的主从切换,对前端应用的影响很大,如果业务高峰时出现主DN故障,那就是灾难性的。现在一些头部企业在这方面做了极大的优化,用户体验也得到了很大的改善。还有一些分布式数据库是采用SQL ON HADOOP架构的,比如易鲸捷。
其存储引擎是一个经过了优化的HBASE兼容的引擎,在此基础上构建整个分布式数据库系统。此种架构的优势是底层采用了一个十分成熟的分布式存储系统HDFS和分布式数据库HBASE,天生对于大数据量的计算以及物联网场景的应用有较好的支持。不过因为HADOOP本身并不是为低延时高并发的OLTP场景甚至HTAP场景设计的,因此存储引擎很可能会成为今后数据库的瓶颈。
2017年我在贵州遇到时任易鲸捷cto的丁洪先生的时候,他也认为易鲸捷要脱胎换骨,必须重写存储引擎,使之更适合HTAP的应用场景。时间关系,今天就聊到这里了,似乎还是没聊透。实际上就本文的标题“没有完美的分布式数据库架构”,我们的分布式数据库架构好坏的争论不妨放一放,大家都多一点考虑自身架构的缺点,想办法去弥补自身的缺陷,那才是对用户负责。解决分布式计算的问题,在分布式SQL引擎优化,算子下推,避免读放大,主副本切换、读写分离、运维可观测性提升等方面,都是需要认真去不断提升的。
选择分布式数据库,仅仅看架构是不够的,更重要的是看数据库厂商是不是针对我们的应用场景做了很好的优化,产品能否满足我们应用场景的需求。