OceanBase架构介绍OceanBase是阿里巴巴集团研发的可扩展的关系数据库,实现了数千亿条记录、数百TB数据上的跨行跨表事务,截止到2012年8月为止,支持了收藏夹、直通车报表、天猫评价等OLTP和OLAP在线业务,线上数据量已经超过一千亿条。从模块划分的角度看,OceanBase可以划分为四个模块:主控服务器RootServer、更新服务器UpdateServer、基准数据服务器ChunkServer以及合并服务器MergeServer。OceanBase系统内部按照时间线将数据划分为基准数据和增量数据,基准数据是只读的,所有的修改更新到增量数据中,系统内部通过合并操作定期将增量数据融合到基准数据中。OceanBase数据库最初是为了解决阿里巴巴集团旗下的淘宝网的大规模数据问题而诞生的,淘宝网的数据规模及其访问量对关系数据库提出了很大挑战:数百亿条的记录、数十TB的数据、数万TPS、数十万QPS让传统的关系数据库不堪重负,单纯的硬件升级已经无法使得问题得到解决,分库分表也并不总是凑效。下面来看一个实际的例子。淘宝收藏夹是淘宝线上应用之一,淘宝用户在其中保存自己感兴趣的宝贝(即商品,此外用户也可以收藏感兴趣的店铺)以便下次快速访问、对比和购买等,用户可以展示和编辑(添加/删除)自己的收藏。淘宝收藏夹数据库包含了收藏info表(一条一条的收藏信息)和收藏item表(被收藏的宝贝和店铺)等:收藏info表保存收藏信息条目,数百亿条收藏item表保存收藏的宝贝和店铺的详细信息,数十亿条热门宝贝可能被多达数十万买家收藏每个用户可以收藏千个宝贝宝贝的价格、收藏人气等信息随时变化如果用户选择按宝贝价格排序后展示,那么数据库需要从收藏item表中读取收藏的宝贝的价格等最新信息,然后进行排序处理。如果用户的收藏条目比较多(例如4000条),那么查询对应的item的时间会较长:假设如果平均每条item查询时间是5ms,则4000条的查询时间可能达到20s,若果真如此,则用户体验会很差。如果把收藏的宝贝的详细信息实时冗余到收藏info表,则上述查询收藏item表的操作就不再需要了。但是,由于许多热门商品可能有几千到几十万人收藏,这些热门商品的价格等信息的变动可能导致收藏info表的大量修改,并压垮数据库。为此,淘宝需要研发适合互联网规模的分布式数据库,这个数据库不仅要能够解决收藏夹面临的业务挑战,还要能够做到可扩展、低成本、易用,并能够应用到更多的业务场景。为此,淘宝研发了千亿级海量数据库OceanBase,并且已经于2011年8月底开源()。虽然距离OceanBase开源已经超过一年多的时间,但OceanBase系统还有很多的问题,其中以易用性和可运维性最为严重。OceanBase团队一直在不断完善着系统,同时,我们也很乐意把设计开发过程中的一些经验分享出来。OceanBase的目标是支持数百TB的数据量以及数十万TPS、数百万QPS的访问量,无论是数据量还是访问量,即使采用非常昂贵的小型机甚至是大型机,单台关系数据库系统都无法承受。一种常见的做法是根据业务特点对数据库进行水平拆分,通常的做法是根据某个业务字段,通常取用户编号,哈希后取模,根据取模的结果将数据分布到不同的数据库服务器上,客户端请求通过数据库中间层路由到不同的分区。这种方式目前还存在一定的弊端:SignupSignupSigninSigninHomePagesWikiHistoryGitAccessalibaba/oceanbasePUBLICCodeCodeNetworkNetworkPullRequestsPullRequestsIssuesIssuesWikiWikiGraphsGraphs0011PageHistoryPageHistory1.1背景简介1.2设计思路ExploreExploreFeaturesFeaturesBlogBlogStarStar225ForkFork86SearchortypeacommandThisrepositoryThisrepository第一,数据和负载增加后添加机器的操作比较复杂,往往需要人工介入;第二,有些范围查询需要访问几乎所有的分区,例如,按照用户编号分区,查询收藏了一个商品的所有用户需要访问所有的分区;第三,目前广泛使用的关系数据库存储引擎都是针对机械硬盘的特点设计的,不能够完全发挥新硬件(SSD)的能力。另外一种做法是参考分布式表格系统的做法,例如GoogleBigtable系统,将大表划分为几万、几十万甚至几百万个子表,子表之间按照主键有序,如果某台服务器发生故障,它上面服务的数据能够在很短的时间内自动迁移到集群中所有的其它服务器。这种方式解决了可扩展性的问题,少量突发的服务器故障或者增加服务器对使用者基本是透明的,能够轻松应对促销或者热点事件等突发流量增长。另外,由于子表是按照主键有序分布的,很好地解决了范围查询的问题。万事有其利必有一弊,分布式表格系统虽然解决了可扩展性问题,但往往无法支持事务,例如Bigtable只支持单行事务,针对同一个user_id下的多条记录的操作都无法保证原子性。而OceanBase希望能够支持跨行跨表事务,这样使用起来会比较方便。最直接的做法是在Bigtable开源实现(如HBase或者Hypertable)的基础上引入两阶段提交(Two-phaseCommit)协议支持分布式事务,这种思路在Google的Percolator系统中得到了体现。然而,Percolator系统中事务的平均响应时间达到2~5秒,只能应用在类似网页建库这样的半线上业务中。另外,Bigtable的开源实现也不够成熟,单台服务器能够支持的数据量有限,单个请求的最大响应时间很难得到保证,机器故障等异常处理机制也有很多比较严重的问题。总体上看,这种做法的工作量和难度超出了项目组的承受能力,因此,我们需要根据业务特点做一些定制。通过分析,我们发现,虽然在线业务的数据量十分庞大,例如几十亿条、上百亿条甚至更多记录,但最近一段时间(例如一天)的修改量往往并不多,通常不超过几千万条到几亿条,因此,OceanBase决定采用单台更新服务器来记录最近一段时间的修改增量,而以前的数据保持不变,称为基准数据。基准数据以类似分布式文件系统的方式存储于多台基准数据服务器中,每次查询都需要把基准数据和增量数据融合后返回给客户端。这样,写事务都集中在单台更新服务器上,避免了复杂的分布式事务,高效地实现了跨行跨表事务;另外,更新服务器上的修改增量能够定期分发到多台基准数据服务器中,避免成为瓶颈,实现了良好的扩展性。当然,单台更新服务器的处理能力总是有一定的限制。因此,更新服务器的硬件配置相对较好,如内存较大、网卡及CPU较好;另外,最近一段时间的更新操作往往总是能够存放在内存中,在软件层面也针对这种场景做了大量的优化。如图1-1,OceanBase有如下几个部分组成:客户端:用户使用OceanBase的方式和Mysql数据库完全相同,支持JDBC、C客户端访问,等等。基于Mysql数据库开发的应用程序、工具能够直接迁移到OceanBase。RootServer:管理集群中的所有服务器,tablet数据分布以及副本管理。RootServer一般为一主一备,主备之间数据强同步。UpdateServer:存储OceanBase系统的增量更新数据。UpdateServer一般为一主一备,主备之间可以配置不同的同步模式。部署时,UpdateServer进程和RootServer进程往往共用物理服务器。ChunkServer:存储OceanBase系统的基准数据。基准数据一般存储两份或者三份,可配置。1.3系统架构1.3.1整体架构图MergeServer:接收并解析用户的SQL请求,经过词法分析、语法分析、查询优化等一系列操作后转发给相应的ChunkServer或者UpdateServer。如果请求的数据分布在多台ChunkServer上,MergeServer还需要对多台ChunkServer返回的结果进行合并。客户端和MergeServer之间采用原生的Mysql通信协议,Mysql客户端可以直接访问MergeServer。如图1-2,OceanBase支持部署多个机房,每个机房部署一个包含RootServer、MergeServer、ChunkServer以及UpdateServer的完整OceanBase集群,每个集群由各自的RootServer负责数据划分、负载均衡,集群服务器管理等操作,集群之间数据同步通过主集群的主UpdateServer往备集群同步增量更新操作日志实现。客户端配置了多个集群的RootServer地址列表,使用者可以设置每个集群的流量分配比例,客户端根据这个比例将读写操作发往不同的集群。OceanBase客户端与MergeServer通信,目前主要支持如下几种客户端:1.Mysql客户端:MergeServer兼容Mysql协议,Mysql客户端及相关工具(如Java数据库访问方式JDBC)只需要将服务器的地址设置为任意一台MergeServer的地址就可以直接使用。2.Java客户端:OceanBase内部部署了多台MergeServer,Java客户端提供对Mysql标准JDBCDriver的封装,并提供流量分配、负载均衡、MergeServer异常处理等功能。简单来讲,Java客户端首先按照一定的策略定位到某台MergeServer,接着调用MysqlJDBCDriver往这台MergeServer发送读写请求。Java客户端实现符合JDBC标准,能够支持Spring、iBatis等Java编程框架。3.C客户端:OceanBaseC客户端的功能和Java客户端类似。它首先按照一定的策略定位到某台MergeServer,接着调用Mysql标准C客户端往这台MergeServer发送读写请求。C客户端的接口和Mysql标准C客户端接口完全相同,因此,能够通过LD_PRELOAD的方式将应用程序依赖的Mysql标准C客户端替换为OceanBaseC客户端,而无需修改应用程序的代码。OceanBase集群有多台MergeServer,这些MergeServer的服务器地址存储在OceanBase服务器端的系统表(与Oracle的系统表类似,存储OceanBase系统的元数据)内。OceanBaseJava/C客户端首先请求服务器端获取MergeServer地址列表,接着按照一定的策略将读写请求发送给某台MergeServer,并负责对出现故障的MergeServer进行容错处理。Java/C客户端访问OceanBase的流程大致如下:1)请求RootServer获取集群中MergeServer的地址列表。2)按照一定的策略选择某台MergeServer发送读写请求。客户端与MergeServer之间的通信协议兼容原生的Mysql协议,因此,只需要调用MysqlJDBCDriver或者MysqlC客户端这样的标准库即可。客户端支持的策略主要有两种:随机以及一致性哈希。一致性哈希的主要目的是将相同的SQL请求发送到同一台MergeServer,方便MergeServer对查询结果进行缓存。1.3.2客户端3)如果请求MergeServer失败,则从MergeServer列表中重新选择一台MergeServer重试;如果请求某台MergeServer失败超过一定的次数,将这台MergeServer加入黑名单并从MergeServer列表中删除。另外,客户端会定期请求RootServer更新MergeServer地址列表。如果OceanBase部署多个集群,客户端还需要处理多个集群的流量分配问题。使用者可以设置多个集群之间的流量分配比例,客户端获取到流量分配比例后,按照这个比例将请求发送到不同的集群。OceanBase程序升级版本