数据一致性-分区可用性-性能—多副本强同步数据库系统实现之我见

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云原生数据库 PolarDB MySQL 版,通用型 2核4GB 50GB
简介: 本文将跳出任何一种数据库,从原理的角度上来分析下面的几个问题: 问题一:数据一致性。在不使用共享存储的情况下,传统RDBMS(例如:Oracle/MySQL/PostgreSQL等),能否做到在主库出问题时的数据零丢失。 问题二:分区可用性。有多个副本的数据库,怎么在出现各种问题时保证系统的

本文将跳出任何一种数据库,从原理的角度上来分析下面的几个问题:

  • 问题一:数据一致性。在不使用共享存储的情况下,传统RDBMS(例如:Oracle/MySQL/PostgreSQL等),能否做到在主库出问题时的数据零丢失。

  • 问题二:分区可用性。有多个副本的数据库,怎么在出现各种问题时保证系统的持续可用?

  • 问题三:性能。不使用共享存储的RDBMS,为了保证多个副本间的数据一致性,是否会损失性能?如何将性能的损失降到最低?

  • 总结

问题一:数据一致性

问:脱离了共享存储,传统关系型数据库就无法做到主备强一致吗?

答:我的答案,是No。哪怕不用共享存储,任何数据库,也都可以做到主备数据的强一致。Oracle如此,MySQL如此,PostgreSQL如此,OceanBase也如此。

如何实现主备强一致?大家都知道数据库中最重要的一个技术:WAL(Write-Ahead-Logging)。更新操作写日志(Oracle Redo Log,MySQL Binlog等),事务提交时,保证将事务产生的日志先刷到磁盘上,保证整个事务的更新操作数据不丢失。那实现数据库主备数据强一致的方法也很简单:

  • 事务提交的时候,同时发起两个写日志操作,一个是将日志写到本地磁盘的操作,另一个是将日志同步到备库并且确保落盘的操作;

  • 主库此时等待两个操作全部成功返回之后,才返回给应用方,事务提交成功;

整个事务提交操作的逻辑,如下图所示:


上图所示,由于事务提交操作返回给应用时,事务产生的日志在主备两个数据库上都已经存在了,强同步。因此,此时主库Crash的话,备库提供服务,其数据与主库是一致的,没有任何事务的数据丢失问题。主备数据强一致实现。用过Oracle的朋友,应该都知道Oracle的Data Guard,可工作在 最大性能,最大可用,最大保护 三种模式下,其中第三种 最大保护 模式,采用的就是上图中的基本思路。

实现数据的强同步实现之后,接下来到了考虑可用性问题。现在已经有主备两个数据完全一致的数据库,备库存在的主要意义,就是在主库出故障时,能够接管应用的请求,确保整个数据库能够持续的提供服务:主库Crash,备库提升为主库,对外提供服务。此时,又涉及到一个决策的问题,主备切换这个操作谁来做?人当然可以做,接收到主库崩溃的报警,手动将备库切换为主库。但是,手动的效率是低下的,更别提数据库可能会随时崩溃,全部让人来处理,也不够厚道。一个HA(High Availability)检测工具应运而生:HA工具一般部署在第三台服务器上,同时连接主备,当其检测到主库无法连接,就切换备库,很简单的处理逻辑,如下图所示:


HA软件与主备同时连接,并且有定时的心跳检测。主库Crash后,HA探测到,发起一个将备库提升为主库的操作(修改备库的VIP或者是DNS,可能还需要将备库激活等一系列操作),新的主库提供对外服务。此时,由于主备的数据是通过日志强同步的,因此并没有数据丢失,数据一致性得到了保障。

有了基于日志的数据强同步,有了主备自动切换的HA软件,是不是就一切万事大吉了?我很想说是,确实这个架构已经能够解决90%以上的问题,但是这个架构在某些情况下,也埋下了几个比较大的问题。

