蚂蚁金服庆涛:OceanBase支撑2135亿成交额背后的技术原理

演讲嘉宾简介:梅庆(花名:庆涛)

现任蚂蚁金服 OceanBase 团队技术专家,曾经支持过阿里云数据库和天猫双 11 大促业务,在分布式数据库的开发和架构上有着丰富的经验。目前主要从事 OceanBase 对外输出的解决方案设计和技术推广工作。

本次直播视频精彩回顾,戳这里!以下内容根据演讲嘉宾视频分享以及PPT整理而成。本次的分享主要围绕以下三个方面:

  1. OceanBase基础概念
  2. OceanBase分布式设计
  3. OceanBase性能调优

一、OceanBase基础概念

集群
OceanBase是一个通用的分布式关系型数据库,也是蚂蚁自主研发的数据库,它是以集群形式呈现的。从外观上来说,以三副本为例,它分为三个区域(Zone),三个区域(Zone)放在三个机房是最好的选择。OceanBase集群的所有服务器是都普通的商用服务器,安装RedHat或者CentOS的Linux系统,生产环境用普通的SSD盘。除此之外,OceanBase集群并不依赖共享存储和光纤设备。

集群内部结构
下图细化了上面集群的内部架构,9台机器的角色基本相同。OceanBase数据库集群搭建非常便捷,每个机器只需要安装OBServer软件。它是一个单进程的程序,内部分为几个模块,SQL引擎,存储引擎以及总控服务(可选)。总控服务主要职责是负责整个集群的元数据管理和集群内部的调度管理。总控服务只需要在每个区域(Zone)的一台机器上出现就可以,一共三个,其中一台上面总控服务提供服务,另外两台机器上的是它的副本,这是为了保证当总控服务不可用时可以迅速另外两台机器上选举一个新的总控服务提供服务。下图还可以看到数据存在分区(Partition)内,可以通过存储引擎访问分区(Partition)。因为数据有三份,即分区(Partition)也有三份。三份数据必定会分在三个不同区域(Zone)里面,不会在同一个区域(Zone)或同一个OBServer中。OBServer还有个特点。当OBServer进程在一台机器上跑起来之后,会把机器上大部分的资源(如CPU、内存和磁盘空间)据为己有。这是为了将集群资源聚合在一起,成为一个大的资源池,再进行资源分配,做多租户的管理。

多租户管理资源
资源规格(ResourceConfig)。租户是资源的抽象,整个集群可能有几百个CPU,几TB的内存,几十TB的空间。一个应用在最开始上线时并不需要如此大的内存,可以分出一部分资源给租户。分配资源前先定义资源规格,资源规格定义CPU的数量,内存的大小等等。定义资源规格后,创建资源池(resource pool),这时需要说明要用哪个规格以及数量。命令执行完之后就是真正把集群里面的资源分出一部分了。

租户(Tenant)。资源池分配之后还要关联到租户才可以被使用。租户需要新建。租户的概念与传统数据库里的实例是一样的。一个租户给到研发人员可以理解为他拿到了一个数据库实例。下图右边是资源的抽象图,最大的框是集群资源的抽象,租户是从大资源池里面挖出一块,租户的大小各有不同,取决于不同的资源规格,这与酒店的房间规格是同理。如果租户的规格在开始定好之后,后面调大小也是非常方便的。研发同学拿到租户之后便可以在里面创建数据库。虽然目前OceanBase的实例与MySQL很相似,但是它绝不是MySQL,你可以看到它多了一个数据库叫OceanBase。同样,可以创建很多用户数据库,数据库中可以建表。在OceanBase里一个普通的表就是一个分区(Partition),一个分区表可以包含很多分区。

Unit均衡。下图是研发同学拿到租户之后可以看到的视图,但是他看不到数据在哪台机器上(也不需要知道)。这张图从另外的角度解释了资源是如何分配的,resource pool创建后,每个区域(Zone)里面分配出两个同样大小的资源规格(因为unit_num=2)。那绿色的Unit应该从哪台机器中选择,下图中每个区域(Zone)有四台机器,OceanBase会选一个比较空闲的机器。这里会涉及到负载均衡的概念,OceanBase在资源分配时(创建Unit时)会尽量维持各个机器的利用率保持均衡。租户是资源的抽象,与实际的机器是解耦的,运维人员不需要关心数据具体在哪台机器上,只需要保证机器上有空余的资源就可以。另外,租户的设计机制里Unit的分布和实际机器解耦,所以租户有着很好的弹性伸缩能力。

