*******************************************************************
dump线程的执行逻辑
主机收到COM_BINLOG_DUMP_GTID 执行com_binlog_dump_gtid (sql_parse.cc)
COM_BINLOG_DUMP 执行 com_binlog_dump
MySQL源码阅读之半同步相关
MySQL源码阅读之半同步相关
MySQL源码阅读之半同步相关
com_binlog_dump_gtid/com_binlog_dump
mysql_binlog_send
Binlog_sender::run()
init()时调用RUN_HOOK(binlog_transmit, transmit_start),执行半同步中的repl_semi_binlog_dump_start
repl_semi_binlog_dump_start调用add_slave在ack_receive线程设置监听
send_binlog执行
1、get_binlog_end_pos
wait_new_events
wait_with_heartbeat/wait_without_heartbeat
MYSQL_BIN_LOG::wait_for_update_bin_log
mysql_cond_wait/mysql_cond_timedwait 阻塞
2、send_events
send_packet


在send_events函数中发送之前调用before_send_hook,执行半同步插件中的repl_semi_before_send_event函数
repl_semi_before_send_event,调用repl_semisync.updateSyncHeader,主要是更新发送的event包头设置需要reply标记,对于group commit的flush 产生的name pos设置需要reply标记

*********************************************************************
在run结束以后执行cleanup,执行RUN_HOOK(binlog_transmit, transmit_stop)执行半同步的repl_semi_binlog_dump_end
调用 ack_receiver.remove_slave(current_thd);repl_semisync.remove_slave();解除监听
*********************************************************************
master组提交的flush->sync->commit
1、在sync以后binlog已经落盘,并singal了dump线程binlog有更新,dump发送binlog到slave
2、同时在commit之前调用hook函数,call_after_sync_hook,阻塞
实现逻辑大侄如下
call_after_sync_hook 调用 RUN_HOOK(binlog_storage, after_sync, (queue_head, log_file, pos)))
RUN_HOOK执行binlog_storage_delegate->after_sync
调用FOREACH_OBSERVER(ret, after_sync, thd, (&param, log_file, log_pos)
对每一个插件执行after_sync函数
半同步插件重新定义了after_sync指针,指向repl_semi_report_binlog_sync
最终在repl_semi_report_binlog_sync函数中调用commitTrx
commitTrx执行mysql_cond_timedwait();等待ack
3、slave io线程执行queue_event落盘以后执行hook,after_queue_event
半同步插件中定义了after_queue_event->repl_semi_slave_queue_event
repl_semi_slave_queue_event调用slaveReply,返回binlog name 和pos

4、master 半同步插件中的ack_receiver线程(poll和select)收到ack包,调用reportReplyPacket
reportReplyPacket调用handleAck,
handleAck判断是否到rpl_semi_sync_master_wait_for_slave_count, 执行reportReplyBinlog
reportReplyBinlog更新reply_file_name_, reply_file_pos_,并调用signal_waiting_sessions_up_to
signal_waiting_sessions_up_to对每一个session 事务 node执行mysql_cond_broadcast(&entry->cond);

5、ordered_commit继续执行,完成最后commit
************************************************************************

在binlog commit完成flush节点以后,调用hook函数
最终执行半同步插件中的repl_semi_report_binlog_update
repl_semi_report_binlog_update,将flush以后的binlog name pos保存起来,保存到
active_tranxs_ 私有变量
active_tranxs_主要是一个TranxNode链表,记录已经每次flush 以后的binlog name和pos

************************************************************************
半同步master 部分代码含义
TranxNode 事务节点
TranxNodeAllocator 事务节点内存分配器,当申请一个TranxNode,半同步的实现方式是一次申请一个block,每个block包含16个TranxNode,申请block时已经完成
对每个TranxNode内cond的初始化,申请一个TranxNode时,如果当前block还有可用TranxNode则用这个
如果没有(last_node == BLOCK_TRANX_NODES-1 || current_block==NULL )则申请一个新的block
ReplSemiSyncMaster类几个变量含义
commit_file_name_
commit_file_pos_ :表示已经完成flush,更新到binlog的name,pos 每次到commit的flush阶段完成就会更新这个变量

wait_file_name_
wait_file_pos_ :表示完成sync,等待半同步ack的name和pos
reply_file_name_
reply_file_pos_ :表示已经收到ack的最大name和pos
相应的有三个bool变量标记三种类型name、pos是否已经初始化
commit_file_name_inited_
wait_file_name_inited_
reply_file_name_inited_

三者之间reply <= wait <=commit
这里需要注意的是每次flush可能会有多个事务,每次sync也可能会有多个事务,但是dump发送binlog是以event为单位


例子:三次flush,binlog name和pos分别是mysql-bin.0001,100 mysql-bin.0001,200 mysql-bin.0001,300
则active_tranxs_有三个TranxNode节点{mysql-bin.0001,100} {mysql-bin.0001,200} {mysql-bin.0001,300}

但是sync时到达mysql-bin.001 250 (应该可以出现这种场景,不确定)
那么 wait_file_name_:mysql-bin.001
wait_file_pos_ :250

commitTrx执行mysql_cond_timedwait(cond);等待的是active_tranxs_第二个TranxNode的cond
当收到ack时更新 reply_file_name_和reply_file_pos_ 直到reply >wait时才会广播释放cond
而且广播的是所有小于reply_file_name_和reply_file_pos_ 的TranxNode的cond

*****************************************************************************

AckContainer是所有收到的ack管理器,内部有AckInfo指针m_ack_array和一个m_greatest_ack,m_ack_array大小是rpl_semi_sync_master_wait_for_slave_count-1
当AckInfo指针满了,新收到的不在m_ack_array的ack则已经收到了达到rpl_semi_sync_master_wait_for_slave_count的ack
那么在m_ack_array,和当前ack找打公共的最大ack,即为需要确认的ack,同时更新m_ack_array

*****************************************************************************
半同步复制超时以后退化成异步模式,把state_设成false,
在 ReplSemiSyncMaster::commitTrx
判断 if (!getMasterEnabled() || !is_on())
goto l_end;
不经过mysql_cond_timedwait()变成异步模式

当有从机连上来,是会改变state_为true恢复成半同步,
***************************************************************************
半同步退化成异步以后,有以下方式可以恢复成半同步
1、关、启rpl_semi_sync_master_enabled
2、有从库连上来
3、收到从库的ack回复时,会判断主库的半同步状态,如果关闭会打开半同步
MySQL源码阅读之半同步相关










相关文章:

  • 2021-10-01
  • 2021-11-07
  • 2021-08-24
  • 2021-09-09
  • 2021-07-25
  • 2021-08-24
  • 2021-12-07
  • 2022-12-23
猜你喜欢
  • 2022-12-23
  • 2022-01-27
  • 2022-12-23
  • 2022-12-23
  • 2021-12-16
  • 2021-08-06
  • 2022-12-23
相关资源
相似解决方案