MySQL高可用方案-集群扩容/半同步复制/读写分离

717 阅读13分钟

一、MySQL高可用方案

本文章仅介绍如下几种方式尽可能提高可用方案的办法,不排除还有其他方案:

主从集群(之前文章已介绍过)、集群扩容、主从半同步复制、主从读写分离、更复杂的集群架构(一主多从、互主集群、多主多从)

下面从以上几种方案来进行介绍如何使用

1-1、集群扩容

  之前已经搭建成功了一主一从的MySQL集群架构,那要扩展到一主多从的集群架构,其实就比较简单了,只需要增加一个binlog复制就行了。

  但是如果我们的集群是已经运行过一段时间,这时候如果要扩展新的从节点就有一个问题,之前的数据没办法从binlog来恢复了。这时候在扩展新的slave节点时,就需要增加一个数据复制的操作。

  MySQL的数据备份恢复操作相对比较简单,可以通过SQL语句直接来完成。具体操作可以使用mysql的bin目录下的mysqldump工具。

mysqldump -u root -p --all-databases > backup.sql
#输入密码

通过这个指令,就可以将整个数据库的所有数据导出成backup.sql,然后把这个backup.sql分发到新的MySQL服务器上,并执行下面的指令将数据全部导入到新的MySQL服务中。

mysql -u root -p < backup.sql
#输入密码

这样新的MySQL服务就已经有了所有的历史数据,然后就可以再安装之前搭建主从同步的步骤,配置Slave从服务的数据同步了。

1-2、搭建半同步复制

1-2-1、什么是半同步复制

我们已经可以搭建MySQL的主从集群,互主集群,但是我们这个集群有一个隐患,就是有可能会丢数据。这是为什么呢?这要从MySQL主从数据复制分析起。

MySQL主从集群默认采用的是一种异步复制的机制。主服务在执行用户提交的事务后,写入binlog日志,然后就给客户端返回一个成功的响应了。而binlog会由一个dump线程异步发送给Slave从服务。

image.png

由于这个发送binlog的过程是异步的。主服务在向客户端反馈执行结果时,是不知道binlog是否同步成功了的。这时候如果主服务宕机了,而从服务还没有备份到新执行的binlog,那就有可能会丢数据。

那怎么解决这个问题呢,这就要靠MySQL的半同步复制机制来保证数据安全。

半同步复制机制是一种介于异步复制和全同步复制之前的机制。主库在执行完客户端提交的事务后,并不是立即返回客户端响应,而是等待至少一个从库接收并写到relay log中,才会返回给客户端。MySQL在等待确认时,默认会等10秒,如果超过10秒没有收到ack,就会降级成为异步复制

image.png

这种半同步复制相比异步复制,能够有效的提高数据的安全性。但是这种安全性也不是绝对的,他只保证事务提交后的binlog至少传输到了一个从库,并且并不保证从库应用这个事务的binlog是成功的。另一方面,半同步复制机制也会造成一定程度的延迟,这个延迟时间最少是一个TCP/IP请求往返的时间。整个服务的性能是会有所下降的。而当从服务出现问题时,主服务需要等待的时间就会更长,要等到从服务的服务恢复或者请求超时才能给用户响应。

1-2-2、搭建半同步复制集群

半同步复制需要基于特定的扩展模块来实现。而mysql从5.5版本开始,往上的版本都默认自带了这个模块。这个模块包含在mysql安装目录下的lib/plugin目录下的semisync_master.sosemisync_slave.so两个文件中。需要在主服务上安装semisync_master模块,在从服务上安装semisync_slave模块

进入mysql插件目录

cd /usr/local/soft/mysql/lib/plugin

image.png

1-2-2-1、master安装插件

首先登陆主服务,安装semisync_master模块:

mysql> install plugin rpl_semi_sync_master soname 'semisync_master.so';
Query OK, 0 rows affected, 1 warning (0.02 sec)

mysql> show global variables like 'rpl_semi%';
+-------------------------------------------+------------+
| Variable_name                             | Value      |
+-------------------------------------------+------------+
| rpl_semi_sync_master_enabled              | OFF        |
| rpl_semi_sync_master_timeout              | 10000      |
| rpl_semi_sync_master_trace_level          | 32         |
| rpl_semi_sync_master_wait_for_slave_count | 1          |
| rpl_semi_sync_master_wait_no_slave        | ON         |
| rpl_semi_sync_master_wait_point           | AFTER_SYNC |
+-------------------------------------------+------------+
6 rows in set (0.01 sec)