分区(Partition)

表分组(Tablegroup)。研发同学拿到租户之后会开始建表,OceanBase中非常重要的概念是分区(Partition)。分区是数据的最小粒度,它可以是一个普通表或者分区表的一个分区,它在租户的Unit里分配。当该租户的资源池(Resource Pool)在该Zone里有多个Unit的时候,创建分区时该选择哪个Unit分配呢,这就是OceanBase负载均衡机制第二个场景。默认的策略是尽可能的让每个Unit资源使用率做到均衡。分区不能跨节点,只能在一个Unit内部,但是分区表的多个分区是可以在不同Unit内部。这是OceanBase分布式的一个特点。有些业务会比较关心是不是在一个机器里,如果业务上主表和子表要做连接(Join)的数据分布在不同的节点,那么这个查询的性能就不是最好。最好的情况是在同一台机器,甚至同一块内存里面。这里OceanBase提供的策略是允许设定两个表有关系,这样底层分配分区位置时会把有关系的表的分区聚合在一个Unit里,这个策略就是通过表分组(Tablegroup)设定,类似与Hadoop里面的Tablefamily,设计思想是一样的。下图把几个表的Tablegroup设为同一个,Tablegroup还可以细分为Partitiongroup,一张分区表有两个分区(Partition)的话,Tablegroup会分为两个Partitiongroup,0号Partitiongroup和1号Partitiongroup。

分区组(Partitiongroup)。下图橘×××虚线框是Tablegroup,黑色虚线框是Partitiongroup。Partitiongroup的作用是将这些分区(Partition)聚集在同一台机器(同一个Unit)里面,确保分区(Partition)不跨节点。虽然一个分区(Partition)不跨节点,但是分区表的不同的分区(Partition)是可以跨机器的。所以当一张表的容量在一台机器上放不下的时候可以设分区(Partition)表,这样便可以分在不同的机器上面。

值得注意的是,分区(Partition)还是数据迁移的最小单元。分布式系统默认不控制分区(Partition)的分布,可能导致机器资源利用率不均衡,OceanBase可能会把分区(Partition)从一个Unit里面挪到另外Unit中,或者把一个Unit整体搬迁到另外一个机器内部。迁移数据时以分区(Partition)为单位,这种数据迁移完全是内部的逻辑,并且是在线迁移对业务读写影响很小。

三副本

下图是三个节点的集群,三个机房是IDC1,IDC2,IDC3,每个区域(Zone)里面有两台机器,Observer1-6。称为222的集群结构。P1,2,…指的是分区(Partition),且每个分区都有三份,每份称为一个副本,内容都是一样的,业务应该访问哪个副本呢?三副本有一个Leader副本和两个Follower副本。粉红色代表Leader副本,默认情况下业务会读写Leader副本。我们通常不叫主副本,因为OceanBase的Leader副本和Follower副本和传统的主备的概念不完全一样。每台机器里面,既有Leader也有Follower,所以无法说哪台机器是主,哪台机器是备,6台机器都提供了访问。对业务来说,不知道要访问的数据的leader副本在哪台机器上。这个通常靠OBProxy解决,它是分区(Partition)的反向代理,OBProxy知道业务要访问的数据的Leader副本位置,业务只要访问 OBProxy就可以了。

分区(Partition)还是高可用的最小单元。如果有一台机器挂了,传统场景下运维人员要把备库切换为主库。但是在OceanBase场景下并没有传统的主备的概念,机器挂了之后只有其中的Leader副本访问受影响,Follower副本并不受影响(因为本来就不提供服务)。Leader副本不可访问时,OceanBase会很快从其他两个Follower副本中选举出一个新的Leader继续提供服务。

三副本作用

