MYSQL 半同步 (GDB查看)
 南窗  分类:IT技术  人气:146  回帖:0  发布于1年前 收藏

基础概念

mysql默认是异步复制, 但是可以使用半同步插件(semisync_master.so和semisync_slave.so)来做半同步复制, 等待至少N个(rpl_semi_sync_master_wait_for_slave_count默认1)从库收到binlog后,才返回客户端提交成功. 当然超时(rpl_semi_sync_master_timeout默认10秒)后就变成异步了

半同步有两种模式 AFTER_SYNC(默认) 和 AFTER_COMMIT 其实从名字就可以看出来: 前者是在SYNC完成之后的阶段等待从库ACK, 后者是在commit阶段完成之后等待从库ACK

写在前面

本文主要是用GDB验证半同步等待ACK的阶段. 不会详细看判断过程.

特殊情况也不在模拟了. 感兴趣的自己去模拟.

GDB的使用可以看前面的文章https://cloud.tencent.com/developer/article/2226040

环境准备

搭建半同步复制

主从搭建(略)完成之后, 主从执行如下SQL即可

install plugin rpl_semi_sync_master soname 'semisync_master.so'; -- 安装半同步插件
install plugin rpl_semi_sync_slave soname 'semisync_slave.so';

set global rpl_semi_sync_master_enabled=1; -- 启用半同步
set global rpl_semi_sync_slave_enabled=1;
set global rpl_semi_sync_master_wait_point=AFTER_SYNC; -- 设置半同步模式为AFTER_SYNC/0

stop slave; -- 从库执行即可. stop slave io_thread;start slave io_thread
start slave;

tcpdump抓包

这一步是可选的, 有的话, 更容易观察

-i ens32 指定网卡为 ens32

not arp 不要arp包

那两IP就是我的主从库的IP

tcpdump -i ens32 'not arp and ((src host 192.168.101.21 and dst host 192.168.101.19) or (src host 192.168.101.19 and dst host 192.168.101.21))' -X

GDB查看过程

分为两种情况: AFTER_SYNC 和 AFTER_COMMIT

AFTER_SYNC

打断点

既然叫after_sync, 那就是在sync之后了. 那断点自然是打在 MYSQL_BIN_LOG::flush_cache_to_file

在gdb里执行如下

set substitute-path /var/lib/pb2/sb_1-6473437-1647886122.65/mysql-5.7.38 /root/mysql_source/mysql-5.7.38
break MYSQL_BIN_LOG::flush_cache_to_file

主库执行事务并提交

堵住了, 然后去gdb瞧瞧

gdb观察

既然是after sync, 那sync阶段直接finish. 然后next往后走

当binlog写完之后, 从库就已经收到数据了, 并返回ACK了. 但我们还是要继续往下看

如果你的线程切换过去了, 你可以使用thread n 切换回来

dump发送的数据(com_binlog_dump_gtid)
从库已经有数据了

然后到了commit阶段的call_after_sync_hook, 使用step进去瞧瞧

发现有个run_hook 还是使用step进去, 然后就到了Binlog_storage_delegate::after_sync

Binlog_storage_delegate::after_sync 里面有个FOREACH_OBSERVER(遍历观察者?), 还是step继续

然后就到了半同步插件的代码. repl_semi_report_binlog_sync

这个函数就判断当前是不是 AFTER_SYNC 是的话就返回repl_semisync.commitTrx.

等待的操作就是在repl_semisync.commitTrx里面的, 这里不详细看了. 直接finish吧, 然后continue, 后面的也不想看了

返回0

sql执行完成, Rpl_semi_sync_master_yes_tx+1

AFTER_COMMIT

这个阶段在上一个阶段之后, 所以binlog肯定也写完, 并且也已经发送到从库了, 这里就不在看了.

设置主从的rpl_semi_sync_master_wait_point为AFTER_COMMIT, 然后重启从库的IO线程

set global rpl_semi_sync_master_wait_point = AFTER_COMMIT;
stop slave;
start slave;

打断点

既然叫after commit那么断点就打在commit之后 MYSQL_BIN_LOG::process_commit_stage_queue

gdb中执行如下

set substitute-path /var/lib/pb2/sb_1-6473437-1647886122.65/mysql-5.7.38 /root/mysql_source/mysql-5.7.38
break MYSQL_BIN_LOG::process_commit_stage_queue
continue

主库执行事务并提交

gdb观察

既然是commit之后, 那直接finish掉commit阶段吧, 然后next

直接就到了process_after_commit_stage_queue, 使用step进去瞧瞧

发现也是run_hook, step进去瞧瞧

然后就到了Trans_delegate::after_commit

应该就是和afer_sync的Binlog_storage_delegate::after_sync对应

Trans_delegate::after_commit里面也是有个FOREACH_OBSERVER, 一样的去瞧瞧

一路step下去也到了repl_semisync.commitTrx 还是不看了.直接continue吧

1表示AFTER_COMMIT

sql执行完成. Rpl_semi_sync_master_yes_tx+1

结论

AFTER_SYNC 是在SYNC阶段执行完成之后, 等待从库的ACK (repl_semisync.commitTrx)

AFTER_COMMIT 是在COMMIT阶段完成之后, 等待从库的ACK.

binlog是在sync之后就发送到从库了, 从库收到之后就返回ACK给dump线程.

after_sync和after_commit只是等待收到ACK的时机不同.

AFTER_SYNC函数调用过程如下

MYSQL_BIN_LOG::flush_cache_to_file 完成后
  call_after_sync_hook
    RUN_HOOK   Binlog_storage_delegate::after_sync 
      FOREACH_OBSERVER
        repl_semisync.commitTrx (如果是AFTER_SYNC的话)

AFTER_COMMIT函数调用过程如下

MYSQL_BIN_LOG::flush_cache_to_file 完成后
MYSQL_BIN_LOG::process_commit_stage_queue 完成后
  MYSQL_BIN_LOG::process_after_commit_stage_queue
    RUN_HOOK   Trans_delegate::after_commit  
      FOREACH_OBSERVER
        repl_semisync.commitTrx (如果是AFTER_COMMIT的话)

讨论这个帖子(0)垃圾回帖将一律封号处理……