mysql主从复制原理

mysql主从复制原理

20201220

11:24

MySQL 是以容易学习和方便的高可用架构,被开发人员青睐的。而它的几乎所有的高可用架构,都直接依赖于 binlog

 

参考 <https://time.geekbang.org/column/article/76446>

 

1、主从复制原理:

 

一条更新语句的执行过程:

主库接收到客户端的更新请求后,执行内部事务的更新逻辑,同时写 binlog

备库 B 跟主库 A 之间维持了一个长连接。主库 A 内部有一个线程,专门用于服务备库 B 的这个长连接。一个事务日志同步的完整过程是这样的:

1)在备库 B 上通过 change master 命令,设置主库 A 的 IP、端口、用户名、密码,以及要从哪个位置开始请求 binlog,这个位置包含文件名和日志偏移量。

2)在备库 B 上执行 start slave 命令,这时候备库会启动两个线程,就是图中的 io_thread 和 sql_thread。其中 io_thread 负责与主库建立连接。

3)主库 A 校验完用户名、密码后,开始按照备库 B 传过来的位置,从本地读取 binlog,发给 B。

4)备库 B 拿到 binlog 后,写到本地文件,称为中转日志(relay log)。

5)sql_thread 读取中转日志,解析出日志里的命令,并执行。

 

2、物理日志 redo log 和逻辑日志 binlog

binlog 的写入逻辑比较简单:事务执行过程中,先把日志写到 binlog cache,事务提交的时候,再把 binlog cache 写到 binlog 文件中。

 

执行器和 InnoDB 引擎在执行简单的 update 语句( update T set c=c+1 where ID=2;)时的内部流程:

1)执行器先找引擎取 ID=2 这一行。ID 是主键,引擎直接用树搜索找到这一行。如果 ID=2 这一行所在的数据页本来就在内存中,就直接返回给执行器;否则,需要先从磁盘读入内存,然后再返回。

2)执行器拿到引擎给的行数据,把这个值加上 1,比如原来是 N,现在就是 N+1,得到新的一行数据,再调用引擎接口写入这行新数据。

3)引擎将这行新数据更新到内存中,同时将这个更新操作记录到 redo log 里面,此时 redo log 处于 prepare 状态。然后告知执行器执行完成了,随时可以提交事务。

4)执行器生成这个操作的 binlog,并把 binlog 写入磁盘。

5)执行器调用引擎的提交事务接口,引擎把刚刚写入的 redo log 改成提交(commit)状态,更新完成。

 

分析一下在两阶段提交的不同时刻,MySQL 异常重启会出现什么现象:

(1)如果在图中时刻 A 的地方,也就是写入 redo log 处于 prepare 阶段之后、写 binlog 之前,发生了崩溃(crash),由于此时 binlog 还没写,redo log 也还没提交,所以崩溃恢复的时候,这个事务会回滚。这时候,binlog 还没写,所以也不会传到备库。

 

(2)在时刻 B,也就是 binlog 写完,redo log 还没 commit 前发生 crash,那崩溃恢复的时候 MySQL 会怎么处理?

  • 如果 redo log 里面的事务是完整的,也就是已经有了 commit 标识,则直接提交;
  • 如果 redo log 里面的事务只有完整的 prepare,则判断对应的事务 binlog (通过xid查找)是否存在并完整:

a.  如果是,则提交事务;

b.  否则,回滚事务。

 

 

3binlog格式

Show binary logs 查看所有binlog文件列表:

 

Show binlog event in 'mysql-bin.000005'

 

第一行 SET @@SESSION.GTID_NEXT='ANONYMOUS’你可以先忽略,后面文章我们会在介绍主备切换的时候再提到;

第二行是一个 BEGIN,跟第五行的 commit 对应,表示中间是一个事务;

第三行

Table_map event,用于说明接下来要操作的表是 test 库的表 t;

Update_rows event,用于定义修改的行为

最后一行是一个 COMMIT。你可以看到里面写着 xid=898726

XID是用来联系bin log和redo log的。崩溃恢复的时候,比如redo log里面有一个事务是prepare状态,但是不知道是不是commit状态,那就可以用XID去bin log里面查询该事务到底有没有提交。有提交则是commit状态,若没有提交则回滚该事务。

 

借助mysqlbinlog工具查看binlog内容:

mysqlbinlog -vv var/lib/mysql/mysql-bin.000005 --start-position=29770832

 

 

 

从这个图中,我们可以看到以下几个信息:

1server id 1,表示这个事务是在 server_id=1 的这个库上执行的。

2)每个 event 都有 CRC32 的值,这是因为我把参数 binlog_checksum 设置成了 CRC32

        MySQL 5.6.2 版本以后,还引入了 binlog-checksum 参数,用来验证 binlog 内容的正确性。对于 binlog 日志由于磁盘原因,可能会在日志中间出错的情况,MySQL 可以通过校验 checksum 的结果来发现。

3Table_map event 跟在图 5 中看到的相同,显示了接下来要打开的表,map 到数字 304。现在我们这条 SQL 语句只操作了一张表,如果要操作多张表呢?每个表都有一个对应的 Table_map event、都会 map 到一个单独的数字,用于区分对不同表的操作。

4)我们在 mysqlbinlog 的命令中,使用了 -vv 参数是为了把内容都解析出来,所以从结果里面可以看到各个字段的值(比如,@1=4 @2=4 这些值)。

5)最后的 Xid event,用于表示事务被正确地提交了。

 

4、循环复制问题

如果节点 A 同时是节点 B 的备库,相当于又把节点 B 新生成的 binlog 拿过来执行了一次,然后节点 A B 间,会不断地循环执行这个更新语句,也就是循环复制了。这个要怎么解决呢?

 

两个库的 server id 必须不同,如果相同,则它们之间不能设定为主备关系;

 

如果我们设置了双 M 结构,日志的执行流就会变成这样:

1)从节点 A 更新的事务,binlog 里面记的都是 A server id

2)传到节点 B 执行一次以后,节点 B 生成的 binlog server id 也是 A server id

3)再传回给节点 AA 判断到这个 server id 与自己的相同,就不会再处理这个日志。所以,死循环在这里就断掉了

 

5、主备延迟问题(拓展)

在官方的 5.6 版本之前,MySQL 只支持单线程复制,由此在主库并发高、TPS 高时就会出现严重的主备延迟问题。

 

并行复制策略:

1)按表分发策略

2)按行分发策略

3)按库分发策略(5.6版本)

4)基于 WRITESET 的并行复制(5.7.22版本)

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值