数据库选型用 OceanBase 的原因很多,其中一类是原数据库确实遇到难解的问题或瓶颈,需要采用分布式数据库技术来脱困。这类客户以运营商、互联网客户居多。本文就是总结这类客户业务在 OceanBase 上的实践经验。


原数据库问题以及应对策略

连接问题分析

这一类业务原数据库一般是 ORACLE 数据库。当业务量增长,应用规模变多,创建的数据库连接也会很多,导致 DBA 不能不出规定限制一下应用的连接数。

连接数太多在 ORACLE 上会成为问题,主要原因还是每个用户连接是一个进程。任凭小机处理能力再强也经不起客户端连接数的任意增长。

在 OceanBase 里,连接数不是问题。每个用户连接是一个线程。用户连接发起的请求会被放到一个内部请求池里,由内部工作线程去处理。内部工作线程数量并不大,跟实例资源的 CPU 规格有关系,默认是实例 CPU 的 10 倍。此外,在 OceanBase 集群前端还有个反向代理 OBPROXY (生产环境,在反向代理 OBPROXY 前面还有个 负载均衡 F5 或者 LVS等产品,不影响结论,这里略去不提)。应用实际上是跟 OBPROXY 建立连接,每个连接也是一个线程。每个 OBPROXY 默认可以创建 8000 个用户连接,这个参数可以调大到 6w ,会多消耗一些 CPU 。OBPROXY 只做 SQL 路由,不做运算,所以对 CPU 消耗也不是很大。资源充足的情况下,每个 OBPROXY 的 SQL路由能力测试下来是单核 2w次/秒。而且在实际部署中至少有 2个 OBPROXY,可以多个分担负载。

所以,在 OceanBase 数据库里,连接数很难成为首要问题或者瓶颈。

空间问题分析

这一类业务一般是运营商行业或者互联网行业有面向终端消费者业务的。用户量大,访问量大,导致业务积累了很多交易数据。

如果原数据库是 ORACLE 数据库,一般已经做了分区表,多是按时间分区(按月/周/日分区)。数据库大小通常在 十几T 或者 几十T 以上了。如果原数据库是 MySQL 数据库,大部分就是单表了(MySQL 分区表功能据说不好用),也有部分可能用了分库分表技术做了水平拆分。

数据量大的苦恼就是磁盘扩容成本越来越高。如果使用了存储,存储的容量也有上限。如果使用云主机产品,本地 SSD 数目和容量也有限,云盘(ESSD) 也有数量和容量的上限(比本地盘大)。只要业务在增长,触达瓶颈只是时间问题。实际上,当表容量非常大的时候,可能会先触达性能瓶颈。

在 OceanBase 里,数据存储时除了可以选择压缩算法外,还可以选择编码技术,极大的降低了数据的存储空间。在 OceanBase 的客户案例里,ORACLE 数据迁移到 OceanBase ,单副本空间可以做到之前的 1/5 。MySQL 业务案例里,近 20T 的 MySQL 数据在 OceanBase 里单副本实际存储空间大概 4.3 T 左右。而性能方面,通过编码技术节省存储空间这个数据并不需要解码操作,不消耗 CPU ,所以整体性能差异并不大。OceanBase 企业版默认就开启了高级压缩特性。有关这个压缩特性请参考《2.0 解析系列 | OceanBase 2.0的高级数据压缩特性》。

不过就空间运维而言,从 ORACLE MySQL 迁移到 OceanBase 后还是有点变化。OceanBase 目前只支持一个数据文件(名字叫 block_file
),其空间大小是预分配式的。默认取所在磁盘目录空间的 95% 。当然如果磁盘后期扩容了,这个文件大小上限也可以自动扩。不过对磁盘扩容不是 OceanBase 的标准玩法。

性能问题分析

在 ORACLE MYSQL 里,表数据量到了亿级别继续增长的话,性能上可能会变慢。其他维护操作(表结构变更、表备份恢复等)风险可能增加。

性能变慢也分读变慢和写变慢。

读查询通常能走索引,不过业务比较复杂,表上的索引也很多。尤其以单列索引居多。往往业务查询条件很多,只是部分字段能命中索引,然后还有大量的索引回表操作。随着数据量的增长,数据存储空间变大,回表的成本也在增长,性能会下降。部分场景下这些索引建的并不合适(单列索引值得商榷的空间最大)。写操作通常也跟索引有关系。update
 和 delete
 操作的条件要走索引,更新数据时还要更新索引数据。当表上索引数量很多(如超过10个)时,索引对数据更新的影响也是很明显的。

由于索引很多都是业务开发创建的,每个业务的开发只管自己的查询,并不一定能全盘考虑。索引是否最优是个未知数。但数据库性能变慢是所有人都能看懂的事实。于是传统数据库背锅,业务寄希望于分布式数据库能解决。

