MySQL主从复制

155 阅读10分钟

一.主从架构原理

MySQL之间数据复制的基础是二进制日志文件(binary log file)。一台MySQL数据库一旦启用二进制日志后,其作为master,它的数据库中所有操作都会以“事件”的方式记录在二进制日志中,其他数据库作为slave通过一个I/O线程与主服务器保持通信,并监控master的二进制日志文件的变化,如果发现master二进制日志文件发生变化,则会把变化复制到自己的中继日志中,然后slave的一个SQL线程会把相关的“事件”执行到自己的数据库中,以此实现从数据库和主数据库的一致性,也就实现了主从复制。

二.搭建主从复制架构

1.修改my.cnf

[mysqld]
server-id=188    #服务id
log-bin=mysql-bin #开启binlog日志
binlog_format=MIXED #binlog日志记录模式
sync_binlog=1 #主从同步策略 =0 从不同步,=1 每次事务提交同步,=n n次事务提交同步事务
binlog-do-db=test  #binlog日志记录的数据库

2创建一个从机用户,用户名为cat,ip端口任意,密码为cat123

Create user 'cat'@'%' identifide by 'cat123'

3.设置user远程从机账户拥有一个可以复制的权限

grant replication slave,replication client on *.* to 'slave'@'%'

4.查看日志开启情况和主机master状态

# 查看binlog开启情况
show variables like 'log_bin';

查看主机master状态
show master status;

5.修改该从机my.cnf

#仅仅添加server-id即可
[mysqld]
server-id=140

6.在关闭从机slave

stop slave;

7.修改从机上主机的参数

change master to master_host='39.101.67.246', master_port=3306,master_user='dog',master_password='dog123',master_log_file='mysql-bin.000004',master_log_pos=1361;

8.启动从机slave

start slave;

9.查看从机同步状态

show slave status \G;

若下面两个状态都是yes,则搭建成功

  Slave_IO_Running: yes
  Slave_SQL_Running: yes

三.MySQL主从架构原理流程

无论是“MySQL 集群如何实现主从复制”还是“当你提交一个事务到 MySQL 集群后,MySQL 集群都执行了哪些操作?”面试官主要是问你:MySQL 的主从复制的过程是怎样的?

总的来讲,MySQL 的主从复制依赖于 binlog ,也就是记录 MySQL 上的所有变化并以二进制形式保存在磁盘上。复制的过程就是将 binlog 中的数据从主库传输到从库上。这个过程一般是异步的,也就是主库上执行事务操作的线程不会等待复制 binlog 的线程同步完成。

  • mysql在收到客户端提交的请求后,会先写入binlog日志然后,跟新存储引擎数据,再提交事务,事务提交后返回客户端"操作成功"的响应
  • 从机会创建一个i/o线程,连接主机log dump线程,读取binlog日志写入relay log中继日志中,然后返回主库复制成功的响应。
  • 从库创建一个线程,去读relay log,然后回放binlog日志,更新存储引擎的数据,完成主从数据的统一。

一主多从

同时,在读流量比较大时,你可以部署多个从库共同承担读流量,这就是“一主多从”的部署方式,你在垂直电商项目中可以用该方式抵御较高的并发读流量。另外,从库也可以作为一个备库,以避免主库故障导致的数据丢失。

MySQL 一主多从

当然,一旦你提及“一主多从”,面试官很容易设陷阱问你:那大促流量大时,是不是只要多增加几台从库,就可以抗住大促的并发读请求了?

当然不是。

因为从库数量增加,从库连接上来的 I/O 线程也比较多,主库也要创建同样多的 log dump 线程来处理复制的请求,对主库资源消耗比较高,同时还受限于主库的网络带宽。所以在实际使用中,一个主库一般跟 2~3 个从库(1 套数据库,1 主 2 从 1 备主),这就是一主多从的 MySQL 集群结构。

其实,你从 MySQL 主从复制过程也能发现,MySQL 默认是异步模式:MySQL 主库提交事务的线程并不会等待 binlog 同步到各从库,就返回客户端结果。这种模式一旦主库宕机,数据就会发生丢失。

而这时,面试官一般会追问你“MySQL 主从复制还有哪些模型?” 主要有三种。

MySQL 主从复制 —— 全同步复制、异步复制、半同步复制主从复制方式有:全同步复制、异步复制、半同步复制、增强半同步 - 掘金 (juejin.cn)

  • 同步复制:事务线程要等待所有从库的复制成功响应。
  • 异步复制:事务线程完全不等待从库的复制成功响应。
  • 半同步复制:MySQL 5.7 版本之后增加的一种复制方式,介于两者之间,事务线程不用等待所有的从库复制成功响应,只要一部分复制成功响应回来就行,比如一主二从的集群,只要数据成功复制到任意一个从库上,主库的事务线程就可以返回给客户端。
[mysqld]
sync_binlog = 1
innodb_flush_log_at_trx_commit = 1
semi_sync_master_enabled = ON
semi_sync_master_timeout = 3000
semi_sync_master_wait_no_slave = OFF