1.三副本强一致性。三副本可以做到Leader副本上的每一个修改在Commit的时候会同步到Follower副本上。正常情况下,传统系统下业务要修改数据,在事务提交时事务日志(Redo)需要落盘,OceanBase也同理,修改数据要生成事务日志然后再修改数据。提交时,事务日志除了本地要持久化一份,还会在其他Follower副本上也会持久化一份。传统一主两备强同步时,两备中只要其中一个接受事务日志并持久化,那么主库上面的提交就可以返回。OceanBase则选择了另外一种策略,每个分区里有三个副本,三个副本中有一半以上的成员成功接受并持久化事务日志之后,Leader就会认为这份事务日志是可靠的,便直接可以返回。两个Follower接受事务日志时会有先后顺序,不需要等待所有的副本都确认成功,最终都是要成功的。这个策略,性能还算可以,事务日志可靠性也得到了保障。

2.无需人为处理机器故障。当Leader不可用时重新选取新的Leader,而且以每个Partition为单位,每个Partition独立选择Leader,选出来时间大概在14-20秒之间。这种选取是自动的,不需要运维人员或外部工具介入。应用会通过OBproxy感知到Leader的切换,所以业务部分也不需要改连接字符串。整体效果上,OceanBase集群里面任何一台机器宕机时都不需要人去处理。

SQL兼容性

数据类型。OceanBase在起初兼容的是MySQL的连接协议,它实现了MySQL大部分的语法,也支持不同的数据类型。但是兼容MySQL并不是OceanBase主要目的,其主要目的是兼容Oracle。

SQL层功能。目前OceanBase支持Oracle的增删改查,内部也已经实现了存储过程,窗口函数,层次查询。DB link和外键还在开发过程中。

事务层。OceanBase支持Read committed的隔离级别,正在做序列化隔离级别(Serializable),以及flashback的功能,另外OceanBase也支持分布式事务(XA协议)。

内部视图。OceanBase实现了Oracle中的大部分内部视图,包括部分ALL_/DBA_/USER_视图。索引支持全局索引,函数索引。

分区(Partition)。OceanBase支持一级分区(Partition)和二级分区(Partition)。由于分区(Partition)组合的类型有很多,现在正在完善中,其中hash分区(Partition),range分区(Partition)和list分区(Partition)现在都是支持的。

伪列。 Oracle中有比较好的伪列,如rownum,sequence和virtual column等伪列OceanBase也正在开发。

存储过程。这是最重要一点,很多传统业务都是在存储过程上面写,如果要换OceanBase,可能不太想去改他们的存储过程。客户端可以连接OceanBase,一种方法是通过OBproxy,还有一种是Java程序可以提供Java驱动。

二、OceanBase分布式设计

拆分设计
一般做分布式设计,都会思考要不要做拆分,从什么纬度拆分。拆分方法有几种途径。

1.垂直拆分。一个大的业务一般在一个库上面,垂直拆分是按照业务模块,将不同模块放到不同的租户里面。

2.水平拆分。水平拆分有以下几种方式。

分库分表。分库分表它通过中间件做拆分,能够把业务上的表拆分到多个相同的物理结构中,从业务上看是在一个表Order里,但是数据库里面是Order00,01...等多个物理表。中间件解决了SQL路由问题,数据的位置可以通过中间件得知,事先要按拆分件的条件通知中间件数据位置。假设SQL里没有拆分,中间件无法得知数据的去向,那它就会选择从所有的物理表中寻找,这时性能便会大打折扣。

分区表。如下图,Order00是存储在数据库里面的表,OceanBase在存储的时候会将表分为很多的小的Partition,然后Partition会分到不同机器上面。OceanBase选择了分区表的方法,它的好处是业务可以控制拆分策略,可以决定按照什么纬度拆分。

存储层面按定长块拆分。首先定义一张大表,然后在存储级别按照固定大小的块,切分很多小块,将小块分到不同机器上面存储。按这种拆分方式的话业务是完全透明的,其好处是业务不需要关心产品规格,但坏处是由于不知道数据位置,后续会有很多跨机器的访问。

Locality设置

由于分布式数据库的分区(Partition)是随机的,但从业务层面考虑是希望能够控制分区(Partition)的分布。OceanBase也提供了一些策略来控制分区(Partition)的分布。

表分组Tablegroup。第一种策略是通过表分组Tablegroup,在建表时添加Tablegroup属性,在不同表有关联时将它们设到同一个Tablegroup中。这样再下一层同号分区会在同一个PartitionGroup中,被约束在同一个Unit内部。

