主从同步的重要性:
- 解决数据可靠性的问题需要用到主从同步;
- 解决 MySQL 服务高可用要用到主从同步;
- 应对高并发的时候,还是要用到主从同步。
一、MySQL 主从同步流程
当客户端提交一个事务到 MySQL 的集群,直到客户端收到集群返回成功响应,在这个过程中,MySQL 集群需要执行很多操作:
- 主库需要:
- 提交事务
- 更新存储引擎中的数据
- 把 Binlog 写到磁盘上
- 给客户端返回响应
- 把 Binlog 复制到所有从库上
- 每个从库需要
- 把复制过来的 Binlog 写到暂存日志中
- 回放这个 Binlog
- 更新存储引擎中的数据
- 给主库返回复制成功的响应
这些操作的时序非常重要,这里面的 时序,说的就是这些 操作的先后顺序。同样的操作,因为时序不同,对应用程序来说,有很大的差异。
比如说,如果先复制 Binlog,等 Binlog 复制到从节点上之后,主节点再去提交事务,这种情况下,从节点的 Binlog 一直和主节点是同步的,任何情况下主节点宕机也不会丢数据。
但如果把这个时序倒过来,先提交事务再复制 Binlog,性能就会非常好,但是存在丢数据的风险。
MySQL 提供了几个参数来配置这个时序,我们先看一下默认情况下的时序是什么样的。
二、主从同步的三种方式
1、异步复制
默认情况下,MySQL 采用异步复制的方式,执行事务操作的线程不会等复制 Binlog 的线程。
MySQL 主库在收到客户端提交事务的请求之后,会先写入 Binlog,然后再提交事务,更新存储引擎中的数据,事务提交完成后,给客户端返回操作成功的响应。
同时,从库会有一个专门的 复制线程,从主库接收 Binlog,然后把 Binlog 写到一个中继日志里面,再给主库返回复制成功的响应。
从库还有另外一个 回放 Binlog 的线程,去读中继日志,然后回放 Binlog 更新存储引擎中的数据,这个过程和我们今天讨论的主从复制关系不大,所以我并没有在图中画出来。
提交事务和复制这两个流程在不同的线程中执行,互相不会等待,这是异步复制。
掌握了异步复制的时序之后,我们就很容易理解之前几节课中讲到的一些问题的原因了。
比如说,在异步复制的情况下,为什么主库宕机存在丢数据的风险?为什么读写分离存在读到脏数据的问题?
产生这些问题,都是因为 异步复制它没有办法保证数据能第一时间复制到从库上。
异步复制的优势是性能好,缺点是数据的安全性比较差。在某一刻主从之间的数据差异可能较大,主机挂掉之后从机接管,可能会丢失一部分数据。
2、同步复制
全同步复制跟半同步复制的区别是,全同步复制必须收到所有从库的ack,才会提交事务。
同步复制这种方式在实际项目中,基本上没法用,原因有两个:
- 一是性能很差,因为要复制到所有节点才返回响应;
- 二是可用性也很差,主库和所有从库任何一个数据库出问题,都会影响业务。
全同步复制的数据一致性最好,但是性能也是最差的。
3、半同步复制
为了解决这个问题,MySQL 从 5.7 版本开始,增加一种 半同步复制(Semisynchronous Replication)的方式。
- 异步复制是,事务线程完全不等复制响应;
- 同步复制是,事务线程要等待所有的复制响应;
- 半同步复制介于二者之间,事务线程不用等着所有的复制成功响应,只要一部分复制响应回来之后,就可以给客户端返回了。
master更新操作写入binlog之后会主动通知slave,slave接收到之后写入relay log 即可应答,master只要收到至少一个ack应答,则会提交事务。
可以发现,相比较于异步复制,半同步复制需要依赖至少一个slave将binlog写入relay log,在性能上有所降低,但是可以保证至少有一个从库跟master的数据是一致的,数据的安全性提高。
半同步复制的方式,它兼顾了异步复制和同步复制的优点。如果主库宕机,至少还有一个从库有最新的数据,不存在丢数据的风险。
并且,半同步复制的性能也还凑合,也能提供高可用保证,从库宕机也不会影响主库提供服务。所以,半同步复制这种折中的复制方式,也是一种不错的选择。
三、半同步复制的注意问题
接下来我跟你说一下,在实际应用过程中,选择半同步复制需要特别注意的几个问题。
配置半同步复制的时候,有一个重要的参数 rpl_semi_sync_master_wait_no_slave,含义是:「至少等待数据复制到几个从节点再返回」。
这个数量配置的越大,丢数据的风险越小,但是集群的性能和可用性就越差。最大可以配置成和从节点的数量一样,这样就变成了同步复制。
一般情况下,配成默认值 1 也就够了,这样性能损失最小,可用性也很高,只要还有一个从库活着,就不影响主库读写。丢
数据的风险也不大,只有在恰好主库和那个有最新数据的从库一起坏掉的情况下,才有可能丢数据。
另外一个重要的参数是 rpl_semi_sync_master_wait_point,这个参数 控制主库执行事务的线程,是在提交事务之前(AFTER_SYNC)等待复制,还是在提交事务之后(AFTER_COMMIT)等待复制。默认是 AFTER_SYNC,也就是先等待复制,再提交事务,这样完全不会丢数据。AFTER_COMMIT 具有更好的性能,不会长时间锁表,但还是存在宕机丢数据的风险。
另外,虽然我们配置了同步或者半同步复制,并且要等待复制成功后再提交事务,还是有一种特别容易被忽略、可能存在丢数据风险的情况。
如果说,主库提交事务的线程等待复制的时间超时了,这种情况下事务仍然会被正常提交。并且,MySQL 会自动降级为异步复制模式,直到有足够多(rpl_semi_sync_master_wait_no_slave)的从库追上主库,才能恢复成半同步复制。如果这个期间主库宕机,仍然存在丢数据的风险。