这种半同步复制的方式,兼顾了异步复制和同步复制的优点,即使出现主库宕机,至少还有一个从库有最新的数据,不存在数据丢失的风险。

讲到这儿,你基本掌握了 MySQL 主从复制的原理,但如果面试官想挖掘你的架构设计能力,还会从架构设计上考察你怎么解决 MySQL 主从复制延迟的问题,比如问你“在系统设计上有哪些方案可以解决主从复制的延迟问题?”

我们来结合实际案例设计一个主从复制延迟的解决方案。

在电商平台,每次用户发布商品评论时,都会先调用评论审核,目的是对用户发布的商品评论进行如言论监控、图片鉴黄等操作。

评论在更新完主库后,商品发布模块会异步调用审核模块,并把评论 ID 传递给审核模块,然后再由评论审核模块用评论 ID 查询从库中获取到完整的评论信息。此时如果主从数据库存在延迟,在从库中就会获取不到评论信息,整个流程就会出现异常。

主从延迟影响评论读取的实时性

这是主从复制延迟导致的查询异常,解决思路有很多,我提供给你几个方案。

  • 使用数据冗余

可以在异步调用审核模块时,不仅仅发送商品 ID,而是发送审核模块需要的所有评论信息,借此避免在从库中重新查询数据(这个方案简单易实现,推荐你选择)。但你要注意每次调用的参数大小,过大的消息会占用网络带宽和通信时间。

  • 开启多线程并行回放

从 MySQL 5.7 版本开始,MySQL 支持了从机多线程回放二进制日志的方式,通常把它叫作“并行复制”,官方文档中称为“Multi-Threaded Slave(MTS)”。

MySQL 的从机并行复制有两种模式。

  1. COMMIT ORDER: 主机怎么并行,从机就怎么并行。
  2. WRITESET: 基于每个事务,只要事务更新的记录不冲突,就可以并行。

COMMIT ORDER 模式的从机并行复制,从机完全根据主服务的并行度进行回放。理论上来说,主从延迟极小。但如果主服务器上并行度非常小,事务并不小,比如单线程每次插入 1000 条记录,则从机单线程回放,也会存在一些复制延迟的情况。

而 WRITESET 模式是基于每个事务并行,如果事务间更新的记录不冲突,就可以并行。还是以“单线程每次插入 1000 条记录”为例,如果插入的记录没有冲突,比如唯一索引冲突,那么虽然主机是单线程,但从机可以是多线程并行回放!!!

所以在 WRITESET 模式下,主从复制几乎没有延迟。那么要启用 WRITESET 复制模式,你需要做这样的配置:

binlog_transaction_dependency_tracking = WRITESET

transaction_write_set_extraction = XXHASH64

slave-parallel-type = LOGICAL_CLOCK

slave-parallel-workers = 16
  • 使用缓存解决

可以在写入数据主库的同时,把评论数据写到 Redis 缓存里,这样其他线程再获取评论信息时会优先查询缓存,也可以保证数据的一致性。

不过这种方式会带来缓存和数据库的一致性问题,比如两个线程同时更新数据,操作步骤如下:

线程 A 先更新数据库为 100,此时线程 B 把数据库和缓存中的数据都更新成了 200,然后线程 A 又把缓存更新为 100,这样数据库中的值 200 和缓存中的值 100 就不一致了,解决这个问题,你可以参考 06 讲。

总的来说,通过缓存解决 MySQL 主从复制延迟时,会出现数据库与缓存数据不一致的情况。虽然它和“使用数据冗余”的方案相比并不优雅,但我还是建议你在面试中做一下补充,这样可以引出更多的技术知识,展现自己与其他人的差异。

  • 直接查询主库

该方案在使用时一定要谨慎,你要提前明确查询的数据量不大,不然会出现主库写请求锁行,影响读请求的执行,最终对主库造成比较大的压力。

当然了,面试官除了从架构上考察你对 MySQL 主从复制延迟的理解,还会问你一些扩展问题,比如:当 MySQL 做了主从分离后,对于数据库的使用方式就发生了变化,以前只需要使用一个数据库地址操作数据库,现在却要使用一个主库地址和多个从库地址,并且还要区分写入操作和查询操作,那从工程代码上设计,怎么实现主库和从库的数据访问呢?

实现主库和从库的数据库访问

一种简单的做法是:提前把所有数据源配置在工程中,每个数据源对应一个主库或者从库,然后改造代码,在代码逻辑中进行判断,将 SQL 语句发送给某一个指定的数据源来处理。

这个方案简单易实现,但 SQL 路由规则侵入代码逻辑,在复杂的工程中不利于代码的维护。

另一个做法是:独立部署的代理中间件,如 MyCat,这一类中间件部署在独立的服务器上,一般使用标准的 MySQL 通信协议,可以代理多个数据库。

该方案的优点是隔离底层数据库与上层应用的访问复杂度,比较适合有独立运维团队的公司选型;缺陷是所有的 SQL 语句都要跨两次网络传输,有一定的性能损耗,再就是运维中间件是一个专业且复杂的工作,需要一定的技术沉淀。