本文为 OceanBase 高级技术专家刘浩在第一届 OceanBase 开发者大会带来的分享。欢迎访问 OceanBase 官网获取更多信息:https://www.oceanbase.com/
3 月 25 日,第一届 OceanBase 开发者大会在北京举行,OceanBase 高级技术专家刘浩为大家带来了《RTO < 8s:OceanBase极致高可用的探索之旅》的分享,为大家介绍了 OceanBase 在 RTO 领域的探索。
以下是演讲实录:
大家好,我今天分享的主题是《RTO<8s,OceanBase 极致高可用的探索之旅》,RTO(Recovery Time Objective)指的是“故障发生后业务的恢复时间”,这里的“恢复时间(30s→8s)”是 OceanBase 从 4.0 到 4.1 版本一直在努力的事情,它是很精确的数字, 今天我将分享我们在客户的应用场景下看到了什么问题才决定将这个值精确到“8”,为什么之前 30 秒还不够?之后又做了哪些优化?解决了哪些挑战等一系列问题。
持续可用的价值是什么?
首先看一下持续可用的价值,这个相信用数据库的人都比较有体感。
第一,关键的 IT 系统宕机会造成巨大的经济损失。 在 OceanBase 商业化过程中,有的客户如果宕机一个小时,损失的金额大概在几十万到百万美金,这是一个非常惊人的数字。
第二,在经济损失以外,还有很多隐性品牌方面的损失。 比如每一个互联网服务都会大量使用数据库,如果数据库出现故障,故障时间内就无法为一部分用户提供服务,这会大大降低用户对该服务的信任度。
第三,对在 IT 系统支撑下的社会运转的影响。 每一次故障都可能带来非常大的社会影响。举个 3 月份的例子,我自己早上会坐十号线,在刚刚过去的半个月里面,我经历了两次十号线的故障,而且每次故障持续的时间长达一个小时,影响非常大。
▋ RPO
在谈到“持续可用”的时候总绕不开两个概念,RPO 和 RTO。
在故障发生的时候一般会有两个数字上的指标,第一个就是 RPO(Recovery Point Objective)。上图中间那条线是代表故障发生的时间点,在故障发生时间点之前会有一个短的线段,代表你使用这样的数据库业务会带来多少数据损失。
有两种主要的可能造成 RPO≠0 的原因:
第一,在使用类似于 MySQL 的数据库的时候,很多时候为了追求性能,事务提交时不强制刷盘。 这样如果恰好发生宕机,就可能丢失最后写入的一部分数据。
第二,现在数据库系统需要容纳更多的故障,比如机房的故障、城市的故障。 在之前的数据库里有典型的主备架构,主备架构更常用的是异步的方式。如果主库宕机时,备库无法接收最新的数据,也会带来明显的 RPO 损失,有的时候会在秒级,有的时候会更长,而每次 RPO 的损失都会要求业务对数据做补偿,这对业务会带来非常大的影响。
▋ RTO
另一方面,故障发生之后怎么去衡量业务恢复时间,这个就是今天会重点讲的 RTO 指标。我们在之前的数据库里可能需要到小时级,无论是 NewSQL 还是 OceanBase,从 30 秒到 8 秒的时间缩短,都给客户带来了更好的业务体验。
在谈到这几个值的时候我们绕不开基础设施的变化。 OceanBase 在 1.0 研发之初,很多关键业务是跑在大机上的,在过去十年的发展中,有非常明显的基础设施的变化和趋势:从大机到了普通的 PC 服务器以及明确的上云趋势,在整个 IT 资产成本下降的同时,也带来了服务可靠性的降低,这就要求我们在数据库软件层面更好地解决问题,因为无论是硬件还是软件,故障随时都可能发生。
▋ 数据库容灾技术的演进
现在,OceanBase 的很多业务都要求 7×24 小时服务,随着现代社会的变迁,用户对我们有更高的要求,从 99.99% 到 99.999%,全年故障时间只能有几分钟,否则作为用户,可能就要通过网络进行投诉了。
这些现实诉求带来了数据库容灾基础的变化,从基于主备的主机架构(Oracle)到基于 Paxos 一致性协议的架构(OceanBase)变化。
▋ 客户对 RTO 的多样诉求
接下来我分享一下我们看到的客户需求,这里有几个故事。
第一个故事是支付宝的故事。 回头看 OceanBase 项目立项的初心,是我们在解决支付宝大体量业务的问题时,发现如果业务停机,会带来明显的客户负反馈,所以 OceanBase 非常大的初心之一就是要解决像支付宝这样核心金融业务的可用性难题,我们要做到:遇到任何故障都不丢数据且用更短的时间恢复业务。
第二个故事是 OceanBase 商业化过程中,我们在服务大型金融客户的故事。 我们发现他之前的核心业务系统是跑在大机和 DB2 上面的,客户有很明确的降本诉求,希望迎接更好的分布式架构体系,以满足未来业务发展。所以他需要在 PC 服务器上去跑 OceanBase 分布式数据库,但是他的业务系统又很复杂,像全局索引、分布式事务,跑在大机上没有问题,因为大机的可靠性很好,但是换到 PC 服务器的时候,可靠性会有下降。
但无论是面对他的客户,还是从整个国家的监管层面,架构变化带来可用性的下降都是没有办法接受的,所以我们需要通过 OceanBase 的技术迭代去解决架构变化带来的可靠性下降问题。
第三个故事是我们在服务云上客户时的案例。 某公有云客户最开始的时候只需要简简单单地把自己的业务跑在云上,但他本身的业务是做跨境电商,此时他需要把自己的业务跑在更多的云上面,比如 AWS、微软,但我们发现很多云厂商的存储能力并没有这样的好,这就要求我们在这样的介质下,可以给客户提供同样的服务连续性保障,即客户对 OceanBase 可靠性的诉求。
在这样业务需求的基础上, OceanBase 之前提出的 RPO=30 秒的指标是足够的,但当我们需要在 4.0 版本上优化到 8 秒,就不仅仅是数字的变化,背后还有很多其他的东西。
从30s到8s,不断追求极致的OceanBase
第一,我们看到的 RTO<8s,除数字以外还需要有更多的场景支持。 不仅仅是常见的“进程挂掉”、“物理机或者 ECS 的重启”,还涉及到更多如网络分区,或存储介质故障等场景,在云上也好,在自己的 PC 服务器也好,这些都是客户关心的,我们需要在这些不同的场景下都能够做到小于 8 秒的指标。
第二,我们看到业务同学更多会从业务视角关注应用恢复的时间。 数据库的恢复其实已经很复杂了,但是如果我们考虑到前面还有更多的组件如“应用服务器”、“负载均衡”,还有 OBProxy 等,我们就要更好地满足业务视角关注的 RTO 时间,此时,需要从业务恢复和数据库恢复两个视角去衡量整个指标的变化。
第三,很关键的方面,我们要做到“零调参”。 无论你使用的是 OceanBase 的商业版还是社区版,都不需要调任何的参数就能够做到 RTO<8s,我们不是为了做 POC 测试,而是立足于面对客户实际的生产场景,去解决用户使用数据库中遇到的挑战和问题。
第四,OceanBase 有一个很重要的概念即所有的节点对等。 内部每一个数据库的服务器都提供很多服务,不仅包括事务存储,还包括集群管控节点(RootService),所以我们要做到其中任何一个节点的故障都能够提供这样的能力。
第五,我们在现有内部的 PC 服务器部署基础之上,希望在多云和跨云部署场景下也提供这样的能力。 这里有一个演示的例子(可移步官网看视频回放),是基于 OceanBase 社区版(与商业版完全一样)在我们内部测试机上跑的简单的 Sysbench 业务。左边已经在把压力起起来,在右边会把提供读写服务的进程 kill 掉,由此大家会对整个 RTO 有个直观的印象。前排同学可以看到从第 30 秒开始下降,到第 34 秒时,整个服务能力就已经完全恢复了。
在小于8s的时间内发生了什么?
如下图访问链路图所示,业务从应用服务器开始,经过负载均衡,再经过 OBProxy(保障业务请求找到正确的后端服务器节点),最后访问到后端服务器节点。
接下来给大家简单介绍一下小于 8 秒的时间内到底发生了什么?
在发生实际的故障时,内部会花短短几秒时间做故障的检测,检测之后整个 OceanBase 内部会进行完全自动的恢复流程,在恢复流程做完之后新的主节点就会对外提供服务,提供服务之后会涉及到路由的刷新,他会有个路由的汇报和刷新机制。在几秒钟的请求之后,业务请求即可恢复。
在这个全自动流程里,也会有一些挑战。第一,如何快速准确地判定故障;第二,发生故障后,数据库如何快速恢复;第三,数据库恢复后,如何帮助业务快速恢复,接下来将围绕这三个板块做详细说明。
▋ 如何快速准确的判定故障
首先,我们要大幅减少故障恢复单元。 这里的故障恢复单元对应的是每一个Paxos Group,OceanBase 1.0 到 3.x 版本里,Paxos Group 是 OceanBase 的一个业务分区,如果是在业务的表或者分区特别多的情况下,它的故障恢复单元数量是非常惊人的,即在 50 万的分区下会有 50 万的故障恢复单元,也就是说我们需要在 30s 内恢复 50 万个故障恢复单元。
在这种场景下,我们很难做到进一步优化。 但在 4.0 版本中,我们做了非常大的架构调整,就是把故障恢复单元变成单机的日志流,而不再与业务 schema 相关,也不与业务数据量相关。 比如像“1:1:1”这样的最典型的三副本集群,他实际上只有一个或者三个故障单元,这个量级的减少会帮助数据库在恢复的时候做得更加稳定,这样才能保证故障恢复时间的进一步下降。
其次,我们在新的架构基础上,发现在之前的设计里还有很多问题要解决。
一方面,在 4.0 版本中,我们把最底层的最核心的部分之一即整个的选举和一致性协议做了重新设计和实现。在谈到选举的时候有一个非常重要的概念:OceanBase 要保证稳定的 RTO,所以选举流程要求基于稳定的算法去完成,无论在何种情况下,它都必须在某个特定的时间内选出,而不是基于随机的算法。
在 4.0 版本中,OceanBase 也延续了这样的思路,但是也做了全新的设计,并且达到了两个特别好的结果—— 就是我们在 4.0 版本的选举里面,彻底去掉了对节点之间 NTP 依赖,不再需要节点之间时钟严格同步;在设计上,也不再依赖于节点之间的绝对时间去做选举,而是变成完全基于消息驱动的相对时间去做选举。另一个比较大的变化是我们把整个选举 Lease 的时间缩短到了 4 秒以内。
另一方面,我们把底层的一致性协议也完全重新做了设计。 OceanBase 的开源版里,有个很奇怪的目录叫 PALF(Paxos-backed Append-only Log File System),这是基于 Paxos 一致性协议全新进行设计的,将最底层的东西做了全新设计实现,并做了大量的优化。
我们解决的是两个问题:一是我们在减少故障恢复单元之后面临了一个挑战,即大家可能会问“一个 Paxos Group 能不能支撑 OceanBase 那么好的性能和 scale-up 的能力”,其他的数据库经常会说:“我的并行能力是基于多个 Paxos 组、多个 Raft 组去实现的”,他们需要把自己多副本之间同构的单元去做拆分,但这个方式和我们做故障恢复、分布式事务的逻辑是不相吻合的。所以 OceanBase 的所有性能,包括单机都跑在一个 Paxos Group 里。
我们可以很自信地告诉大家:我们用一个 Paxos Group 可以跑到非常好的性能,并且可以超过 MySQL,我们也对这套协议做了测试,我们在自己的测试场景下,这套协议做到了接近 200 万的 TPS,性能非常好。
第三方面,我们重新基于 TCP 和 RPC 框架,做了故障检测机制的设计。 大家可以理解为:我们需要在 TCP 层面做故障的检测,某端网络挂掉我们要及时的发现,所以在 3.x 里,我们利用了 TCP Keepalive 去做检测,这里有些场景不好解决——比如说因为某种原因进程 core 了,此时 TCP 是联通的,但在 RPC 层面看,却是发给这个节点的所有请求都 hang 住了。这种情况对 RTO 非常有挑战,我们基于 TCP Keepalive 的机制,发现无法解决这样的场景问题,所以我们在更上层的 RPC 框架内部又重新设计了一套故障检测的机制,发现和帮助解决这样场景的 RTO 恢复。
▋ 发生故障之后,数据库如何快速恢复?
关于发生故障之后数据库如何快速恢复的问题,因为我们在“1:1:1”的环境里承担主节点的集群和承担备节点的集群,是一个对等的结构。其中主节点的集群是并发写入的,而其他的备节点一定要时刻做好准备,在主节点故障之后能够立即承担服务。 这里面涉及到一个关键的核心:整个 Follower 节点要实时并行地去回放主节点写入的内容,即“主节点写入的有多快备节点回放就有多快”。
另外,我们统一设计了故障检测框架和有主改选。 这里解决的是我们在整个的故障检测里节点不可服务的问题,还有一些磁盘 hang 的场景,下面对端的整个网络都是通着的,此时你通过选举是发现不了问题的,那么在这种情况下我们需要留足够的时间去帮助故障检测,发现磁盘内的故障。比如我需要 5 秒的时间才能发现故障,那么 5 秒过去之后,留给恢复的时间其实就很少了,我们没有机会去给 Lease 让他再做超时,如果这俩叠加 RTO 会翻倍。
因此,在这个过程里我们重新做了设计:在发现整个磁盘层面出现故障之后,不会再走无主选举,而是与选举协议做了更有机的结合,这种情况下会直接走有主的改选,可以在百毫秒的级别就把主的服务切换到一个新的 leader 上,这样可以保证更多的场景能够达到 RTO<8 秒的体验。
另外,我们要基于 RPC 去做路由的刷新。 在分布式数据库里面有一个重要的概念:如果想要知道我这个服务用户数据的节点位置在哪里,那么每次出现故障的时候都是在变更数据库的位置元信息。在典型的数据库场景里,解决的方式是我的中心节点,可能是 OceanBase 内部的 Root Service 去记录节点信息,当这个节点出现故障后,它就需要把自己的位置信息汇报给新的中心节点,集群里其他所有节点再去查询这张表拿到这个信息。
实际上,这个过程还是蛮麻烦的,因为涉及到故障之后内部表的汇报中间要执行 SQL,可能还会引入一些对中控节点的依赖,此外,后面其他节点去查询位置相关信息的时候也需要走 SQL,这中间的耗时蛮长。OceanBase 内部做了一套新的机制——即完全基于消息的去查询。在故障之后新的主节点位置的信息,即路由信息查询的去中心化。
▋ 数据库恢复后,如何帮助业务快速恢复
当数据恢复之后怎么快速帮助业务恢复?我们回到前面 OceanBase 的访问链路图,其中有个节点叫 OBProxy,我们在 OBProxy 上也做了很多新的设计。
首先,我们把之前 OBProxy 的探活策略进行了重新改造,我们称之为“基于建连的探活策略”。 之前的探活策略是走整个 RPC 的框架,涉及到整个队列的处理机制,很有可能在队列请求积压或者整个节点 load 升高的时候出现误判,这种误判可能会导致 Proxy 误判把一个正常服务的节点杀掉了或把正常服务的节点标记在黑名单。因此,我们重新设计了机制,去基于建连去做探活,避免请求排队的影响,从而大幅度减少误判的概率。
另一方面, Proxy 也对故障节点做了实时的拉黑和洗白,在中间件层面,可以秒级判断故障的节点,出现问题可以秒级把故障的节点重新加回到集群里面去也会比较好。
以上就是我的全部分享,谢谢大家。
欢迎访问 OceanBase 官网获取更多信息:https://www.oceanbase.com/