OB君:做为一款100%自研的分布式数据库,OceanBase历经了近十年的发展历程。近十年来,OceanBase的存储架构经历了屡次演进,以解决业务愈来愈复杂苛刻的存储需求。本文整理自赵裕众(花名陈群)在2019 SACC中国系统架构师大会中的演讲。
随着用户数据量的不断增加,基于传统共享存储的纵向扩展能力渐渐变得力不从心,分布式存储成为应对用户海量数据的标配。算法
做为一位架构师,在设计系统的分布式存储架构时,须要关注哪些方面呢?或者咱们换句话说,对于客户来讲,一个理想中的分布式存储产品应该具备哪些特性呢?数据库
咱们认为完善的分布式存储架构应该关注这五个方面:编程
架构设计服务于业务,再完美的系统架构都须要有业务进行使用才能创造价值。对于业务来讲,固然但愿咱们的产品可以同时具有高扩展、高可用、强一致、低成本以及零门槛的易用性,但对于系统架构师和开发者来讲,这五个特性之间存在相互矛盾的很多,同时在实现这些特性时也会面临巨大的复杂性,这要求咱们在作系统设计及实现时须要有所权衡。服务器
下面咱们就OceanBase创立九年多以来存储架构的演进历程,来回顾每一次架构变动背后的权衡与思考。架构
1)OceanBase 0.1版本(2010年)并发
OceanBase由阳振坤于2010年在淘宝创立,当时淘宝大多数业务都已经按照用户维度作了分库分表,一个全新的分布式存储系统彷佛很难有用武之地。最终咱们找到了OceanBase的第一个业务:淘宝收藏夹,也就是咱们今天打开手淘看到喜欢的商品点收藏时用到的收藏夹,直到今天它仍然跑在OceanBase数据库上面。负载均衡
当时收藏夹面临了一个分库分表难以解决的问题,它的核心业务主要包括两张表,一张是用户表,记录一个用户收藏的商品条目,数量从几条到几千条不等;另外一张是商品表,记录一件商品的描述、价格等明细信息。若是一个用户增长/删除收藏,那么相应的就向用户表中插入/删除数据就能够了;同时若是一个商家须要修改商品描述,例如修改商品价格等信息,那么相应的更新商品表就能够了。框架
当用户打开收藏夹时,经过用户表和商品表的链接查询,就能够展示给用户最新的商品信息。最开始的时候,这两张表是在一个数据库里面,也一直运行地很好,但随着用户数据量的增加,单个数据库放不下了,通常经常使用的作法是将表按照用户维度进行拆分,用户表是能够这样拆,可是商品表中没有用户字段,若是按照商品条目进行拆分,那么在用户打开收藏夹时,就须要对多个不一样的库进行查询并作链接,当时的数据库中间件并无这样的能力,即便能够这么作,一次查询也会耗费很是长的时间,会极大的影响用户体验,业务遇到了很大的困难。运维
OceanBase接下了用户的这个难题,若是咱们分析扩展性、高可用、一致性、低成本和易用性这五个特性,那么什么是业务的刚需,什么是业务能够放弃的呢?业务最强的刚需是扩展性,由于传统的单机模式已经走到了尽头;最能够放弃的实际上是易用性,由于业务对写入查询的使用很是简单,提供简单的读写接口就能够知足业务需求,业务甚至不须要在表上构建索引。同时咱们也注意到业务对一致性也有必定的需求,业务能够容忍必定的弱一致读,但不能容忍数据出错。这些特性决定了OceanBase从诞生的第一天起,就是一个支持在线事务处理的关系型分布式数据库。异步
咱们注意到收藏夹这个业务的特性,它的存量数据比较大,可是天天的增量并不大,毕竟天天新增收藏的用户并非特别多。它更关心数据存储的扩展性,而对写入的扩展性要求并非很高。咱们将数据分为两部分:基线数据和增量数据。基线数据是静态的,分布式地存储在ChunkServer上。增量数据写在UpdateServer上,一般是存储在内存里面,经过Redo Log支持在线事务,在天天的业务低峰期,UpdateServer上的数据会与ChunkServer上的数据作合并,咱们称之为“每日合并”。MergeServer是一个无状态的Server,提供数据写入的路由与数据查询的归并;RootServer负责整个集群的调度与负载均衡。这是一个相似于LSM Tree的存储架构,这也决定了从此OceanBase的存储引擎都是基于LSM Tree的。
咱们回过头来看OceanBase0.1的架构,它实际上具备很强的一致性,由于写入是个单点,读到的数据必定是最新写入的数据,同时成本也并不高,也具备必定的扩展性,存储空间能够很容易地作扩展,很好知足了当时业务的需求。
2)OceanBase 0.2-0.3版本(2011年)
很快OceanBase 0.1版本上线了,并为收藏夹业务提供了读服务,但业务不能把全部流量都切到OceanBase上面来,由于OceanBase 0.1版本的架构有着一个很大的缺陷:它不是高可用的。任何一台服务器的宕机都会形成数据的不可访问,这对于收藏夹这样的业务是没法接受的。很快咱们带来了OceanBase 0.2版本的架构,补上了高可用的短板。
在OceanBase 0.2版本中咱们引入了主备库模式,这也是当时传统数据库经常使用的容灾模式,数据经过redo log从主库同步到备库,当主库发生问题时,能够把备库切换为主库继续提供服务。redo log的同步是异步的,这意味着主备的切换是有损的,可能会丢失若干秒的数据。
咱们将OceanBase 0.2版本和OceanBase 0.1版本的架构进行对比,会发现OceanBase 0.2版本终于有了高可用这一重要特性,但高可用的得到不是没有代价的,首先系统再也不是强一致的了,咱们不可以保证业务老是可以读到最新的数据,在宕机场景下,数据可能会有部分丢失;其次主备库的引入极大地增长了成本,咱们使用的机器数量翻番了。以后的OceanBase 0.3版本基于OceanBase 0.2版本作了不少代码上的优化,进一步下降了系统成本,但从架构上来讲和OceanBase 0.2版本并无显著的差异。
3)OceanBase 0.4版本(2012年)
随着收藏夹业务的成功,很快咱们接到了更多新的业务,淘宝直通车是一个面向商家的业务,也面临着分库分表难以解决的问题。首先淘宝直通车的数据量愈来愈大,单库难以支撑,同时它又是一个OLAP类型的业务,有不少多表间的关联查询,每张表的维度又各不相同,没法统一按照用户id进行拆分。对于OceanBase的扩展性、高可用以及低成本业务都很满意,可是接口使用确实是太痛苦了。那么问题来了,什么是最好的接口语言?对于编程来讲,可能不一样的语言都有不一样的拥趸,但对于数据操做来讲,咱们认为SQL必定是最好的语言。对于简单的KV查询,你可能会以为SQL过于沉重了,但当你的业务慢慢复杂起来后,SQL必定是使用最简单轻便的。
在OceanBase 0.4版本,咱们对SQL有了初步的支持,用户可使用标准SQL来访问OceanBase,支持简单的增删改查以及关联查询,但对SQL的支持并不完整。同时OceanBase 0.4版本也是咱们最后一个开源版本。
对比OceanBase 0.4版本和OceanBase 0.2版本的架构,在OceanBase 0.4版本咱们最终补上了易用性的白板,开始慢慢有了一个标准分布式数据库的样子。
4)OceanBase 0.5版本(2014年)
2012年末的时候,OceanBase团队来到了支付宝。当时支付宝面临着全面去掉IOE的强烈需求,IOE的成本过高了,但PC服务器的稳定性难以和高端存储相比,若是咱们使用MySQL这样的开源数据库基于PC服务器进行替代,业务就面临着可能会丢数据的潜在风险。当时基于MySQL的容灾方案仍然只是主备同步,对于支付宝交易支付这样的核心系统来讲,丢失一笔订单形成的损失难以估量。业务对数据库的强一致和高可用提出了更高的要求,也使得咱们搭建了OceanBase 0.5版本的新一代架构。
在OceanBase 0.5版本中,咱们引入了Paxos一致性协议,经过多数派选举来保障单点故障下的数据一致性。通常状况下,OceanBase 0.5版本的部署模式会是三副本,当有一个副本出现问题时,另外两个副本会补齐日志并从新选出一个主提供服务,咱们能够作到单点故障下不丢失任何数据,同时故障恢复时间小于30s。同时为了更好地支撑业务,在OceanBase 0.5版本中,咱们全面兼容了MySQL协议,支持了二级索引并有了基于规则的执行计划,用户能够用MySQL的客户端来无缝链接OceanBase,能够像使用MySQL同样来使用OceanBase。
对比OceanBase 0.4版本和OceanBase 0.5版本的架构,咱们发现OceanBase 0.5版本基于Paxos,有了更强的高可用以及强一致,基于SQL有了更好的易用性,但代价是从两副本变成三副本,系统成本进一步增长了50%。
5)OceanBase 1.0版本(2016年)
在OceanBase 0.5版本的架构下,业务对强一致、高可用以及易用性的需求都获得了很好的支持,痛点慢慢集中在扩展性和成本上。随着用户写入量的不断增加,UpdateServer的写入单点老是会成为瓶颈,同时三副本也带来了太高的成本消耗。OceanBase 1.0版本带来了全新的架构,重点解决了扩展性和成本的痛点。
在OceanBase 1.0版本中,咱们支持了多点写入,从架构上将UpdateServer、ChunkServer、MergeServer和RootServer都合并为一个OBServer,每个OBServer均可以承担读写,总体架构更加优雅,运维部署也更加简单。一张表能够被划分为多个分区,不一样分区能够散布在不一样的OBServer上,用户的读写请求经过一层代理OBProxy路由到具体的OBServer上进行执行。对于每一个分区都仍然经过Paxos协议作三副本高可用,当有一台OBServer出现故障时,这台OBServer上的分区会自动切到其余包含对应分区的OBServer上提供服务。
在成本方面,咱们注意到在Paxos协议中,须要三副本同步的只是日志,日志须要写三份,可是数据并非,和数据相比日志量老是小的。若是将日志和数据分开,咱们就可使用两副本的存储开销实现三副本高可用。在OceanBase 1.0版本中,咱们将副本分为两种类型:全功能副本和日志副本,其中全功能副本既包含数据也包含日志,提供完整的用户读写;日志副本只包含日志,只进行Paxos投票。
同时在OceanBase 1.0版本中,咱们引入了多租户的概念,在同一个OceanBase集群中,能够支持多个不一样的租户,这些租户共享整个集群资源,OceanBase会对不一样租户的CPU、内存、IO及磁盘使用进行资源隔离。租户能够根据本身的须要配置不一样的资源容量,集群会根据不一样OBServer的负载作动态的负载均衡。这使得咱们能够把不少个小租户部署到同一个大集群中来,下降总体的系统成本。
和OceanBase 0.5版本相比,OceanBase 1.0版本的扩展性有了大幅提高,并且因为引入了日志副本和多租户技术,在成本上有了大幅下降;但扩展性的提高不是没有代价的,多点写入带来了巨大的复杂性。首先用户的写入并不必定会只写单个分区,对于多个分区的写入不可避免会带来分布式事务,咱们使用两阶段提交协议来完成分布式事务。其次在一个分布式系统中获取一个全局单调递增的时间戳是极其困难的,因为没有全局时钟,咱们基于局部时间戳作单分区内的读写并发控制,这使得系统的一致性有了必定的限制,虽然单分区的查询仍然是强一致的,但跨分区的查询没法保证读的强一致,这对于用户而言会是一个不小的限制。同时因为分区的关系,二级索引成为了分区内的局部索引,这要求索引键中必定须要包含分区键,没法支持全局的惟一索引,这对于用户使用也形成了必定的不便。
6)OceanBase 2.0版本(2018年)
OceanBase 2.0版本的外部总体架构与OceanBase 1.0版本没有太大差异,仍然是一个share nothing的三副本架构,但在内部咱们对扩展性、高可用、一致性、低成本和易用性都作了极大的提高。
在扩展性方面,咱们实现了分区分裂的功能。在建表的时候,用户对合适的分区数可能没有很好的估计,当分区过大时,能够经过分区分裂来使得分区数变多。尽管分区分裂是一个比较重的DDL操做,在OceanBase 2.0版本中,分区分裂是能够在线进行的,对用户的正常业务读写并不会形成太大影响。
在高可用方面,咱们支持了主备库功能,对于某些只有双机房的用户,能够在机房内经过三副本作机房内的无损容灾,经过主备库作跨机房的有损容灾。
在一致性方面,咱们支持了全局快照,真正意义上实现了分布式读写下的强一致。基于全局快照,咱们也完成了对全局索引以及外键的支持。
在低成本方面,咱们在事务层支持了TableGroup,容许把一组相近的表“绑定”在一块儿,减小分布式事务的开销。在存储层引入了数据编码,经过字典、RLE、Const、差值、列间等值、列间前缀等算法进一步压缩存储空间的占用,而且对于数据的编码是自适应的,会根据数据特征来自动选择合适的编码算法。
在易用性方面,咱们支持了Oracle租户,容许用户在同一套ObServer集群中同时使用MySQL租户与Oracle租户,而且支持存储过程、窗口函数、层次查询、表达式索引、全文索引、ACS、SPM、回收站等功能。
尽管今天的OceanBase 2.0版本在扩展性、高可用、一致性、低成本以及易用性方面作到了更好的平衡,但这样的架构并非一蹴而就的,从OceanBase 0.1版本到OceanBase 2.0版本的发展历程来看,OceanBase的架构老是在一直进化,为了可以更好地服务于业务,不少事物老是面临着许多权衡取舍,一项特性的提高会以其余特性的下降为代价。架构的优化演进没有终点,将来为了更好知足业务的需求,OceanBase的架构还会不断进行演化。
做者介绍:赵裕众(花名陈群),蚂蚁金服高级技术专家,目前在OceanBase团队负责存储相关的开发工做。2010年于中国科学技术大学得到计算机博士学位,同年加入支付宝从事分布式事务框架的研发,2013年加入OceanBase团队。
本文做者:赵裕众
本文为云栖社区原创内容,未经容许不得转载。