mysql> set global rpl_semi_sync_master_enabled=ON;
Query OK, 0 rows affected (0.01 sec)

  • 这三行指令中

    • 第一行是通过扩展库来安装半同步复制模块,需要指定 扩展库的文件名。

    • 第二行查看系统全局参数,rpl_semi_sync_master_timeout就是半同步复制时等待应答的最长等待时间,默认是10秒,可以根据情况自行调整。

    • 第三行则是打开半同步复制的开关。

在第二行查看系统参数时,最后的一个参数rpl_semi_sync_master_wait_point其实表示一种半同步复制的方式。

半同步复制有两种方式

  一种是我们现在看到的这种默认的AFTER_SYNC方式。这种方式下,主库把日志写入binlog,并且复制给从库,然后开始等待从库的响应。从库返回成功后,主库再提交事务,接着给客户端返回一个成功响应。

  而另一种方式是叫做AFTER_COMMIT方式。他不是默认的。这种方式,在主库写入binlog后,等待binlog复制到从库,主库就提交自己的本地事务,再等待从库返回给自己一个成功响应,然后主库再给客户端返回响应

1-2-2-2、从库安装插件

登陆从服务,安装smeisync_slave模块

mysql> install plugin rpl_semi_sync_slave soname 'semisync_slave.so';
Query OK, 0 rows affected (0.01 sec)

mysql> show global variables like 'rpl_semi%';
+---------------------------------+-------+
| Variable_name | Value |
+---------------------------------+-------+
| rpl_semi_sync_slave_enabled | OFF |
| rpl_semi_sync_slave_trace_level | 32 |
+---------------------------------+-------+
2 rows in set, 1 warning (0.01 sec)

mysql> set global rpl_semi_sync_slave_enabled = on;
Query OK, 0 rows affected (0.00 sec)

mysql> show global variables like 'rpl_semi%';
+---------------------------------+-------+
| Variable_name | Value |
+---------------------------------+-------+
| rpl_semi_sync_slave_enabled | ON |
| rpl_semi_sync_slave_trace_level | 32 |
+---------------------------------+-------+
2 rows in set, 1 warning (0.00 sec)

mysql> stop slave;
Query OK, 0 rows affected (0.01 sec)

mysql> start slave;
Query OK, 0 rows affected (0.01 sec)

slave端的安装过程基本差不多,不过要注意下安装完slave端的半同步插 件后,需要重启下slave服务

1-3、主从集群与读写分离

MySQL主从集群是单向的,也就是只能从主服务同步到从服务,而从服务的数据表更是无法同步到主服务的。

所以,在这种架构下,为了保证数据一致,通常会需要保证数据只在主服务上写,而从服务只进行数据读取。这个功能,就是大名鼎鼎的读写分离。但是这里要注意下,mysql主从本身是无法提供读写分离的服务的,需要由业务自己来实现。

在MySQL主从架构中,是需要严格限制从服务的数据写入的,一旦从服务有数据写入,就会造成数据不一致。并且从服务在执行事务期间还很容易造成数据同步失败。

如果需要限制用户写数据,我们可以在从服务中将read_only参数的值设 为1( set global read_only=1; )。

在配置文件中设置(设置完成之后注意要重启服务) image.png

这样就可以限制用户写入数据。但是这个属性有两个需要注意的地方:

1、read_only=1设置的只读模式,不会影响slave同步复制的功能。 所以在MySQL slave库中设定了read_only=1后,通过 "show slave status\G" 命令查看salve状态,可以看到salve仍然会读取master上的日志,并且在slave库中应用日志,保证主从数据库同步一致;

2、read_only=1设置的只读模式, 限定的是普通用户进行数据修改的操 作,但不会限定具有super权限的用户的数据修改操作。 在MySQL中设置read_only=1后,普通的应用用户进行insert、update、delete等会 产生数据变化的DML操作时,都会报出数据库处于只读模式不能发生数据变化的错误,但具有super权限的用户,例如在本地或远程通过root用户登录到数据库,还是可以进行数据变化的DML操作; 如果需要限定 super权限的用户写数据,可以设置super_read_only=0。另外如果要 想连super权限用户的写操作也禁止,就使用"flush tables with read lock;",这样设置也会阻止主从同步复制!

1-4、其他更复杂的集群

在生产环境中,通常会以主从为基础,根据业务情况以及负载情况,搭建 更大更复杂的集群。

例如为了进一步提高整个集群的读能力,可以扩展出一主多从。而为了减轻主节点进行数据同步的压力,可以继续扩展出多级从的主从集群。

为了提高这个集群的写能力,可以搭建互主集群,即两个服务互为主从。这样不管写到哪个服务上,集群内的数据都是同步的。这样就可以用一个集群来分担写数据的压力。

以此为基础,可以扩展出多主多从的集群,全方位提升集群的数据读写能力。甚至,我们也可以扩展出环形的主从集群,实现MySQL多活部署。

搭建互主集群只需要按照上面的方式,在主服务上打开一个slave进程,并且指向slave节点的binlog当前文件地址和位置。

image.png

在主从集群中,有一个比较隐藏的问题,就是这样的主从复制之间会有延迟。这在复杂集群中,做了读写分离后,会更容易体现出来。即数据往主服务写,而读数据在从服务读。这时候这个主从复制延迟就有可能造成主库上刚插入了数据但是从库查不到。当然,这在我们目前的这个集群中是很难出现的,但是在大型集群中会很容易出现。

出现这个问题的根本在于:面向业务的主服务数据都是多线程并发写入的,而从服务是单个线程慢慢拉取binlog,这中间就会有个效率差。所以解决这个问题的关键是要让从服务也用多线程并行复制binlog数据。

1-4-1、设置slave节点并行复制binlog

MySQL自5.7版本后就已经支持并行复制了。可以在从服务上设置slave_parallel_workers为一个大于0的数,然后把slave_parallel_type参数设置为LOGICAL_CLOCK,这就可以了。

set global slave_parallel_workers=4;
set global slave_parallel_type='LOGICAL_CLOCK';

需要注意的是在设置:set global slave_parallel_type='LOGICAL_CLOCK';时,如果已经处于主从同步中,回报如下错误:

mysql> set global slave_parallel_type='LOGICAL_CLOCK';
ERROR 3017 (HY000): This operation cannot be performed with a running slave sql thread; run STOP SLAVE SQL_THREAD first

先停止同步,然后再进行设置,完成后启动同步,这样就可以完成设置了 image.png

我这边安装的是5.8版本,已经默认设置了

mysql> show global variables like 'slave_parallel%';
+------------------------+---------------+
| Variable_name          | Value         |
+------------------------+---------------+
| slave_parallel_type    | LOGICAL_CLOCK |
| slave_parallel_workers | 4             |
+------------------------+---------------+
2 rows in set (0.02 sec)

二、MySQL的其他高可用方案

之前的MySQL服务集群,都是使用MySQL自身的功能来搭建的集群。但是 这样的集群,不具备高可用的功能。即如果是MySQL主服务挂了,从服务是没办法自动切换成主服务的。而如果要实现MySQL的高可用,需要借助一些第三方工具来实现。

这一部分方案只需要了解即可,因为一方面,这些高可用方案通常都是 运维需要考虑的事情,作为开发人员没有必要花费太多的时间精力,偶 尔需要用到的时候能够用起来就够了。另一方面,随着业界技术的不断 推进,也会冒出更多的新方案。例如ShardingSphere的5.x版本的目标 实际上就是将ShardingSphere由一个数据库中间件升级成一个独立的数 据库平台,而将MySQL、PostGreSql甚至是RocksDB这些组件作为数据 库的后端支撑。等到新版本成熟时,又会冒出更多新的高可用方案。

常见的MySQL集群方案有三种: MMM、MHA、MGR。这三种高可用框架都有 一些共同点:

  • 对主从复制集群中的Master节点进行监控
  • 自动的对Master进行迁移,通过VIP。
  • 重新配置集群中的其它slave对新的Master进行同步

2-1、MMM集群

MMM(Master-Master replication managerfor Mysql,Mysql主主复制管理器)是一套由Perl语言实现的脚本程序,可以对mysql集群进行监控和故障迁移。他需要两个Master,同一时间只有一个Master对外提供服务,可以说是主备模式。

他是通过一个VIP(虚拟IP)的机制来保证集群的高可用。整个集群中,在主节点上会通过一个VIP地址来提供数据读写服务,而当出现故障时,VIP就会从原来的主节点漂移到其他节点,由其他节点提供服务。

image.png

优点:
提供了读写VIP的配置,使读写请求都可以达到高可用
工具包相对比较完善,不需要额外的开发脚本
完成故障转移之后可以对MySQL集群进行高可用监控
缺点:
故障简单粗暴,容易丢失事务,建议采用半同步复制方式,减少失败的概率
目前MMM社区已经缺少维护,不支持基于GTID的复制\

适用场景:
读写都需要高可用的
基于日志点的复制方式

2-2、MHA

Master High Availability Manager and Tools for MySQL。是由日本人开发的一个基于Perl脚本写的工具。这个工具专门用于监控主库的状态,当发现master节点故障时,会提升其中拥有新数据的slave节点成为新的master节点,在此期间,MHA会通过其他从节点获取额外的信息来避免数据一致性方面的问题。MHA还提供了mater节点的在线切换功能,即按需切换master-slave节点。MHA能够在30秒内实现故障切换,并能在故障切换过程中,最大程度的保证数据一致性。在淘宝内部,也有一个相似的TMHA产品。

MHA是需要单独部署的,分为Manager节点和Node节点,两种节点。其中 Manager节点一般是单独部署的一台机器。而Node节点一般是部署在每台MySQL机器上的。 Node节点得通过解析各个MySQL的日志来进行一些操作。

Manager节点会通过探测集群里的Node节点去判断各个Node所在机器上的 MySQL运行是否正常,如果发现某个Master故障了,就直接把他的一个Slave提升为Master,然后让其他Slave都挂到新的Master上去,完全透明。

image.png

优点:

  • MHA除了支持日志点的复制还支持GTID的方式
  • 同MMM相比,MHA会尝试从旧的Master中恢复旧的二进制日志,只是未必每次都能成功。如果希望更少的数据丢失场景,建议使用MHA架构。

缺点:\

  • MHA需要自行开发VIP转移脚本。
  • MHA只监控Master的状态,未监控Slave的状态

2-3、MGR

MGR:MySQL Group Replication。 是MySQL官方在5.7.17版本正式推出的一种组复制机制。主要是解决传统异步复制和半同步复制的数据一致性问题。

由若干个节点共同组成一个复制组,一个事务提交后,必须经过超过半数节点的决议并通过后,才可以提交。引入组复制,主要是为了解决传统异步复制和半同步复制可能产生数据不一致的问题。MGR依靠分布式一致性协议(Paxos协议的一个变体),实现了分布式下数据的最终一致性,提供了真正的数据高可用方案(方案落地后是否可靠还有待商榷)。

支持多主模式,但官方推荐单主模式:

  • 多主模式下,客户端可以随机向MySQL节点写入数据
  • 单主模式下,MGR集群会选出primary节点负责写请求,primary节点与其它节点都可以进行读请求处理.

image.png

优点:\

  • 高一致性,基于原生复制及paxos协议的组复制技术,并以插件的方式提供,提供一致数据安全保证;
  • 高容错性,只要不是大多数节点坏掉就可以继续工作,有自动检测机制,当不同节点产生资源争用冲突时,不会出现错误,按照先到者优先原则进行处理,并且内置了自动化脑裂防护机制;
  • 高扩展性,节点的新增和移除都是自动的,新节点加入后,会自动从其他节点上同步状态,直到新节点和其他节点保持一致,如果某节点被移除了,其他节点自动更新组信息,自动维护新的组信息;
  • 高灵活性,有单主模式和多主模式,单主模式下,会自动选主,所有更新操作都在主上进行;多主模式下,所有server都可以同时处理更新操作。

缺点:\

  • 仅支持InnoDB引擎,并且每张表一定要有一个主键,用于做write set的冲突检测;
  • 必须打开GTID特性,二进制日志格式必须设置为ROW,用于选主与write set;主从状态信息存于表中(--master-info-repository=TABLE 、--relay-loginfo-repository=TABLE),--log-slave-updates打开;
  • COMMIT可能会导致失败,类似于快照事务隔离级别的失败场景
  • 目前一个MGR集群最多支持9个节点
  • 不支持外键于save point特性,无法做全局间的约束检测与部分事务回滚

适用的业务场景:

  • 对主从延迟比较敏感
  • 希望对对写服务提供高可用,又不想安装第三方软件
  • 数据强一致的场景