首先,一个一目了然的问题,主库Crash,备库提升为主库之后,此时的数据库是一个单点,原主库重启的这段时间,单点问题一直存在。如果这个时候,新的存储再次Crash,整个系统就处于不可用状态。此问题,可以通过增加更多副本,更多备库的方式解决,例如3副本(一主两备),此处略过不表。

其次,在主备环境下,处理主库挂的问题,算是比较简单的,决策简单:主库Crash,切换备库。但是,如果不是主库Crash,而是网络发生了一些问题,如下图所示:


若Master与Slave之间的网络出现问题,例如:断网,网络抖动等。此时数据库应该怎么办?Master继续提供服务?Slave没有同步日志,会数据丢失。Master不提供服务?应用不可用。在Oracle中,如果设置为 最大可用 模式,则此时仍旧提供服务,允许数据不一致;如果设置为 最大保护 模式,则Master不提供服务。因此,在Oracle中,如果设置为 最大保护 模式,一般建议设置两个或以上的Slave,任何一个Slave日志同步成功,Master就继续提供服务,提供系统的可用性。

网络问题不仅仅出现在Master和Slave之间,同样也可能出现在HA与Master,HA与Slave之间。考虑下面的这种情况:


HA与Master之间的网络出现问题,此时HA面临两个抉择:

  • HA到Master之间的连接不通,认为主库Crash。选择将备库提升为主库。但实际上,只是HA到Master间的网络有问题,原主库是好的(没有被降级为备库,或者是关闭),仍旧能够对外提供服务。新的主库也可以对外提供服务。两个主库,产生双写问题,最为严重的问题。

  • HA到Master之间的连接不同,认为是网络问题,主库未Crash。HA选择不做任何操作。但是,如果这时实际上确实是主库Crash了,HA不做操作,数据库不对外提供服务。此时,双写问题避免了,但是应用的可用性受到了影响。

最后,数据库会出现问题,数据库之间的网络会出现问题,那么再考虑一层,HA软件本身也有可能出现问题。如下图所示:


如果是HA软件本身出现了问题,怎么办?我们通过部署HA,来保证数据库系统在各种场景下的持续可用,但是HA本身的持续可用谁来保证?难道我们需要为HA做主备,然后再HA之上再做另一层HA?一层层加上去,子子孙孙无穷尽也 … …

