MySQL-高可用篇-⑦高可用架构
MySQL-高可用篇-⑦高可用架构
学习核心
高可用架构概念核心
- 高可用实现的基础、高可用解决方案
- 高可用架构设计
- 高可用套件
- InnoDB Cluster
MySQL中常见的几种高可用架构部署方案
如何将数据库异常的损失降到最低
学习资料
高可用核心概念
1.高可用基础
什么是高可用?
高可用(High Availability)是系统所能提供无故障服务的一种能力。 简单地说就是避免因服务器宕机而造成的服务不可用。高可用是每个业务系统设计时,开发人员必须考虑的关键点。比如系统在发生不可用时,业务表现如何?用户能否容忍系统不可用时长?而业界度量高可用能力也有统一标准:判断宕机时间,并以此计算出每年系统可用时间达到几个 9,来判断高可用架构是否健壮。具体如下表所示:
通常来说,系统至少要达到 4 个 9(99.99%),也就是每年宕机时间不超过 52.56 分钟,否则用户体验会非常差,感觉系统不稳定。99.99% = 1 - 52.56 / (365*24*60)
不过 4 个 9 宕机 52 分钟对于生产环境的影响还是比较大,但是 5 个 9 对大部分系统来说要求又太高。所以一些云服务商会提出一个 99.995% 的可用性概念,那么系统一年的不可用时长为:不可用时长 = (1 - 99.995%)*365*24*60 = 26.28 (分钟)。即一年最多的影响服务的时间为 26.28 分钟。
2.高可用架构设计
高可用架构设计核心(实现基础):冗余+故障转移
- 软硬件冗余:冗余是高可用的基础,系统要达到高可用,一定要做好软硬件的冗余,消除单点故障(SPOF single point of failure)。通常认为,系统投入硬件资源越多,冗余也就越多,系统可用性也就越高。
- 故障转移:除了做好冗余,系统还要做好故障转移(Failover)的处理。也就是在最短的时间内发现故障,然后把业务切换到冗余的资源上。
MySQL 复制是高可用的技术基础,用于将数据实时同步到从机。高可用套件是MySQL 高可用实现的解决方案,负责切换新主机。
在明确上述高可用设计的基本概念后之后,进一步了解高可用架构设计的类型:无状态服务高可用设计、数据库高可用架构设计
无状态服务高可用设计
无状态服务高可用设计:发现问题直接转移,可通过负载均衡服务处理问题节点(当发现节点有问题,直接剔除)。如下图所示,当第①台Nginx服务器出现问题导致服务不可用时,Load Balance负载均衡服务发现问题后就可以直接将其剔除,而对于上层用户来说可能只会出现几s内的访问出现问题,待问题节点剔除后服务立刻恢复正常
数据库高可用架构设计
系统高可用设计,真正的难点、痛点不在于无状态服务的设计,而在于数据库的高可用设计,这是因为:
- 数据持久化在数据库中,是有状态的服务;
- 数据库的容量比较大,Failover 的时间相对无状态服务会更多;
- 一些系统,如金融场景的数据库,会要求数据完全不能丢失,无形增加了高可用实现的难度;
从架构角度看,数据库高可用本身也是业务高可用,所以要从业务全流程的角度出发,思考数据库的高可用设计。此处提供了三种数据库的高可用架构设计方法,它们不但适用于 MySQL 数据库,也适用于其他数据库。
- 基于数据层的数据库高可用架构
(1)基于数据层的数据库高可用架构
基于数据层的数据库高可用架构,就是基于数据同步技术。当主服务器 Master 发生宕机,则故障转移到从服务器 Slave。对于 MySQL 数据库来说,就是基于复制技术。以下图示为例,在基于读写分离的场景中,当主服务器发生宕机,Slave3升级为新的Master,建立了新的复制拓扑架构,Slave1、Slace2都连接到新的Master进行数据同步。为了在故障转移后对 Service 服务无感知,所以需要引入 VIP(Virtual IP)虚拟 IP 技术,当发生宕机时,VIP 也需要漂移到新的主服务器。
这个架构的实现难点在于:
- 如何保障数据一致性;
- 如何发现主服务器宕机;
- 故障转移逻辑的处理;
“如何保障数据一致性”可以通过 MySQL 提供的无损复制技术,来保障“数据一致性”。而“发现主服务器宕机”“处理故障转移逻辑”要由数据库高可用套件完成
(2)基于业务层的数据库高可用架构
“基于业务层的数据库高可用架构设计”则完全基于业务实现,数据库只是用于存储数据。当一台数据库主服务器不可用,业务直接写另一台数据库服务器就可以
Service 服务写入 Master1 主服务器失败后,不用等待故障转移程序启用主从切换,而是直接通过代码层控制把数据写入 Master2 主服务器。这看似是一种非常简单、粗暴的高可用架构实现方式,但能符合这样设计的业务却并不多,因为该设计前提是状态可修改。
比如电商中的订单服务,其基本逻辑就是存储电商业务中每笔订单信息,核心逻辑就是往表Orders 中插入数据,即:
INSERT INTO Orders(o_orderkey, ... ) VALUES (...)
此处 o_orderkey 是主键。为了实现基于业务层的数据库高可用,可以在主键生成过程中加入额外信息,比如服务器编号(此处订单的主键设计:PK = 有序UUID-服务器编号)。当写入服务器编号 1 时失败了,业务层会把订单的主键修改为服务器编号 2,以此实现了业务层的高可用,电商中的这种订单号生成方式也称为“跳单”。
而当查询订单信息时,由于主键中包含了服务器编号,那么业务知道该笔订单存储在哪台服务器,就可以非常快速地路由到指定的服务器。但这样设计的前提是整个服务的写入主键是可以进行跳单设计,且查询全部依赖主键进行搜索,概念类似NoSQL 的 KV 访问设计
(3)融合的数据库高可用架构设计
在“基于业务层的数据库高可用架构”中,虽然通过跳单设计,可以实现写入业务的高可用实现,但这时订单服务的查询功能会受到极大影响。在上述案例场景中,当【Master1】发生宕机时,服务器编号为 1 的相关订单则无法查询。
因此可以采用业务和数据层相结合的高可用设计,用于解决【基于业务层的数据库高可用架构】中服务器宕机后,查询服务受限的问题。其架构图如下所示:
不同编号的订单根据不同的数据库进行存放,比如服务器编号为 1 的订单存放在数据库 DB1 中,服务器编号为 2 的订单存放在数据库 DB2 中。使用MySQL的部分复制技术,将服务器1的DB1同步到服务器2的DB1,将服务器2的DB2同步到服务器1的DB2
- 在常态情况下,上面两台 MySQL 数据库是双活的,都可以有数据的写入,业务的性能得到极大提升。
- 订单数据是完整的,服务器编号为 1 和 2 的数据都在一个 MySQL 实例上(将订单数据拆写到2个数据库中,各自存储对应服务器编号的订单数据)
- 服务器1的DB1存储的是服务器编号1的订单信息,并将数据同步到服务器2的DB1数据库
- 服务器2的DB2存储的是服务器编号2的订单信息,并将数据同步到服务器1的DB2数据库
- 当发生宕机时,Service 服务的写入不受到影响
- 结合图示,如果服务器1宕机,分析其工作流程(类似的,当服务器2宕机也是类似思路分析)
- 针对写入操作:新的订单会通过跳单设计写入服务器2的DB2
- 针对读取操作:基于图示的同步操作,在宕机之前已经将服务器1的DB1数据同步到服务器2的DB1中,当要检索服务器编号为1的数据时可以直接读取服务器2的DB1
- 结合图示,如果服务器1宕机,分析其工作流程(类似的,当服务器2宕机也是类似思路分析)
3.高可用套件
高可用透明切换机制
MySQL 的高可用套件用于负责数据库的 Failover 操作,即当数据库发生宕机时,MySQL 可以剔除原有主机,选出新的主机,然后对外提供服务,保证业务的连续性。
为了不让业务感知到数据库的宕机切换,要用到 VIP(Virtual IP)技术。其中,VIP 不是真实的物理 IP,而是可以随意绑定在任何一台服务器上。
==业务访问数据库,不是服务器上与网卡绑定的物理 IP,而是这台服务器上的 VIP。==当数据库服务器发生宕机时,高可用套件会把 VIP 插拔到新的服务器上。数据库 Failover后,业务依旧访问的还是 VIP,所以使用 VIP 可以做到对业务透明。如下图示分析,当数据库Failover后,数据库架构的变化:
但是VIP也是具备局限性的,其仅限于同机房同网段的 IP 设定。如果是是类似三园区同城跨机房容灾架构,VIP 就不可用了。需要使用名字服务,常见的名字服务就是 DNS(Domain Name Service),上层业务通过域名进行访问,当发生宕机进行机房级切换后如下所示:
虽然使用域名或其他名字服务可以解决跨机房的切换问题,但是引入了新的组件。新组件的高可用的问题也需要特别注意。
MHA
MHA(Master High Availability)是一款开源的 MySQL 高可用程序,它为 MySQL 数据库主从复制架构提供了 automating master failover 的功能。
MHA 是由业界大名鼎鼎的 Facebook 工程师 Yoshinorim 开发,它由两大组件所组成,MHA Manger 和 MHA Node。
MHA Manager 通常部署在一台服务器上,用来判断多个 MySQL 高可用组是否可用。当发现有主服务器发生宕机,就发起 failover 操作。MHA Manger 可以看作是 failover 的总控服务器。而 MHA Node 部署在每台 MySQL 服务器上,MHA Manager 通过执行 Node 节点的脚本完成failover 切换操作。
MHA Manager 和 MHA Node 的通信是采用 ssh 的方式,也就是需要在生产环境中打通 MHA Manager 到所有 MySQL 节点的 ssh 策略,那么这里就存在潜在的安全风险。另外,ssh 通信,效率也不是特别高。所以,MHA 比较适合用于规模不是特别大的公司,所有MySQL 数据库的服务器数量不超过 20 台。
Orchestrator
Orchestrator 是另一款开源的 MySQL 高可用套件,除了支持 failover 的切换,还可通过Orchestrator 完成 MySQL 数据库的一些简单的复制管理操作。可以把 Orchestrator 当成 MHA 的升级版,而且提供了 HTTP 接口来进行相关数据库的操作,比起 MHA 需要每次登录 MHA Manager 服务器来说,方便很多。
下图显示了 Orchestrator 的高可用设计架构
其基本实现原理与 MHA 是一样的,只是把元数据信息存储在了元数据库中,并且提供了HTTP 接口和命令的访问方式,使用上更为友好。但是由于管控节点到下面的 MySQL 数据库的管理依然是 ssh 的方式,依然存在 MHA 一样的短板问题,总的来说,关于 Orchestrator ,建议使用在较小规模的数据库集群。
数据管理平台
虽然 MHA 和 Orchestrator 都可以完成 MySQL 高可用的 failover 操作,但是,在生产环境中如果需要管理成千乃至上万的数据库服务器,由于它们的通信仅采用 ssh 的方式,并不能满足生产上的安全性和性能的要求。
所以,几乎每家互联网公司都会自研一个数据库的管理平台,用于管理公司所有的数据库集群,以及数据库的容灾切换工作。下图显示了数据库管理平台大致的实现框架:
数据库管理平台是用户操作数据库的入口。对数据库的大部分操作,比如数据库的初始化、数据查询、数据备份等操作、后续都能在这个平台完成,不用登录数据库服务器,这样的好处是能大大提升数据库操作的效率。数据库管理平台提供了 HTTP API 的方式,可用前后端分离的方式支持 Web、手机等多种访问方式。
元数据库用于存储管理 MySQL 数据库所有的节点信息,比如 IP 地址、端口、域名等。数据库管理平台 Manager 用来实际控制下面的所有 MySQL 节点,Manager 和后端 MySQL 的通信通过 MySQL 服务器上部署的 agent 方式进行。两者通过 BP 协议以 grpc 的方式通信,以解决了 ssh 的不安全性以及性能。
其中,agent 用来上报数据库各节点的状态给 Manager,管理节点 Manager 通过上报的信息判断数据库是否宕机,是否需要进行切换,切换到哪个节点。
上图的设计,能完成一个比较基本的数据库管理平台。另外,每个公司有自己的一些需求,也可以做到数据库管理平台中,比如安全要求、审计需求、工单系统等。所以,有了数据库管理平台,数据库的高可用切换、数据库日常管理和访问,都可以由平台自动完成。有了数据库管理平台,才能真正实现数据库管理的无人驾驶。
高可用套件总结
MySQL 复制是高可用实现冗余的技术基础,但是需要高可用套件才能完成 Failover(故障转移) 的操作
- 为了实现数据切换的透明性,可以采用 VIP 和名字服务机制(如DNS)。VIP 仅用于同机房同网段,名字服务器(如域名)可以跨机房进行切换
- MySQL 常用的高可用套件有 MHA 和 Orchestrator,它们都能完成 failover 的工作。但是由于管理节点与 MySQL 通信采用 ssh 协议,所以安全性不高,性能也很一般,一般建议用在不超过 20 台数据库节点的环境中
- 对于要管理 MySQL 数量比较多的场景,推荐自研数据库平台,这样能结合每家公司的不同特性,设计出 MySQL 数据库的自动管理平台,这样才能解放 DBA 的生产力,投入业务的优化工作中去。
4.InnoDB Cluster - 一种新的MySQL高可用解决方案
在前面的场景案例学习中,都是围绕读写分离、数据库高可用解决方案、数据库管理平台等
- MySQL复制是一种数据同步技术,是高可用冗余基础的基础实现
- 高可用的另一个核心是故障转移,还需要依赖外部组件,例如MHA、Orchestrator、数据库管理平台等
结合上述技术应用分析,进一步思考一些场景优化:
- 目前数据库复制的瓶颈:在于单节点写入,再将数据同步到各个节点,数据库性能无法扩展 =》思考是否可以存在一种技术实现多个节点写入并且保证数据同步的能力?
- 故障转移依赖于外部的高可用套件,如果高可用套件本身不可靠,则意味着高可能得不可靠性(例如当数据库真的宕机时,是否可以确保一定可以切换成功呢?)
基于此,引入InnoDB Cluster概念,其底层是由 MySQL Group Replication(下面简称MGR)实现,是一种新的MySQL 高可用解决方案。它的底层是基于MGR实现,通过类 Paoxs 算法进行数据同步,性能更好,且能保证数据的完整性。再结合管理工具 MySQL Shell、路由工具 MySQL Router 以构建一个完整的 MySQL 高可用解决方案。
对于金融用户来说,非常推荐这种高可用解决方案,建议在最新的 MySQL 8.0 版本中使用 InnoDB Cluster
MGR技术
(1)MGR技术核心
MGR 是官方在 MySQL 5.7 版本推出的一种基于状态机的数据同步机制。与半同步插件类似,MGR 是通过插件的方式启用或禁用此功能
注意,谈及 MGR时,不要简单认为它是一种新的数据同步技术,而是应该把它理解为高可用解决方案,而且特别适合应用于对于数据一致性要求极高的金融级业务场景。
首先,MGR 之间的数据同步并没有采用复制技术,而是采用 GCS(Group Communication System)协议的日志同步技术。
GSC 本身是一种类似 Paxos 算法的协议,要求组中的大部分节点都接收到日志,事务才能提交。所以,MRG 是严格要求数据一致的,特别适合用于金融级的环境。由于是类 Paxos 算法,集群的节点要求数量是奇数个,这样才能满足大多数的要求。
基于此概念,其有点像是主从复制方式中的无损半同步复制调调,但是虽然通过无损半同步复制也能保证主从数据的一致性,但通过 GCS 进行数据同步有着更好的性能:当启用 MGR 插件时,MySQL 会新开启一个端口用于数据的同步,而不是如复制一样使用MySQL 服务端口,这样会大大提升复制的效率。
其次,MGR 有两种模式:单主(Single Primary)模式、多主(Multi Primary)模式
单主模式只有 1 个节点可以写入,多主模式能让每个节点都可以写入。而多个节点之间写入,如果存在变更同一行的冲突,MySQL 会自动回滚其中一个事务,自动保证数据在多个节点之间的完整性和一致性。
在单主模式下,MGR 可以自动进行 Failover 切换,不用依赖外部的各种高可用套件,所有的事情都由数据库自己完成,比如最复杂的选主(Primary Election)逻辑,都是由 MGR 自己完成,用户不用部署额外的 Agent 等组件。
MGR 的缺点和限制在于:
- 仅支持 InnoDB 表,并且每张表一定要有一个主键;
- 目前一个 MGR 集群,最多只支持 9 个节点;
- 有一个节点网络出现抖动或不稳定,会影响集群的性能;
第 1、2 点问题不大,因为目前用 MySQL 主流的就是使用 InnoDB 存储引擎,9 个节点也足够用了
对于第 3 点,和复制不一样的是,由于 MGR 使用的是 Paxos 协议,对于网络极其敏感,如果其中一个节点网络变慢,则会影响整个集群性能。而半同步复制,比如 ACK 为1,则 1 个节点网络出现问题,并不影响整个集群的性能。所以,在决定使用 MGR 后,切记一定要严格保障网络的质量。
(1)MGR 多主模式
多主模式是一种全新的数据同步模式,在使用多主模式时,需要做一些架构上的调整,从而充分发挥 MGR 多主的优势。
冲突检测
MGR 多主模式是近几年数据库领域最大的一种创新,而且目前来看,仅 MySQL 支持这种多写的 Share Nothing 架构。
多主模式要求每个事务在本节点提交时,还要去验证其他节点是否有同样的记录也正在被修改。如果有的话,其中一个事务要被回滚。比如两个节点同时执行下面的 SQL 语句:
-- 节点1
UPDATE User set money = money - 100 WHERE id = 1;
-- 节点2
UPDATE User set money = money + 300 WHERE id = 1;
如果一开始用户的余额为 200,当节点 1 执行 SQL 后,用户余额变为 100,当节点 2 执行SQL,用户余额变味了 500,这样就导致了节点数据的不同。所以 MGR 多主模式会在事务提交时,进行行记录冲突检测,发现冲突,就会对事务进行回滚。
在上面的例子中,若节点 2 上的事务先提交,则节点 1 提交时会失败,事务会进行回滚。所以,如果要发挥多主模式的优势,就要避免写入时有冲突。**最好的做法是:每个节点写各自的数据库,比如节点 1 写 DB1,节点 2 写 DB2,节点 3 写 DB3,这样集群的写入性能就能线性提升了。**不过这要求在进行架构设计时,就做好这样的考虑,否则多主不一定能带来预期中的性能提升。
自增处理
在多主模式下,自增的逻辑发生了很大的变化。简单来说,自增不再连续自增。
因为,如果连续自增,这要求每次写入时要等待自增值在多个节点中的分配,这样性能会大幅下降,所以 MGR 多主模式下,可以通过设置自增起始值和步长来解决自增的性能问题。看下面的参数:
group_replication_auto_increment_increment = 7
参数 group_replication_auto_increment_increment 默认为 7,自增起始值就是 server-id。
假设 MGR 有 3 个节点 Node1、Node2、Node3,对应的 server-id 分别是 1、2、3, 如果这时多主插入自增的顺序为 Node1、Node1、Node2、Node3、Node1,则自增值产生的结果为
插入时间-------> | t1 | t2 | t3 | t4 | t5 |
---|---|---|---|---|---|
Node1 | 1 | 8 | 22 | ||
Node2 | 16 | ||||
Node3 | 17 |
多主模式,允许多个节点并发的产生自增值。所以自增的产生结果为1、8、16、17、22,自增值不一定是严格连续的,而仅仅是单调递增的,这与单实例 MySQL 有着很大的不同。
实际业务场景中,尽量不要使用单调的自增值做主键,在 MGR 存在问题,在后续分布式架构中也一样存在类似的自增问题。所以,对于核心业务表,还是使用有序 UUID 的方式更为可靠,性能也会更好。
基于上述概念分析,使用 MGR 技术后,所有高可用事情都由数据库自动完成,则可利用 MGR 的特性构建高可用解决方案
InnoDB Cluster
MGR 是基于 Paxos 算法的数据同步机制,将数据库状态和日志通过 Paxos 算法同步到各个节点,但如果要实现一个完整的数据库高可用解决方案,就需要更高一层级的 InnoDB Cluster 完成。一个 InnoDB Cluster 由三个组件组成:MGR 集群、MySQL Shell、MySQL Router。具体如下图所示:
其中,MySQL Shell 用来管理 MGR 集群的创建、变更等操作。一般不要手动去管理 MGR 集群,而是通过 MySQL Shell 封装的各种接口完成 MGR 的各种操作。如:
mysql-js> cluster.status()
{
"clusterName": "myCluster",
"defaultReplicaSet": {
"name": "default",
"primary": "ic-2:3306",
"ssl": "REQUIRED",
"status": "OK",
"statusText": "Cluster is ONLINE and can tolerate up to ONE failure.",
"topology": {
"ic-1:3306": {
"address": "ic-1:3306",
"mode": "R/O",
"readReplicas": {},
"role": "HA",
"status": "ONLINE"
},
"ic-2:3306": {
"address": "ic-2:3306",
"mode": "R/W",
"readReplicas": {},
"role": "HA",
"status": "ONLINE"
},
"ic-3:3306": {
"address": "ic-3:3306",
"mode": "R/O",
"readReplicas": {},
"role": "HA",
"status": "ONLINE"
}
}
},
"groupInformationSourceMember": "mysql://root@localhost:6446"
}
MySQL Router 是一个轻量级的代理,用于业务访问 MGR 集群中的数据,当 MGR 发生切换时(这里指 Single Primary 模式),自动路由到新的 MGR 主节点,这样业务就不用感知下层MGR 数据的切换(类似替换了传统机制的VIP、DNS访问机制)
为了减少引入 MySQL Router 带来的性能影响,官方建议 MySQL Router 与客户端程序部署在一起,以一种类似 sidecar 的方式进行物理部署。这样能减少额外一次额外的网络开销,基本消除引入 MySQL Router 带来的影响。因此此处 MySQL Router 的定位是一种轻量级的路由转发,而不是一个数据库中间件,主要解决数据库切换后,做到对业务无感知。
如何将数据库异常的损失降到最低
对于任何一个企业来说,数据安全的重要性是不言而喻的。能够影响数据安全的事件,都是极小概率的事件,比如说:数据库宕机、磁盘损坏甚至机房着火、删库跑路(无意),但这些事一旦发生了,业务就会损失惨重。一般来说,存储系统导致的比较严重的损失主要有两种情况:
- 数据丢失造成的直接财产损失,比如大量的坏账;
- 由于存储系统损坏,造成整个业务系统停止服务而带来的损失。
所谓防患于未然,从设计一个系统的第一天起,就需要考虑在出现各种问题的时候,如何来保证这个系统的数据安全性。此处从两个方面切入:
- 数据库定期备份和安全恢复
- 配置MySQL HA 实现高可用
1.数据库定期备份和安全恢复
保证数据安全,最简单而且有效的手段就是定期备份数据,这样出现任何问题导致的数据损失,都可以通过备份来恢复数据。但并不是简单地定期把数据备份一下就躺平,而是要考虑到应对各种各样的异常问题。此处以MySQL 为例介绍如何更安全地来做数据备份和恢复。
最简单的备份方式就是全量备份。备份的时候,把所有的数据复制一份,存放到文件中,恢复的时候再把文件中的数据复制回去,这样可以保证恢复之后数据库中的数据和备份时是完全一样的。在 MySQL 中,可以使用mysqldump 命令来执行全量备份。
# 全量备份指令(备份指定数据库到指定SQL文件),例如此处备份mydb数据库
mysqldump -uroot -p mydb > mydb-bak.sql
# 根据备份的SQL文件恢复数据
mysql -uroot mydb < mydb-bak.sql
全量备份操作虽然简单,但是其相应代价也是非常高。备份文件包含数据库中的所有数据,占用的磁盘空间非常大;其次,每次备份操作都要拷贝大量数据,备份过程中会占用数据库服务器大量的 CPU、磁盘 IO 资源,并且为了保证数据一致性,还有可能会锁表,这些都会导致备份期间,数据库本身的性能严重下降。所以,为了不对业务造成重大影响,不能经常对数据库执行全量备份。一般来说,每天执行一次全量备份已经是非常频繁了。那这就意味着,如果数据库中的数据丢了,那只能恢复到最近一次全量备份的那个时间点,这个时间点之后的数据还是丢了。也就是说,全量备份不能做到完全无损地恢复。
全量备份代价太高,不能频繁执行, 于是引入增量备份概念以更低的代价减少数据损失。相比于全量备份,增量备份每次只备份相对于上一次备份变化的那部分数据,所以每次增量备份速度更快。
MySQL 自带了 Binlog,就是一种实时的增量备份。Binlog 里面记录的就是 MySQL 数据的变更的操作日志,开启 Binlog 之后, 对 MySQL 中的每次更新数据操作,都会被记录到 Binlog 中。通过回放 Binlog把之前对数据库所有数据更新操作按照顺序重新执行了一遍,回放完成之后数据自然就恢复了。很多数据库都有类似于 MySQL Binlog 的日志,原理和 Binlog 是一样的,备份和恢复方法也是类似的。
mysql> show variables like '%log_bin%';
+---------------------------------+-----------------------------------+
| Variable_name | Value |
+---------------------------------+-----------------------------------+
| log_bin | ON |
| log_bin_basename | /usr/local/var/mysql/binlog |
+---------------------------------+-----------------------------------+
mysql> show master status;
+---------------+----------+--------------+------------------+-------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+---------------+----------+--------------+------------------+-------------------+
| binlog.000001 | 18745 | | | |
+---------------+----------+--------------+------------------+-------------------+
在实际应用中需要全量备份和增量备份结合使用来以最小的代价尽可能减少数据损失。例如每天全量备份一次数据库(mydb),然后开启binlog 使用增量备份加以辅助。假设模拟【删库跑路】场景,可以使用全量备份将数据全量恢复到最近执行全量备份的时间节点,然后回放binlog将数据恢复到执行【删库跑路】的时间节点
# 根据备份的SQL文件恢复数据
mysql -uroot mydb < mydb-bak.sql
# 回放binlog(设定回放的起止时间和binlog日志文件)
mysqlbinlog --start-datetime "2024-02-20 00:00:00" --stop-datetime "2024-02-20 15:09:00" /usr/local/var/mysql/binlog.000001 | mysql -uroot
在实际应用中还需要注意几点以确保数据备份的正确性和完整性:
- 尽量不要将备份数据和原始数据库放在同一个服务器上(为避免一些物理和不可控因素影响,不要将鸡蛋放在同一个篮子中)
- 回放Binlog的时候,指定的起始时间可以比全量备份的时间稍微提前(尽量确保全量备份之后的操作都被记录在恢复的binlog范围内,以确保数据恢复的完整性)
2.配置MySQL HA 实现高可用,避免宕机停服
通过全量备份加上 Binlog,可以将数据库恢复到任何一个时间点,这样至少不会丢数据了。如果说,数据库服务器宕机了,因为有备份数据的存在,完全可以启动一个新的数据库服务器,把备份数据恢复到新的数据库上,这样新的数据库就可以替代宕机的数据库,继续提供服务。但是,这个恢复数据的时间是很长的,如果数据量比较大的话,有可能需要恢复几个小时。这几个小时,如果系统是一直不可用的话会很大地影响业务。
那么该如何处理这个问题呢?=》未雨绸缪,主备切换
也就是说,提前准备一台备机,实时同步主数据库的数据(将数据恢复成和主库一样,然后实时同步binlog),当主数据库出现宕机的时候就可以立刻切换到备机以继续提供服务。
MySQL 的高可用方案(MySQL HA):准备一台备用的数据库,把它的数据恢复成主库一样,然后 实时地在主备数据库之间来同步 Binlog,主库做了一次数据变更,生成一条 Binlog,就把这一条 Binlog 复制到备用库并立即回放,让备用库里面的数据和主库中的数据一直保持是一致。一旦主库宕机,就可以立即切换到备用库上继续提供服务。
MySQL 自身就提供了主从复制的功能,通过配置就可以让一主一备两台 MySQL 的数据库保持数据同步,具体的配置方法可以参考 MySQ 官方文档中「复制」。当对主库执行一次更新操作的时候,主从两个数据库更新数据实际的时序分析如下:普通复制:主库写入并响应,随后将数据复制到从库
- 在主库的磁盘上写入 Binlog;
- 主库更新存储引擎中的数据;
- 给客户端返回成功响应;
- 主库把 Binlog 复制到从库;
- 从库回放 Binlog,更新存储引擎中的数据;
主从延迟问题
在主从复制的过程中,从库的数据是有可能比主库上的数据旧一些的,这个主从之间复制数据的延迟,称为「主从延迟」。正常情况下,主从延迟基本都是毫秒级别,可以认为主从就是实时保持同步的。麻烦的是不正常的情况,一旦主库或者从库繁忙的时候,有可能会出现明显的主从延迟。
而很多情况下,数据库都不是突然宕机的,而是先繁忙,性能下降,最终宕机。这种情况下,很有可能主从延迟很大,如果把业务直接切到从库上继续读写,主从延迟这部分数据就丢了,并且这个数据丢失是不可逆的。即使事后找回了当时主库的 Binlog 也是没法做到自动恢复的,因为它和从库的数据是冲突的。
即如果主库宕机并且主从存在延迟的情况下,切换到从库继续读写,虽然可以保证业务的可用性,但是主从延迟这部分数据就丢失了。此处需要做一个选择:
选项1:保证不丢数据,牺牲可用性,暂时停止服务,想办法把主库的 Binlog 恢复到从库上之后再提供服务
选项2:冒着丢一些数据的风险,保证可用性,第一时间切换到从库继续提供服务
如果希望既保证数据不丢,还能做到高可用,则不免需要牺牲一些性能。MySQL 也支持 同步复制 ,开启同步复制时,MySQL 主库会等待数据成功复制到从库之后,再给客户端返回响应。
这种牺牲性能稍加等待的方式虽然可以保证数据不丢和高可用,但是如果遇到一种极端的场景从库宕机,则会变得非常棘手。本来从库宕机对主库是完全没影响的,因为现在主库要等待从库写入成功再返回,从库宕机,主库就会一直等待从库,主库也卡死了。其解决方案为再加一个从库,把主库配置成:成功复制到任意一个从库就返回,只要有一个从库还活着,就不会影响主库写入数据,以解决了从库宕机阻塞主库的问题。如果主库发生宕机,在两个从库中,至少有一个从库中的数据是和主库完全一样的,可以把这个库作为新的主库,继续提供服务。为此需要付出的代价是至少用三台数据库服务器,并且这三台服务器提供的服务性能,还不如一台服务器高。
综合上述场景分析,上述典型的HA方案总结如下
方案 | 高可用 | 可能丢数据 | 性能 |
---|---|---|---|
一主一从 异步复制,手动切换 | 否 | 可控(可手动找回数据后切换) | 好 |
一主一从 异步复制,自动切换 | 是 | 是 | 好 |
一主二从 同步复制,自动切换 | 是 | 否 | 差 |
针对主从延迟问题可以借助采用监控mysql性能的工具观察主从延迟:
- 简单查看:可以连接到主库上使用
show slave status
命令查看- 实时监控:可以使用
pt-heatbeat
工具
- 实时监控:可以使用
同步复制时延问题
同步复制性能差,哪到底差到一个什么样的程度呢?
同步复制时延 = 异步复制的时延 + 最慢的那个从库的复制时延,理论上同步复制的时延大概是异步复制的 2-3 倍左右。
MySQL中常见的几种高可用架构部署方案
1.MySQL Replication
MySQ Replication
MySQL Replication
是官方提供的主从同步方案,用于将一个 MySQL 的实例同步到另一个实例中。Replication 为保证数据安全做了重要的保证,是目前运用最广的 MySQL 容灾方案。Replication 用两个或以上的实例搭建了 MySQL 主从复制集群,提供单点写入,多点读取的服务,实现了读的 scale out
。
一种基础的读写分离架构,基于主从复制实现,存在主从延迟问题
MySQL Group Replication(MGR)
MySQL Group Replication
组复制,又称为 MGR。是 Oracle MySQL
于 2016 年 12 月发布 MySQL 5.7.17
推出的一个全新高可用和高扩展的解决方案。引入复制组主要是为了解决传统异步复制和半同步复制可能产生数据不一致的问题。
MGR 由若干个节点共同组成一个复制组,一个事务的提交,必须经过组内大多数节点 (N / 2 + 1)
决议并通过,才能得以提交。
当客户端发起一个更新事务时,该事务先在本地执行,执行完成之后就要发起对事务的提交操作。在还没有真正提交之前,需要将产生的复制写集广播出去,复制到其它成员。因为事务是通过原子广播发送的,所以组中的成员要么都接收事务,要么都不接收事务。如果组中的所有成员收到了该广播消息(事务),那么他们会按照之前发送事务的相同顺序收到该广播消息。因此,所有组成员都以相同的顺序接收事务的写集,并为事务建立全局顺序。因此,所有组成员都以相同的顺序接收事务的写集,并为事务建立全局顺序。
在不同组成员并发执行的事务可能存在冲突。冲突是通过检查和比较两个不同并发事务的 write set
来验证的,这个过程称为认证。在认证期间,冲突检测在行级别执行的:如果在不同组成员上执行的两个并发事务更新了同一行数据,则存在冲突。根据冲突认证检测机制判断,按照顺序,第一次提交的会正常执行,第二次提交的事务会在事务发起的原始组成员上执行回滚,,组中的其他成员对该事务执行删除。如果两个事务经常发生冲突,那么最好将这两个事务放在同一个组成员中执行,这样它们在本地锁管理器的协调下将都有机会提交成功,而不至于因为处在两个不同的组成员中由于冲突认证而导致其中一个事务被频繁回滚。
最终,所有组内成员以相同的顺序接收同一组事务。因此组内成员以相同的顺序应用相同的修改,保证组内数据强一致性。
其核心优势在于:
- 避免脑裂:MGR 中不会出现脑裂的现象;
- 数据一致性保障:MGR 的冗余能力很好,能够保证
Binlog Event
至少被复制到超过一半的成员上,只要同时宕机的成员不超过半数便不会导致数据丢失。MGR还保证只要Binlog Event
没有被传输到半数以上的成员,本地成员不会将事务的Binlog Event
写入 Binlog 文件和提交事务,从而保证宕机的服务器上不会有组内在线成员上不存在的数据。因此,宕机的服务器重启后,不再需要特殊的处理就可以加入组; - 多节点写入支持:多写模式下支持集群中的所有节点都可以写入。
2.InnoDB Cluster
InnoDB Cluster
是官方提供的高可用方案,是 MySQL 的一种高可用性(HA)解决方案,它通过使用 MySQL Group Replication
来实现数据的自动复制和高可用性,InnoDB Cluster
通常包含三个关键组件
MySQL Shell
: 它是 MySQL 的高级管理客户端;MySQL Server
和MGR
,使得一组MySQL
实例能够提供高可用性,对于 MGR,Innodb Cluster
提供了一种更加易于编程的方式来处理 MGR;MySQL Router
,一种轻量级中间件,主要进行路由请求,将客户端发送过来的请求路由到不同的 MySQL 服务器节点
MySQL Server
基于 MySQL Group Replication
构建,提供自动成员管理,容错,自动故障转移动能等。InnoDB Cluster
通常以单主模式运行,一个读写实例和多个只读实例。不过也可以选用多主模式。
优点:
- 高可用性:通过
MySQL Group Replication
,InnoDB Cluster
能够实现数据在集群中的自动复制,从而保证数据的可用性; - 简单易用:
InnoDB Cluster
提供了一个简单易用的管理界面,使得管理员可以快速部署和管理集群; - 全自动故障转移:
InnoDB Cluster
能够自动检测和诊断故障,并进行必要的故障转移,使得数据可以继续可用。
- 高可用性:通过
缺点:
- 复杂性:
InnoDB Cluster
的部署和管理比较复杂,需要对 MySQL 的工作原理有一定的了解; - 性能影响:由于自动复制和高可用性的要求,
InnoDB Cluster
可能对 MySQL 的性能造成一定的影响; - 限制:
InnoDB Cluster
的功能对于一些特殊的应用场景可能不够灵活,需要更多的定制。
- 复杂性: