My Little World

MySQL 集群高可用

主从模式

架构模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
                +----------+
| Master |
+----------+
/ | \
/ | \
/ | \
v v v
+---------+ +---------+ +---------+
| Slave | | Slave | | Slave |
+---------+ +---------+ +---------+
/ \
/ \
v v
+---------+ +---------+
| Slave | | Slave |
+---------+ +---------+

一主多从复制有四大优点:分摊负载、专机专用、便于冷备、高可用。

实现原理

复制概述

Replication,MySQL 的主从复制是一种数据同步机制,除了可以将一个主数据库中的数据同步复制到一个从数据库上,还可以将一个主数据库上的数据同步复制到多个从数据库上,也就是所谓的 MySQL 的一主多从复制。

多个从数据库关联到主数据库后,将主数据库上的 Binlog 日志同步地复制到了多个从数据库上,通过执行日志,每个从数据库的数据都和主数据库上的数据保持了一致。

这里面的数据更新操作表示的是所有数据库的更新操作,除了 SELECT 之类的查询读操作以外,其他的 INSERT、DELETE、UPDATE 这样的 DML 写操作,以及 CREATE TABLE、DROP TABLE、ALTER TABLE 等 DDL 操作都可以同步复制到从数据库上去。

Replication,复制使来自一个MySQL数据库服务器(源)的数据可以复制到一个或多个MySQL数据库服务器(副本)。

  • 默认情况下,复制是异步的
  • 复制品不需要永久连接即可接收源的更新
  • 根据配置,可以复制数据库中的所有数据库、选定的数据库,甚至选定的表。

MySQL中复制的优点包括:

  • 扩展解决方案:在多个副本之间分散负载以提高性能。
    • 读写分离,所有写入和更新都必须在源服务器上进行。然而,读取可能会在一个或多个副本上进行。
    • 该模型可以提高写入性能(源服务器专门用于更新),同时大幅提高越来越多的副本的读取速度。
  • 数据安全:由于副本可以暂停复制过程,因此可以在副本上运行备份服务,而不会损坏相应的源数据。
  • 分析:可以在源代码上创建实时数据,而信息分析可以在副本上进行,而不会影响源的性能。
  • 远程数据分发:可以使用复制创建本地数据副本供远程站点使用,而无需永久访问源。

异步复制


复制流程:

  • 主库 Master 将数据库的变更操作记录在二进制日志 Binary Log 中。
  • 备库 Slave 读取主库上的日志并写入到本地中继日志 Relay Log 中。
  • 备库读取中继日志 Relay Log 中的 Event 事件在备库上进行重放 Replay(回放)。

线程工作:

  1. Master 服务器上对数据库的变更操作记录在 Binlog 中。
  2. Master 的 Binlog Dump Thread 接到写入请求后读取 Binlog 推送给 Slave I/O Thread。
  3. Slave I/O Thread 将读取的 Binlog 写入到本地 relay log 文件。
  4. Slave SQL Thread 检测到 relay log 的变更请求,解析 relay log 并在从库上进行应用。


以上整个复制过程都是异步操作,所以主从复制俗称异步复制(MySQL 默认的复制策略),存在数据延迟。Master 数据变更后记录 Binlog,只是通知 Binlog Dump Thread 线程处理,然后告诉存储引擎提交事务,并不会关注 Slave 是否接受并落地 Binlog Event。

异步复制性能最好,但是一旦 Master 崩溃,发送主从切换将会发送数据不一致性的风险。

考虑到一个场景:主库正常写入数据并提交事务 T1,但是 Slave1 和 Slave2 由于某种原因(例如网络原因)一直无法接受到 Binlog Dump Thread Event 的推送请求,如果这时候 Master Crash,Slave 提升为 Master 后导致事务 T1 数据丢失。

全同步复制

指当主库执行完一个事务,所有的从库都执行了该事务才返回给客户端。因为需要等待所有从库执行完该事务才能返回,所以全同步复制的性能必然会收到严重的影响。

对于全同步复制,当主库提交事务之后,所有的从库节点必须收到,APPLY并且提交这些事务,然后主库线程才能继续做后续操作。这里面有一个很明显的缺点就是,主库完成一个事务的时间被拉长,性能降低。

半同步复制

除了内置的异步复制外,MySQL 8.0还支持由插件实现的半同步复制接口。半同步复制介于异步复制和全同步复制之间。主服务器等待至少一个副本接收并记录事件,然后提交事务。

主服务器不会等待所有副本确认接收,它只需要副本的确认,而不是事件已在副本端完全执行和提交。因此,半同步复制保证,如果主服务器崩溃,它提交的所有事务都已传输到至少一个副本。

相比异步复制,半同步复制提高了数据完整性,在一个事务提交成功之后,这个事务就至少会存在于两个地方(主服务器、某一个从服务器)。

与异步复制相比,半同步复制的性能和数据完整性的权衡。

增加的延迟是将提交发送到副本并等待副本确认接收的TCP/IP往返时间(发送binlog到从机、从机写入relay log、从机返回ack)。这意味着半同步复制最适合通过快速网络通信的近距离服务器,而最不适合通过慢网络通信的远程服务器。半同步复制还限制了二进制日志事件从源(主服务器 master)发送到副本(从服务器 slave)的速度,超时时间。当一个用户太忙时,这会减慢速度,这在某些部署情况下可能会有用。

半同步复制具体特性:

  • 从库会在连接到主库时告诉主库,它是否支持半同步。
  • 如果半同步复制在主库端是开启了的,并且至少有一个半同步复制的从库节点,那么此时主库的事务线程在提交时会被阻塞并等待,结果有两种可能,要么至少一个从库节点通知它已经收到了所有这个事务的Binlog事件,要么一直等待直到超时,如果超时则半同步复制将自动关闭,转换为异步复制。
  • 从库节点只有在接收到某一个事务的所有Binlog,将其写入并Flush到Relay Log文件之后,才会通知对应主库上面的等待线程。
  • 半同步复制必须是在主库和从库两端都开启时才行,如果在主库上没打开,或者在主库上开启了而在从库上没有开启,主库都会使用异步方式复制。

对于当前会话的客户端进行事务提交后,主库等待 ACK 的过程中有两种情况。

  1. 事务还没发送到从库,主库 crash 并发起切换,从库为新主库。客户端收到事务提交失败的信息,需要重新提交该事务。
  2. 事务已经发送到从库,主库 crash 并发起切换,从库为新主库。从库已经应用该事务并写入数据,但客户端连接重置同样会收到事务提交失败的信息,重新提交该事务时会报错数据已存在(如订单已提交成功)

复制延时解决

由于主库和从库是两个不同的数据源,主从复制过程会存在一个延时,当主库有数据写入之后,同时写入 binlog 日志文件中,然后从库通过 binlog 文件同步数据,由于需要额外执行日志同步和写入操作,这期间会有一定时间的延迟。特别是在高并发场景下,刚写入主库的数据是不能马上在从库读取的,要等待几十毫秒或者上百毫秒以后才可以。

主从复制本质是实现的数据的最终一致性,而不是强一致性。在非常短的时间内,主从机器可能会存在数据不一致的情况。

在某些对一致性要求较高的业务场景中,这种主从导致的延迟会引起一些业务问题,比如订单支付,付款已经完成,主库数据更新了,从库还没有,这时候去从库读数据,会出现订单未支付的情况,在业务中是不能接受的。

为了解决主从同步延迟的问题,通常有以下几个方法:

  1. 敏感业务强制读主库
    在开发中有部分业务需要写库后实时读数据,这一类操作通常可以通过强制读主库来解决。

  2. 关键业务不进行读写分离
    对一致性不敏感的业务,比如电商中的订单评论、个人信息等可以进行读写分离,对一致性要求比较高的业务,比如金融支付,不进行读写分离,避免延迟导致的问题。

读写分离

大多数互联网业务中,往往读多写少,这时候数据库的读会首先成为数据库的瓶颈。如果我们已经优化了SQL,但是读依旧还是瓶颈时,这时就可以选择“读写分离”架构了。读写分离首先需要将数据库分为主从库,一个主库用于写数据,多个从库完成读数据的操作,主从库之间通过主从复制机制进行数据的同步,如图所示。
写:主库
读:主库、从库

在应用中可以在从库追加多个索引来优化查询,主库这些索引可以不加,用于提升写效率。
读写分离架构也能够消除读写锁冲突从而提升数据库的读写性能。使用读写分离架构需要注意:主从同步延迟和读写分配机制问题

主从同步延迟

使用读写分离架构时,数据库主从同步具有延迟性,数据一致性会有影响,对于一些实时性要求比较高的操作,可以采用以下存储层解决方案。

  • 写后立刻读
    在写入数据库后,某个时间段内读操作就去主库,之后读操作访问从库。
  • 二次查询策略
    先去从库读取数据,找不到时就去主库进行数据读取。该操作容易将读压力返还给主库,为了避免恶意攻击,建议对数据库访问API操作进行封装,有利于安全和低耦合。
  • 根据业务特殊处理
    根据业务特点和重要程度进行调整,比如重要的,实时性要求高的业务数据读写可以放在主库。对于次要的业务,实时性要求不高可以进行读写分离,查询时去从库查询。

读写分离落地

读写路由分配机制是实现读写分离架构最关键的一个环节,就是控制何时去主库写,何时去从库读。目前较为常见的实现方案分为以下两种:

  • 基于编程和配置实现(应用层)
    程序员在代码中封装数据库的操作,代码中可以根据操作类型进行路由分配,增删改时操作主库,查询时操作从库。这类方法也是目前中小型企业生产环境下应用最广泛的。
    优点是实现简单,因为程序在代码中实现,不需要增加额外的硬件开支,缺点是需要开发人员来实现,运维人员无从下手,如果其中一个数据库宕机了,就需要修改配置重启项目。
  • 基于数据库代理实现(数据网关层)


中间件代理一般介于应用服务器和数据库服务器之间,从图中可以看到,应用服务器并不直接进入到master数据库或者slave数据库,而是进入MySQL proxy代理服务器。代理服务器接收到应用服务器的请求后,先进行判断然后转发到后端master和slave数据库。

  • MySQL Proxy:是官方提供的MySQL中间件产品可以实现负载平衡、读写分离等。
  • MyCat:MyCat是一款基于阿里开源产品Cobar而研发的,基于 Java 语言编写的开源数据库中间件。
  • ShardingSphere:ShardingSphere是一套开源的分布式数据库中间件解决方案,它由Sharding-JDBC、Sharding-Proxy和Sharding-Sidecar (计划中)这3款相互独立的产品组成。已经在2020年4月16日从Apache孵化器毕业,成为Apache顶级项目。
  • Atlas:Atlas是由 Qihoo 360公司Web平台部基础架构团队开发维护的一个数据库中间件。

主主模式

很多企业刚开始都是使用MySQL主从模式,一主多从、读写分离等。但是单主如果发生单点故障,从库切换成主库还需要作改动。因此,如果是双主或者多主,就会增加MySQL入口,提升了主库的可用性。

因此随着业务的发展,数据库架构可以由主从模式演变为双主模式。双主模式是指两台服务器互为主从,任何一台服务器数据变更,都会通过复制应用到另外一方的数据库中。

工作原理

一主多从,当主数据库宕机不可用的时候,数据依然是不能够写入的,因为数据不能够写入到从服务器上面去,从服务器是只读的。

为了解决主服务器的可用性问题,我们可以使用 MySQL 的主主复制方案。所谓的主主复制方案是指两台服务器都当作主服务器,任何一台服务器上收到的写操作都会复制到另一台服务器上。

当客户端程序对主服务器 A 进行数据更新操作的时候,主服务器 A 会把更新操作写入到 Binlog 日志中,然后 Binlog 会将数据日志同步到主服务器 B,写入到主服务器的 Relay log 中,然后执行 Relay log,获得 Relay log 中的更新日志,执行 SQL 操作写入到数据库服务器 B 的本地数据库中。B 服务器上的更新也同样通过 Binlog 复制到了服务器 A 的 Relay log 中,然后通过 Relay log 将数据更新到服务器 A 中,通过这种方式,服务器 A 或者 B 任何一台服务器收到了数据的写操作都会同步更新到另一台服务器,实现了数据库主主复制。主主复制可以提高系统的写可用,实现写操作的高可用。

如下图所示,正常情况下用户会写入到主服务器 A 中,然后数据从 A 复制到主服务器 B 上。当主服务器 A 失效的时候,写操作会被发送到主服务器 B 中去,数据从 B 服务器复制到 A 服务器。

再具体看一下主主失效的维护过程,如下图。

最开始的时候,所有的主服务器都可以正常使用,当主服务器 A 失效的时候,进入故障状态,应用程序检测到主服务器 A 失效,检测过程可能需要几秒钟或者几分钟的时间,然后应用程序需要进行失效转移,将写操作发送到备份主服务器 B 上面去,将读操作发送到 B 服务器对应的从服务器上面去。一段时间后故障结束,A 服务器需要重建失效期间丢失的数据,也就是把自己当作从服务器去从 B 服务器上面同步数据,同步完成后系统才能恢复正常。这个时候 B 服务器是用户的主要访问服务器,A 服务器当作备份服务器。

主主复制注意事项

  • 不要对两个数据库同时进行数据写操作,因为这种情况会导致数据冲突。
    两个服务器对同一条记录进行写操作,互相进行数据复制的时候,数据库就不知道哪条数据是正确的。

  • 主主复制并没有增加写并发的能力和系统存储能力。
    因为数据复制后,所有的数据库存储的数据都是一样的,不管是主主复制,还是主从复制。如果存储资源不足、磁盘不够大,数据复制后,即使写到多个服务器上,存储依然是不够用的。同时,它也没有增加写并发能力,即使使用主主复制,应用程序一个时间内也只能向一个数据库写入。

  • 更新数据表的结构会导致巨大的同步延迟。
    比如要在一张表中增加一个字段,要执行一个 ALTER TABLE 操作,这个操作会导致同步延迟巨大,因为该操作会阻塞其它的 Binlog 日志同步,这时候主数据库的很多写操作都无法同步到从数据库上面去,导致数据不一致。所以在实践中需要更新表结构的操作,不要写入到 Binlog 中,也就是关闭更新表结构的 Binlog。如果要对表结构进行更新,应该有运维工程师DBA 对所有主从数据库分别手动进行数据表结构的更新操作。

单写还是双写

使用双主双写还是双主单写?

建议大家使用双主单写,因为双主双写存在以下问题:

  • ID冲突
    • 自增策略,A(1,2)1,3,5…. B(2,2)2,4,6,…
    • 分布式ID生成解决方案,借助于雪花算法、中间件生成id
  • 写冲突
    • 两个客户端分别在同一个时刻针对同一个行数据进行更新,Thread1->A,Thread2—>B

高可用架构如下图所示,其中一个Master提供线上服务,另一个Master作为备胎供高可用切换,Master下游挂载Slave承担读请求。

随着业务发展,架构会从主从模式演变为双主模式,建议用双主单写,再引入高可用组件,例如Keepalived和MMM等工具,实现主库故障自动切换。

集群高可用

高可用性

系统高可用的挑战

一个互联网应用想要完整地呈现在最终用户的面前,需要经过很多个环节,任何一个环节出了问题,都有可能会导致系统不可用。

系统的高可用架构,说的就是如何去应对这些问题和挑战。

互联网应用可用性的度量

业务常用的描述方法是“系统的可用性达到几个 9”。这“几个 9”就表示可用水平,正如下图所示,9 越多意味着可用性越高。常说的“5 个 9”意味着系统每年故障时间小于 5.3 分钟,其计算方式也很简单:系统 1 年内服务中断维护了 5 分钟,HA = 1年/(1 年+5 分钟) = 99.999% 。

HA (可用水平) T (每年可中断时间)
99.9999% <1分钟
99.999% <5.3分钟
99.99% <53分钟
99.9% <8小时46分钟
99% <87小时36分钟

故障分类

故障分类的计算方式是用故障时间乘以故障权重来计算得到的。而故障的权重通常是在故障产生以后,根据影响程度,由运营方确定的一个故障权重值。下图是故障权重的示例。

分类 描述 权重
事故级故障 严重故障,网站整体不可用 100
A类故障 网站访问不顺畅或核心功能不可用 20
B类故障 非核心功能不可用,或核心功能少数用户不可用 5
C类故障 以上故障以外的其他故障 1

故障分=故障时间x故障权重

一般互联网应用的故障处理流程和故障时间的确定,如下图。

(流程图)

1
2
3
4
5
6
+---------------------+      +---------------------+      +---------------------+      +---------------------+      +---------------------+
| 客服报告故障 | | 提交故障给相关 | | | | 故障处理完毕 | | 确认故障归属 |
| 或 | ---> | 部门接口人 | ---> | 故障接手&处理 | ---> | 故障归档 | ---> | 记入绩效考核 |
| 监控系统发现故障 | | | | | | (故障结束时间) | | |
| (故障开始时间) | +---------------------+ +---------------------+ +---------------------+ +---------------------+
+---------------------+

高可用策略

(1)负载均衡