分布式数据库确实有很多办法应对这个挑战。

首先是通过水平拆分技术将一个大表的数据分散到多个实例多台机器上(多台机器是关键)。水平拆分技术有分库分表、分区表和存储切片三类技术。这里主要讨论 前两者。水平拆分技术原理详情可以参考《分布式数据库选型——数据水平拆分方案》。

分库分表技术将业务大表拆分多个分片,每个分片是一个实际存在的物理表。分区表拆分后的每一个分片是一个分区,是表的子集,不是分表。两种技术的产品都呈现给业务的是一个业务表。前者呈现的是逻辑表,后者呈现的是物理表。这是二者原理的区别。不同的分片相比原表,不仅数据量小,存储空间也小,很多小范围的数据查询和修改性能都会提升。此外,不同的分片如果落到不同的机器上,则可以将多台机器的存储能力和计算能力(CPU、内存和IO)都同时运用起来,总体处理能力也会提升。

不过,分布式除了带来好处,也带来挑战。那就是跨机的查询和事务。

在分库分表技术里,这个处理复杂度很高,只有少数几家厂商产品实现了这个功能。但是都有一个问题,就是数据强一致和读写性能方面要有取舍。数据强一致包含跨机的查询数据都满足MVCC、跨机的事务修改的数据要是强一致的(不能是最终一致性解决方案)。在 OceanBase 里,自 2.0版本后,跨机的分布式查询和分布式事务都支持(指满足强一致),性能方面也不差。强一致要求会对性能有损耗,损耗多少就是 SQL 引擎的基本功,OceanBase 始终把数据强一致放在首位。当然,平时大家听到的或者理解的强一致可能更多的是指机器故障前后数据不丢(RPO=0)那个。无论指哪个,这句话都是对的。

分库分表问题分析

也有些互联网业务已经探索了分库分表的水平拆分技术。但是由于业务数据量大、以及访问量大,也有很多痛点。

比如说:

  • 大表在线DDL导致从库延时很大。分库分表技术将一个大的DDL 拆分为多个分表的 DDL。表数据量大的时候,ONLINE DDL会导致MySQL 的从库出现很大延迟。

  • 大表在线 DDL 可能阻塞 DML。在 MySQL 的 Online DDL的开始和结束阶段,涉及到元数据的变更时会尝试加表锁,会阻塞 DML。此外有些 DDL还不支持 Online,也会阻塞 DML。

  • 大表在线 DDL 在机器故障后处理过程比较麻烦。当 DDL 进行中,有 MySQL 机器出现故障。首先会话要报错(DDL 部分成功部分失败),失败的 MySQL 要主从切换。主从切换时如果有 Binlog 丢失(异步同步),则还需要修复主从同步。然后还要在新主上重新开始 DDL 。

  • 小表广播故障时问题。广播表适合那些不能做拆分的配置表,但是会频繁的跟其他拆分过的分表做表连接,广播表会把数据通过Binlog复制技术应用到所有实例的分库中,这个同步是外部逻辑同步,其可靠性最高不超过 MySQL的Binlog复制。其麻烦也是在同步过程中出现机器故障后,处理过程比较麻烦。风险是故障后广播表多个副本数据不一致。

  • 大量分库拆分,导致MySQL实例和机器规模增长很快,运维成本增加。在淘宝内部业务,核心业务单机跑2个MySQL实例,其次跑4个、8个、16个也有。很多长尾业务都是小实例,每个机器上可能有几十个 MySQL 实例。其运维成本和风险会增加,当然厂商产品也会有自动化运维平台(DB PaaS 平台)。其麻烦依然是在出现机器故障后。只要有三四个实例要修复主从同步,DBA 半天的时间就过去了。

在 OceanBase 里,上面大部分问题会因为技术方案不同而有所改变。

  • 首先,OceanBase 每笔事务默认就是三副本强同步,Paxos协议会保证至少一半成员事务日志落盘了,这个事务才会成功。这个对主副本的事务提交性能会有制约。说通俗一点,主副本不能甩开备副本自由酣畅的跑了(MySQL “自由”的代价就是出问题的时候,备副本可能有延迟导致故障时主备不一致。)。此外,由于 OceanBase 事务日志同步粒度是分区级别,多个分区独立同步,延时的水平也很低。

  • 第二,OceanBase的三副本强同步的另外一个好处就是 OceanBase 的高可用能力会时刻处于可用状态。只要发生少数派机器故障,内部就切换(选举出新的主副本),并且OceanBase 保证数据跟故障前是绝对一致的(RPO=0)。故障机器恢复后,原来的DDL可以继续,不需要运维介入处理。也没有内部数据不一致。

  • 第三,OceanBase的数据存储模型是 LSM Tree。所以 OceanBase 里的 DDL 都是 Online DDL,不会阻塞 DML。当然,OceanBase 目前的缺陷也有,有很多 DDL 都不支持。比如说加主键、改列的类型(大类变换)等等。这个预计在 3.2 某个版本就会支持了。

  • 第四,不能拆分的配置表可以在 OceanBase 里配置为复制表,OceanBase会自动将复制表的变更数据同步到实例所在的所有机器(也可以指定机器范围)。这种同步是全同步策略,全部成功事务才提交,所以所有副本数据都是完全一致的。这种同步在机器故障的时候,会自动剔除故障节点,也不需要运维介入。故障机器恢复后,自动恢复数据同步,并自动恢复到全同步成员中。

  • 第五,OceanBase只要一个实例,实例里表用分区表就可以替换原分库分表架构。实例可以在线扩容和缩容,负载均衡和数据同步都是 OceanBase 内部逻辑,运维非常的“省心”。


OceanBase 分布式能力实践

OceanBase的分布式能力包含:高可用、多实例、多活、分区表、复制表。

高可用

高可用指的的分布式数据库 OceanBase 集群里有机器故障的时候,数据库访问服务在中断后能自动恢复。有些观点可能不把高可用能力当作分布式能力,但这个是OceanBase 最核心集群最基础的能力(不可剥夺)。这里就简单提一下,OceanBase的高可用能力就是少数派机器故障,自动切换(RTO~30s),数据不丢(RPO=0)。故障恢复后,数据同步自动恢复,非常的省心。

多实例

多实例就是多租户,实例更通俗一些。OceanBase 以集群形式部署,提供给业务的是实例。集群在每个机器上只有一个进程,实例是逻辑实例,对业务完全透明,体感跟单实例一样。实例提供原生的 SQL 能力和事务能力,分布式细节对使用者完全透明。

OceanBase 的多实例可以提高对机器资源的利用率,根据业务需求有多种使用方法。关于 OceanBase 多实例的部署形态可以参考前面文章《闲话 OB 部署架构实践》。其关键点就是 实例的主副本位置策略(PRIMARY_ZONE
)的设置。默认情况下设置为一个 zone
,以及 unit_num
 设置为 1 。业务压力大的实例,有很多分区表的实例,可以考虑 将 unit_num
 设置为 2 以及 PRIMARY_ZONE
 设置为多个 zone
,以提升实例的总体处理能力。

OceanBase 多实例之间有资源隔离(CPU和内存),数据完全隔离。不过现在也支持 DBLINK 联通不同 ORACLE 实例之间的数据。

多活能力

前文《闲话 OB 部署架构实践》里也解释了 OceanBase实例跟传统 ORACLE/MYSQL 实例的不同。OceanBase 可以很好的将集群的多台机器都提供服务,这就是多活能力。多实例和多活能力结合使用,将不同实例的压力在集群机器之间可以实现一定程度的错开。

在 OceanBase 里做读写分离也是很方便的。在三副本之外增加一个只读副本,就可以专门用于只读查询业务。OceanBase 保证只读副本的数据只会有延时,绝不会有错误。

分区表

OceanBase 分布式最核心能力之一就是分区表,这个概念跟 ORACLE 分区表是一样的。分区表详情可以参考《OceanBase分区表有什么不同?》。

分区表的用法就是选一个字段当作分区字段,按照某种策略(LIST\HASH\RANGE\二级分组组合)拆分表数据为多个分区。当业务表非常多的时候,比较麻烦的也是这个分区字段的选择。

精力有限的情况下,建议是抓住关键的表,有这么些策略选择表:

  • 业务主要流程的相关表。如订单业务,那就是订单表、订单历史表、会员表。支付业务就是支付订单表、支付历史表等等。每个表如果有通过业务外键关联的表都算上。

  • 业务数据量在 1 亿以上的大表。或者业务访问量(QPS)非常高的表。

  • 核心业务查询很慢的 SQL 对应的表。

分区字段选择

对于核心业务相关表,当表之间都遵循第三范式设计的时候,有两种做法。

一种做法就是加入冗余列(比如说会员id)。如果所有表数据都是围绕会员的,所有表都根据会员 ID 做 HASH 分区。这些相关的表就可以放到一个表分组中。这是最优的设计。

不过由于表很多的时候,老业务用户可能不想改动。那就退而求其次,选第二个方案。每个表独立的选择分区字段。对于主表,如果有自增 ID 做主键,简单一点就使用自增 iD 作为分区键。OceanBase目前版本(3.1以及以前)还不支持自增ID 作为分区键,就需要换一个业务字段。OceanBase 预计在 3.2 某个版本支持以自增 ID 作为分区键主键。更好的建议是用业务字段。因为业务查询带上业务字段的概率更高。使用相同分区字段的分区表,尽量保证分区数(分片数)是一致的。