租户的分组(TenantGroup)。更大范围的控制就是租户的分组(TenantGroup)。当业务量很大时,首先做垂直拆分,划分为不同业务,不同业务分到不同租户中。但在业务流程中,某些业务是有关系的,所以希望相关的业务能够分在同一个机房内,通过租户的分组便可以将所有业务的请求同在一个机房内完成。

Primary Zone。还有一种方法是设置不同业务的Primary Zone,Primary Zone可以控制Leader副本在哪个Zone里面。

Locality详细设置。下图最后一条命令是Locality从上到下的详细设置,其实用户可以不用如此详细。其中最大范围是租户Tenant,之后是数据库,再下面是表。如果对租户加了设置,数据库和表可以不加设置,它们可以继承上层的设置属性。OceanBase里面的Locality的概念主要用来设计单元化访问,规避分布式事务以及跨地域SQL请求。

异地多活

异地多活是传统数据库中也会提到的概念。下图中五条异地多活形式,从上到下难度变得越来越高。

•第一个是应用双活,双击访问,由于应用是无状态的,其中给每个机房部署称为应用双活。但数据一边可读写,另外一边不可读写,这就是主备架构,无法做到两边都是主库,这种称为备份容灾。

•第二个是应用双活,数据库还是一边读写,但是另外一边可以开一个只读库,如Oracle的active dataguard,做多个备户,在另外的机房把主户打开。这种称为读写分离。

•第三个是应用双活,数据库多活,同时读写不同表。两地机房都可以提供写入,单从业务层面看是双写,但是写的是不同的表,如此而来写的数据便不会有冲突。

•第四个是应用双活,数据库多活,同时读写相同表。但是写的记录不同,这种多活采取了错开写的方式。

•最后一种形态是两边写相同的记录,出现冲突的时候会报错。虽然设计时可以这样写,但是不可避免的会有数据冲突,这时要舍弃一方的写入。

OceanBase做到了第三种错开写,借助分库分表的方式,将业务的数据拆分到不同表中,不同的表在两边提供写入,如1号表在A机房写,2号表在B机房写。

单元化指的是应用本地读写数据,但是更高的要求是两边机房要同时本地写。没有跨地域的请求,即自封闭。

单元化

下图是三地五中心的多活容灾解决方案,其中数据有五份,这个解决方案可以做到五个机房的应用同时写五个机房的数据。其中应用无状态,每个机房也都有数据,但是写入点是不同的,走的是不同链路,这时需要依靠应用和数据库的结合,要让应用层的流量拆分规则和数据层的拆分规则保持一致。这是理解阿里和蚂蚁单元化的非常关键的地方。OceanBase可以干预数据的拆分规则,可以设Leader副本分布在什么位置。数据之间保持同步是数据库内的行为,不需要外部的产品去做,所以不需要担心数据丢失和数据一致性的问题,出现问题时也不需要担心可用性的问题。

三、OceanBase性能调优

与Oracle相似,OceanBase的SQL执行计划一样用软解析,硬解析等。OceanBase支持执行计划及缓存,软解析以及各种join语法.

另外,OceanBase支持非常复杂的Hints,如改变表连接顺序的Hints,索引相关的Hints和调试的语句的Hints。在调试SQL的性能时,会有一些Hints必须调试。以及包括并行,SQL改写之类的Hints。

Outline是Oracle特有的东西,它的作用是可以在线改变SQL执行计划。如果SQL执行的计划有问题,需要在线定义Outline来改变执行计划,包括在前期上线前做测试时将执行计划固定住,之后把执行计划迁到线上。Outline更好的应用是做SQL限流,假设SQL业务上某个SQL性能非常不好,便可以通过Outline限制SQL的并行数量。

一般OceanBase性能调优有两个策略.第一个是SQL响应时间调优,包括通过优化访问路径,优化排序或聚合操作,优化分区(Partition)裁剪,调整查询并行度以及优化连接等策略。如果SQL响应时间调优方法达不到优化目的,就需要调数据库吞吐量,主要是通过优化SQL的流量分布,优化分区(Partition)的均衡分布。

点击 阅读更多 查看更多详情

posted @ 2019-01-21 16:20  蚂蚁金服技术  阅读(2048)  评论(0编辑  收藏  举报