其实,上面提到的这些问题,其实就是经典的分布式环境下的一致性问题(Consensus),近几年比较火热的Lamport老爷子的Paxos协议,Stanford大学最近发表的Raft协议,都是为了解决这一类问题。(对Raft协议感兴趣的朋友,可以再看一篇Raft的动态演示PPT:Understandable Distributed Consensus

问题二:分区可用性


前面,我们回答了第一个问题,数据库如果不使用共享存储,能否保证主备数据的强一致?答案是肯定的:可以。但是,通过前面的分析,我们又引出了第二个问题:如何保证数据库在各种情况下的持续可用?至少前面提到的HA机制无法保证。那么是否可以引入类似于Paxos,Raft这样的分布式一致性协议,来解决上面提到的各种问题呢?

答案是可以的,我们可以通过引入类Paxos,Raft协议,来解决上面提到的各类问题,保证整个数据库系统的持续可用。考虑仍旧是两个数据库组成的主备强一致系统,仍旧使用HA进行主备监控和切换,再回顾一下上一节新引入的两个问题:

  • HA软件自身的可用性如何保证?

  • 如果HA软件无法访问主库,那么这时到底是主库Crash了呢?还是HA软件到主库间的网络出现问题了呢?如何确保不会同时出现两个主库,不会出现双写问题?

  • 如何在解决上面两个问题的同时,保证数据库的持续可用?

为了解决这些问题,新的系统如下所示:


相对于之前的系统,可以看到这个系统的复杂性明显增高,而且不止一成。数据库仍旧是一主一备,数据强同步。但是除此之外,多了很多变化,这些变化包括:

  • 数据库上面分别部署了HA Client;

  • 原来的一台HA主机,扩展到了3台HA主机。一台是HA Master,其余的为HA Participant;

  • HA主机与HA Client进行双向通讯。HA主机需要探测HA Client所在的DB是否能够提供服务,这个跟原有一致。但是,新增了一条HA Client到HA主机的Master Lease通讯。

这些变化,能够解决上面的两个问题吗?让我们一个一个来分析。首先是:HA软件自身的可用性如何保证?

从一台HA主机,增加到3台HA主机,正是为了解决这个问题。HA服务,本身是无状态的,3台HA主机,可以通过Paxos/Raft进行自动选主。选主的逻辑,我这里就不做赘述,不是本文的重点,想详细了解其实现的,可以参考互联网上洋洋洒洒的关于Paxos/Raft的相关文章。总之,通过部署3台HA主机,并且引入Paxos/Raft协议,HA服务的高可用可以解决。HA软件的可用性得到了保障。

第一个问题解决,再来看第二个问题:如何识别出当前是网络故障,还是主库Crash?如何保证任何情况下,数据库有且只有一个主库提供对外服务?

通过在数据库服务器上部署HA Client,并且引入HA Client到HA Master的租约(Lease)机制,这第二个问题同样可以得到完美的解决。所谓HA Client到HA Master的租约机制,就是说图中的数据库实例,不是永远持有主库(或者是备库)的权利。当前主库,处于主库状态的时间是有限制的,例如:10秒。每隔10秒,HA Client必须向HA Master发起一个新的租约,续租它所在的数据库的主库状态,只要保证每10秒收到一个来自HA Master同意续租的确认,当前主库一直不会被降级为备库。

第二个问题,可以细分为三个场景:

  • 场景一:主库Crash,但是主库所在的服务器正常运行,HA Client运行正常

主库Crash,HA Client正常运行。这种场景下,HA Client向HA Master发送一个放弃主库租约的请求,HA Master收到请求,直接将备库提升为主库即可。原主库起来之后,作为备库运行。

  • 场景二:主库所在的主机Crash。(主库和HA Client同时Crash)

此时,由于HA Client和主库同时Crash,HA Master到HA Client间的通讯失败。这个时候,HA Master还不能立即将备库提升为主库,因为区分不出场景二和接下来的场景三(网络问题)。因此,HA Master会等待超过租约的时间(例如:12秒),如果租约时间之内仍旧没有续租的消息。那么HA Master将备库提升为主库,对外提供服务。原主库所在的主机重启之后,以备库的状态运行。

  • 场景三:主库正常,但是主库到HA Master间的网络出现问题

对于HA Master来说,是区分不出场景二和场景三的。因此,HA Master会以处理场景二同样的逻辑处理场景三。等待超过租约的时间,没有收到续租的消息,提升原备库为主库。但是在提升备库之前,原主库所在的HA Client需要做额外的一点事。原主库HA Client发送给HA Master的续租请求,由于网络问题,一直没有得到响应,超过租约时间,主动将本地的主库降级为备库。如此一来,待HA Master将原备库提升为主库时,原来的主库已经被HA Client降级为备库。双主的情况被杜绝,应用不可能产生双写。

同过以上三个场景的分析,问题二同样在这个架构下被解决了。而解决问题二的过程中,系统最多需要等待租约设定的时间,如果租约设定为10秒,那么出各种问题,数据库停服的时间最多为10秒,基本上做到了持续可用。这个停服的时间,完全在于租约的时间设置。

到这儿,基本可以说,要实现一个持续可用(分区可用性保证),并且保证主备数据强一致的数据库系统,是完全没问题的。在现有数据库系统上做改造,也是可以的。但是,如果考虑到实际的实现,这个复杂度是非常高的。数据库的主备切换,是数据库内部实现的,此处通过HA Master来提升主库;通过HA Client来降级备库;保证数据库崩溃恢复后,恢复为备库;通过HA Client实现主库的租约机制;实现HA主机的可用性;所有的这些,在现有数据库的基础上实现,都有着相当的难度。能够看到这儿,而且有兴趣的朋友,可以针对此问题进行探讨。

问题三:性能

数据一致性,通过日志的强同步,所有数据均可以解决。分区可用性,在出现任何异常情况时仍旧保证系统的持续可用,可用在数据强同步的基础上引入Paxos/Raft等分布式一致性协议来解决,虽然这个目前没有成熟的实现。接下来再让我们来看看一个很多朋友都很感兴趣的问题:如何在保证强同步的基础上,同时保证高性能?回到我们本文的第一幅图:


为了保证数据强同步,应用发起提交事务的请求时,必须将事务日志同步到Slave,并且落盘。相对于异步写Slave,同步方式多了一次Master到Slave的网络交互,同时多了一次Slave上的磁盘sync操作。反应到应用层面,一次Commit的时间一定是增加了,具体增加了多少,要看主库到备库的网络延时和备库的磁盘性能。

为了提高性能,第一个很简单的想法,就是部署多个Slave,只要有一个Slave的日志同步完成返回,加上本地的Master日志也已经落盘,提交操作就可以返回了。多个Slave的部署,对于消除瞬时的网络抖动,非常有效果。在Oracle的官方建议中,如果使用最大保护模式,也建议部署多个Slave,来最大限度的消除网络抖动带来的影响。如果部署两个Slave,新的部署架构图如下所示:


新增一个Slave,数据三副本。两个Slave,只要有一个Slave日志同步完成,事务就可以提交,极大地减少了某一个网络抖动造成的影响。增加了一个副本之后,还能够解决当主库Crash之后的数据安全性问题,哪怕主库Crash,仍旧有两个副本可以提供服务,不会形成单点。

但是,在引入数据三副本之后,也新引入了一个问题:主库Crash的时候,到底选择哪一个备库作为新的主库?当然,选主的权利仍旧是HA Master来行使,但是HA Master该如何选择?这个问题的简单解决可以使用下面的几个判断标准:

  1. 日志优先。两个Slave,哪个Slave拥有最新的日志,则选择这个Slave作为新的主库。

  2. 主机层面排定优先级。如果两个Slave同时拥有最新的日志,那么该如何选择?此时,选择任何一个都是可以的。例如:可以根据Slave主机IP的大小进行选择,选择IP小的Slave作为新的主库。同样能够解决问题。

新的主库选择出来之后,第一件需要做的事,就是将新的Master和剩余的一个Slave,进行日志的同步,保证二者日志达到一致状态后,对应用提供服务。此时,三副本问题就退化为了两副本问题,三副本带来的防止网络抖动的红利消失,但是由于两副本强同步,数据的可靠性以及一致性仍旧能够得到保障。

当然,除了这一个简单的三副本优化之外,还可以做其他更多的优化。优化的思路一般就是同步转异步处理,例如事务提交写日志操作;使用更细粒度的锁;关键路径可以采用无锁编程等。

多副本强同步,做到极致,并不一定会导致系统的性能损失。当然,极致应该是什么样子的?我的想法是:

  • 对于单个事务来说,RT增加。其响应延时一定会增加(至少多一个网络RT,多一次磁盘Sync);

  • 对整个数据库系统来说,吞吐量不变。远程的网络RT和磁盘Sync并不会消耗本地的CPU资源,本地CPU的开销并未增大。只要是异步化做得好,整个系统的吞吐量,并不会由于引入强同步而降低。

总结


洋洋洒洒写了一堆,最后做一个小小的总结:

  • 各种主流关系型数据库系统是否可以实现主备的强一致,是否可以保证不依赖于存储的数据一致性?

          可以。Oracle有,MySQL 5.7,阿里云RDS,网易RDS都有类似的功能。

  • 目前各种关系型数据库系统,能否在保证主备数据强一致的基础上,提供系统的持续可用和高性能?

          可以做,但是难度较大,目前主流关系型数据库缺乏这个能力。



作者介绍

何登成(花名:圭多),阿里巴巴数据库技术资深专家,2014年加入阿里巴巴数据库团队。2014、2015连续两次作为“双11大促/新春红包”活动数据库整体负责人。同时负责领导AliDB内核的研发工作(包括AliSQL, AliRocks等数据库内核)。圭多在追求技术的道路上,十年如一日。


欢迎加入

阿里巴巴数据库技术

长按二

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
10天前
|
XML Java 数据库连接
性能提升秘籍:如何高效使用Java连接池管理数据库连接
在Java应用中,数据库连接管理至关重要。随着访问量增加,频繁创建和关闭连接会影响性能。为此,Java连接池技术应运而生,如HikariCP。本文通过代码示例介绍如何引入HikariCP依赖、配置连接池参数及使用连接池高效管理数据库连接,提升系统性能。
35 5
|
1月前
|
存储 缓存 监控
数据库优化技术:提升性能与效率的关键策略
【10月更文挑战第15天】数据库优化技术:提升性能与效率的关键策略
56 8
|
19天前
|
缓存 关系型数据库 MySQL
高并发架构系列:数据库主从同步的 3 种方案
本文详解高并发场景下数据库主从同步的三种解决方案:数据主从同步、数据库半同步复制、数据库中间件同步和缓存记录写key同步,旨在帮助解决数据一致性问题。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
高并发架构系列:数据库主从同步的 3 种方案
|
19天前
|
缓存 监控 关系型数据库
如何根据监控结果调整 MySQL 数据库的参数以提高性能?
【10月更文挑战第28天】根据MySQL数据库的监控结果来调整参数以提高性能,需要综合考虑多个方面的因素
58 1
|
19天前
|
监控 关系型数据库 MySQL
如何监控和诊断 MySQL 数据库的性能问题?
【10月更文挑战第28天】监控和诊断MySQL数据库的性能问题是确保数据库高效稳定运行的关键
39 1
|
19天前
|
缓存 关系型数据库 MySQL
如何优化 MySQL 数据库的性能?
【10月更文挑战第28天】
43 1
|
21天前
|
监控 关系型数据库 MySQL
数据库优化:MySQL索引策略与查询性能调优实战
【10月更文挑战第27天】本文深入探讨了MySQL的索引策略和查询性能调优技巧。通过介绍B-Tree索引、哈希索引和全文索引等不同类型,以及如何创建和维护索引,结合实战案例分析查询执行计划,帮助读者掌握提升查询性能的方法。定期优化索引和调整查询语句是提高数据库性能的关键。
98 1
|
24天前
|
Java 数据库连接 数据库
优化之路:Java连接池技术助力数据库性能飞跃
在Java应用开发中,数据库操作常成为性能瓶颈。频繁的数据库连接建立和断开增加了系统开销,导致性能下降。本文通过问题解答形式,深入探讨Java连接池技术如何通过复用数据库连接,显著减少连接开销,提升系统性能。文章详细介绍了连接池的优势、选择标准、使用方法及优化策略,帮助开发者实现数据库性能的飞跃。
27 4
|
21天前
|
Java 数据库连接 数据库
深入探讨Java连接池技术如何通过复用数据库连接、减少连接建立和断开的开销,从而显著提升系统性能
在Java应用开发中,数据库操作常成为性能瓶颈。本文通过问题解答形式,深入探讨Java连接池技术如何通过复用数据库连接、减少连接建立和断开的开销,从而显著提升系统性能。文章介绍了连接池的优势、选择和使用方法,以及优化配置的技巧。
22 1
|
27天前
|
存储 缓存 监控
数据库优化:提升性能与效率的关键策略
【10月更文挑战第21】数据库优化:提升性能与效率的关键策略