不同表的分区字段不一样,关联查询的时候可能会出现 1:N 或者 M:N 这样的连接模型,这个在 OceanBase里很可能是跨机的请求,但是这种查询可以发挥多机并行能力,所以性能也比较好。

本地索引和全局索引

当分区表分区字段定了后,总会有很多查询是不带分区键的,建议优先用本地分区索引。有些人了解 OceanBase 分区表原理后,可能会觉得本地索引很慢,用全局索引会更好。实际上,用本地索引时,会扫描所有分区的本地索引,OceanBase可以在分区间并行,这个扫描也不会特别慢。当索引返回的数据量很少的时候,这个性能是很好的。

全局索引对少量数据查询场景是非常友好的。但是如果索引过滤数据不高返回很多数据的时候,全局索引的性能有可能会比本地索引性能还要差。因为全局索引是独立的分区,本地索引跟数据是在一个分区上的。查询时使用全局分区索引,在索引回表的时候很有可能是分布式查询,回表数据量很大的时候,这个性能会有所下降。当然,技术上OceanBase SQL引擎也是有一些优化去缓解这个影响。

此外,正如索引会降低数据修改(DML)的性能,全局索引对数据修改(DML)的影响会更明显一些。因为这个修改很可能就是一个跨机的分布式事务。这也是一个考虑因素。

索引是为最重要的查询场景服务的,其代价就是写入的性能,全局索引更是如此。如果一定要用,要控制数量,要控制使用场景一定是非常重要且必要的场景。要避免滥用索引包括全局索引。这就是业务开发建索引不靠谱的地方。重要表的索引的创建应该由专业的DBA来审核或者创建。

表分组

实例架构是  1-1-1  的时候,实例所在的每个机器上都拥有该实例的全部数据(分区)。再通过设置实例的 PRIMARY_ZONE为具体的值将实例的全部分区主副本约束在该区域(ZONE )的机器上。如果实例架构是 2-2-2 的时候,即使设置了 PRIMARY_ZONE ,全部主副本也是分散在某个区域的 2 台机器上。业务可能会有分布式查询或事务。表分组可以将分区位置控制策略细化到一组分区。 

表分组的详细使用介绍请参考《OCEANBASE TABLEGROUP 用法介绍》。通常有两种场景。

第一种简单一些,大部分表都是普通非分区表。根据业务属性将全部表划分为几个子集,每个子集里的表设置为一个独立的表分组。那么 OceanBase 的负载均衡策略会保证在一个表分组中的分区在稳定的时候在一个节点内部。这样同一个表分组内部的表之间的连接或者事务,都是本地连接或本地事务。这样性能最优。

二复杂一点,有很多分区表,并且很多分区表的分区策略相同(分区字段、分区数相同)。把这些表放到一个表分组里,它们的同号分区就形成一个分区组。OceanBase 的负载均衡策略会保证在一个分区组中的分区在稳定的时候在一个节点内部。这样对于一些有分区字段作为连接条件的查询,很容易实现一些本地表连接或者多个本地表连接并行的局面。这样也性能最优。

表分组是 OceanBase 提供给业务架构师干预 OceanBase 负载均衡的一个精细化手段(OceanBase 负载均衡调度会考虑分区组),需要对业务和 OceanBase 都很了解。这个设计如果使用不当也可能会削弱 OceanBase 的分布式能力。

复制表

跟索引的使用思想一样,复制表是另外一个需要权衡考虑的方案。复制表跟表分组不能同时使用。

复制表主要解决一些表业务上决定了不能拆分(分区)或者没有必要拆分,但它又频繁的跟分区表有关联关系。为了减少不必要的跨机分布式查询,复制表将自己的数据强同步到实例的所有机器上,所以不管分区表的分区的主副本在哪个机器上,它都可以在 同机跟复制表的副本做表连接。

OceanBase 里普通的表多是三副本,但是在一个 2-2-2 的租户(实例)里,复制表的副本可以是六副本,并且还是全同步,六个副本事务日志同时落盘事务才提交。所以复制表的写性能会比 Paxos协议强同步策略要慢一些,适合写入TPS 不高的业务表(如配置表、参数表)。这就是复制表的代价。

以上是复制表常用用法。

还有更极端一点的用法,为了避免两个分区表的跨节点的 M:N 的连接,也可以将其中一个分区表做成复制表。这样做的可能原因只会是因为收益大于成本。这需要先测试验证一下。

普通表到复制表的转换可以在线做,也很方便,就是改变表的一个属性。运维也是相当简单。扩充副本( 3 副本到 6 副本)的时候会需要一些数据迁移时间。

本文适用于 OceanBase 企业版和社区版。个人经验,仅供参考,欢迎交流(http://open.oceanbase.com/answer )。

Logo

了解最新的技术洞察和前沿趋势,参与 OceanBase 定期举办的线下活动,与行业开发者互动交流

更多推荐