*   HTTP 重定向负载均衡
*   DNS 负载均衡
*   反向代理负载均衡
*   IP 层负载均衡(四层负载均衡
*   数据链路层负载均衡

(2) 数据库复制与失效转移
(3) 消息队列隔离
(4)限流和降级

*   系统高可用的另一个策略是限流和降级。主要针对的是,在高并发场景下,如果系统的访问量超过了系统的承受能力,如何对系统进行保护

(5)异地多活机房架构

*   系统高可用的另一个策略是异地多活的架构。

(6)高可用运维

*   自动化测试
*   自动化监控
    *   业务指标:用户访问量、订单量、查询量
    *   技术指标:CPU、磁盘、内存
*   预发布
*   灰度发布

高可用架构

MMM 架构

企业初期使用较多的高可用架构:

一类是基于 Keepalived + VIP + MySQL 主从/双主
一类是封装好的 MMM 集群,两者本质是一样的,MMM 相比前者多了一套工具集来帮助运维。

MMM故障处理机制:

MMM 的VIP包含writer和reader两类角色,分别对应写节点和读节点。

  • 当 writer节点出现故障,程序会自动移除该节点上的VIP
  • 写操作切换到 Master2,并将Master2设置为writer
  • 将所有Slave节点会指向Master2

除了管理双主节点,MMM 也会管理 Slave 节点,在出现宕机、复制延迟或复制错误,MMM 会移除该节点的 VIP,直到节点恢复正常。

优点 缺点
1.基于MySQL原生态复制 1.VIP无法跨网段
2.稳定成熟的开源产品 2.网络抖动误切导致集群双写
3.安装/部署/使用简单 3.MMM agent/monitor单进程,无watch dog守护
4.工具集功能强大,提供一整套HA、failover的tools 4.MMM备选主延迟过大会导致无法切换
5.读写分离和负载均衡需要程序支持
6.MMM不支持MySQL新特性,长时间没有更新

MHA架构

MHA(Master High Availability)是一套比较成熟的 MySQL 高可用方案,也是一款优秀的故障切换和主从提升的高可用软件。

MHA为了保证 Master 的高可用,通常会部署一个 Standby(备用) 角色的 Master。在 MySQL 故障切换过程中,MHA能做到在 0~30 秒内自动完成数据库的故障切换操作,并且在进行故障切换的过程中,MHA 能最大程度的保证数据的一致性,以达到真正意义上的高可用。

如下图,整个 MHA 架构分为 MHA Manager 节点和 MHA Node 节点,其中 MHA Manager 节点是单点部署,MHA Node节点是部署在每个需要监控的 MySQL 集群节点上的。MHA Manager 会定时探测集群中的 Master 节点,当 Master 出现故障时,它可以自动将最新数据的 Standby Master 或 Slave 提升为新的 Master,然后将其他的 Slave 重新指向新的Master。

MHA由两部分组成:MHA Manager(管理节点)和MHA Node(数据节点)。

  • MHA Manager可以单独部署在一台独立的机器上管理多个master-slave集群,也可以部署在一台slave节点上。负责检测master是否宕机、控制故障转移、检查MySQL复制状况等。
  • MHA Node运行在每台MySQL服务器上,不管是Master角色,还是Slave角色,都称为Node,是被监控管理的对象节点,负责保存和复制master的二进制日志、识别差异的中继日志事件并将其差异的事件应用于其他的slave、清除中继日志。

MHA Manager会定时探测集群中的master节点,当master出现故障时,它可以自动将最新数据的slave提升为新的master,然后将所有其他的slave重新指向新的master,整个故障转移过程对应用程序完全透明。

MHA故障处理机制:

  • 把宕机master的binlog保存下来
  • 根据binlog位置点找到最新的slave
  • 用最新slave的relay log修复其它slave
  • 将保存下来的binlog在最新的slave上恢复
  • 将最新的slave提升为master
  • 将其它slave重新指向新提升的master,并开启主从复制

MHA优点:

  • 自动故障转移快
  • 主库崩溃不存在数据一致性问题
  • 性能优秀,支持半同步复制和异步复制
  • 一个Manager监控节点可以监控多个集群

MHA Manager 和 MMM Monitor 一样,都没有 watch dog 守护进程,存在单点故障的问题。

首先是集群脑裂,当一部分应用程序 Client 和 Master 形成内部网络,而 Manager、Slave、Standby及其他 Client 端组成内部网络时。首先集群的复制状态中断,两边数据不一致。其次 Manager 会认为 Master 已经 Crash,会发起故障切换,例如将 Standby Master 提升为新主库,将 Slave 自动 change master 挂载到 Standby Master 节点下形成新的数据库集群。这时候集群一分为二,出现集群脑裂的故障。

其次是数据丢失,同样当出现 Master 和 Standby(最新的 Slave)节点所处的服务器Crash 或组成内部网时,Manager 只能提升为 Slave(数据非最新,存在延迟),并为新 Master 提供服务,这就导致数据丢失。虽然 MHA 可以使用半同步复制来保证数据安全,但是半同步复制在网络抖动时同样是会存在退化为异步复制的风险。

MHA 作者承诺最大程度上保证数据的一致性,但故障切换过程中存在数据丢失的风险。

QMHA架构

下图是去哪儿网基于分布式监控哨兵构建的 QMHA 高可用集群,它是加强版的 MHA 集群。

QMHA 集群能够满足数据一致性的需求,支持跨机房部署,但不支持多点写入。通过分布式哨兵选举投票来减少误切换,发起切换时会比对 GTID 来进行主从数据的快速比对,然后进行数据补齐。同时使用 Semi Sync 半同步复制提高数据安全性。

PXC/MGR

对于 MySQL,PXC 集群和 MGR 集群同样可以使用分布式监控哨兵进行集群高可用性守护

相比传统主从复制,PXC 集群和 MGR 集群支持多点写入,更完美得满足了高可用切换的需求,集群自身保证数据强一致性,不需要额外进行数据补齐操作。多点写入意味着切换时间更短,集群切换后恢复更快。