美文网首页
MySQL:半同步(三)从库端初始化和回调函数

MySQL:半同步(三)从库端初始化和回调函数

作者: 重庆八怪 | 来源:发表于2021-02-10 18:22 被阅读0次

源码版本5.7.29

一、全局变量

semisync_slave_plugin.cc

ReplSemiSyncSlave repl_semisync;
/*
  indicate whether or not the slave should send a reply to the master.

  This is set to true in repl_semi_slave_read_event if the current
  event read is the last event of a transaction. And the value is
  checked in repl_semi_slave_queue_event.
*/
bool semi_sync_need_reply= false;

semisync.cc

char rpl_semi_sync_slave_enabled;
char rpl_semi_sync_slave_status= 0;
unsigned long rpl_semi_sync_slave_trace_level;

二、相关信息

类ReplSemiSyncSlave

继承自ReplSemiSyncBase

  /* True when initObject has been called */
  bool init_done_;//插件是否初始化
  bool slave_enabled_;        /* semi-sycn is enabled on the slave */ //完全根据参数semi_sync_slave_enabled设置进行更改
  MYSQL *mysql_reply;         /* connection to send reply */
半同步hook 从库端
Binlog_relay_IO_observer relay_io_observer = {
  sizeof(Binlog_relay_IO_observer), // len

  repl_semi_slave_io_start, // start  OK
  repl_semi_slave_io_end,   // stop  OK
  repl_semi_slave_sql_start,    // start sql thread  空
  repl_semi_slave_sql_stop,     // stop sql thread   空
  repl_semi_slave_request_dump, // request_transmit  OK
  repl_semi_slave_read_event,   // after_read_event    OK
  repl_semi_slave_queue_event,  // after_queue_event OK
  repl_semi_reset_slave,    // reset 空
};
反馈ack的信息
/* The layout of a semisync slave reply packet:
   1 byte for the magic num
   8 bytes for the binlog positon
   n bytes for the binlog filename, terminated with a '\0'
*/
#define REPLY_MAGIC_NUM_LEN 1
#define REPLY_BINLOG_POS_LEN 8
#define REPLY_BINLOG_NAME_LEN (FN_REFLEN + 1)
#define REPLY_MESSAGE_MAX_LENGTH \
  (REPLY_MAGIC_NUM_LEN + REPLY_BINLOG_POS_LEN + REPLY_BINLOG_NAME_LEN)
#define REPLY_MAGIC_NUM_OFFSET 0
#define REPLY_BINLOG_POS_OFFSET (REPLY_MAGIC_NUM_OFFSET + REPLY_MAGIC_NUM_LEN)
#define REPLY_BINLOG_NAME_OFFSET (REPLY_BINLOG_POS_OFFSET + REPLY_BINLOG_POS_LEN)

三、初始化

执行语句
INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';

#0  semi_sync_slave_plugin_init (p=0x354bd40) at /home/mysql/soft/percona-server-5.7.29-32/plugin/semisync/semisync_slave_plugin.cc:230
#1  0x00000000014e7cf0 in plugin_initialize (plugin=0x354bd40) at /home/mysql/soft/percona-server-5.7.29-32/sql/sql_plugin.cc:1279
#2  0x00000000014ea354 in mysql_install_plugin (thd=0x7ffddc000c00, name=0x7ffddc0066e8, dl=0x7ffddc0066f8) at /home/mysql/soft/percona-server-5.7.29-32/sql/sql_plugin.cc:2279
#3  0x00000000014f0547 in Sql_cmd_install_plugin::execute (this=0x7ffddc0066e0, thd=0x7ffddc000c00) at /home/mysql/soft/percona-server-5.7.29-32/sql/sql_plugin.cc:4664
#4  0x00000000014c0054 in mysql_execute_command (thd=0x7ffddc000c00, first_level=true) at /home/mysql/soft/percona-server-5.7.29-32/sql/sql_parse.cc:5154
#5  0x00000000014c2025 in mysql_parse (thd=0x7ffddc000c00, parser_state=0x7fffe81d64a0, update_userstat=false) at /home/mysql/soft/percona-server-5.7.29-32/sql/sql_parse.cc:5927
#6  0x00000000014b6c5f in dispatch_command (thd=0x7ffddc000c00, com_data=0x7fffe81d6c90, command=COM_QUERY) at /home/mysql/soft/percona-server-5.7.29-32/sql/sql_parse.cc:1539
#7  0x00000000014b5a94 in do_command (thd=0x7ffddc000c00) at /home/mysql/soft/percona-server-5.7.29-32/sql/sql_parse.cc:1060
#8  0x00000000015e9d32 in handle_connection (arg=0x36a8a20) at /home/mysql/soft/percona-server-5.7.29-32/sql/conn_handler/connection_handler_per_thread.cc:325
#9  0x00000000018b97f2 in pfs_spawn_thread (arg=0x36d4480) at /home/mysql/soft/percona-server-5.7.29-32/storage/perfschema/pfs.cc:2198
#10 0x00007ffff7bc6ea5 in start_thread () from /lib64/libpthread.so.0
#11 0x00007ffff5f2b8dd in clone () from /lib64/libc.so.6

相关调用过程

semi_sync_slave_plugin_init
  ->ReplSemiSyncSlave::initObject
    ->if (init_done_) 如果已经初始化
      打印警告
      sql_print_warning("%s called twice", kWho);
    init_done_ = true; //设置为已经初始化
    setSlaveEnabled(rpl_semi_sync_slave_enabled); //根据参数semi_sync_slave_enabled设置插件状态,默认初始化为flase
      ->slave_enabled_ = enabled;
    setTraceLevel(rpl_semi_sync_slave_trace_level);//根据参数semi_sync_slave_trace_level设置日志级别
      ->trace_level_ = trace_level;
  -> (register_binlog_relay_io_observer(&relay_io_observer, p))
    进行插件回调函数注册

四、回调函数repl_semi_slave_io_start

调用者:IO线程

(gdb) bt
#0  ReplSemiSyncSlave::slaveStart (this=0x7ffdb2c953b0 <repl_semisync>, param=0x7ffdb2a8fb20) at /home/mysql/soft/percona-server-5.7.29-32/plugin/semisync/semisync_slave.cc:82
#1  0x00007ffdb2a93889 in repl_semi_slave_io_start (param=0x7ffdb2a8fb20) at /home/mysql/soft/percona-server-5.7.29-32/plugin/semisync/semisync_slave_plugin.cc:146
#2  0x00000000013dd878 in Binlog_relay_IO_delegate::thread_start (this=0x2d1ca40 <delegates_init()::relay_io_mem>, thd=0x7ffdc8002bd0, mi=0x7ffddd16eaa0)
    at /home/mysql/soft/percona-server-5.7.29-32/sql/rpl_handler.cc:868
#3  0x0000000001836320 in handle_slave_io (arg=0x7ffddd16eaa0) at /home/mysql/soft/percona-server-5.7.29-32/sql/rpl_slave.cc:5687
#4  0x00000000018b97f2 in pfs_spawn_thread (arg=0x7ffddcd20ea0) at /home/mysql/soft/percona-server-5.7.29-32/storage/perfschema/pfs.cc:2198
#5  0x00007ffff7bc6ea5 in start_thread () from /lib64/libpthread.so.0
#6  0x00007ffff5f2b8dd in clone () from /lib64/libc.so.6
(gdb) p param->server_id
$1 = 623306
(gdb) p param->thread_id
$2 = 154
(gdb) 

回调时机

handle_slave_io
  ->初始化IO线程属性 init_slave_thread
  ->(RUN_HOOK(binlog_relay_io, thread_start, (thd, mi))
  ->safe_connect 进行连接主库
  ->request_dump 发送gtid,获取主库信息等语句

具体过程

Binlog_relay_IO_delegate::thread_start(THD *thd, Master_info *mi)
   输入参数为IO线程的线程的processlist id
   ->repl_semi_slave_io_start
    ->semi_sync= getSlaveEnabled();//判断是否开启了半同步 slave_enabled_,由参数semi_sync_slave_enabled设置进行更改控制
    ->输出日志级别信息
      "Slave I/O thread: Start %s replication to master '%s@%s:%d' in log '%s' at position %lu"
    ->if (semi_sync && !rpl_semi_sync_slave_status)
      如果是半同步,但是状态不对则进行修改
      rpl_semi_sync_slave_status= 1;

这个回调函数比较简单,只是设置了从库是否使用为半同步方式,但是随后还会修改,参考repl_semi_slave_request_dump回调函数。

五、回调函数repl_semi_slave_io_end

调用者:IO线程

#0  ReplSemiSyncSlave::slaveStop (this=0x7ffdb2c953b0 <repl_semisync>, param=0x7ffdb2a8fb20) at /home/mysql/soft/percona-server-5.7.29-32/plugin/semisync/semisync_slave.cc:98
#1  0x00007ffdb2a938ad in repl_semi_slave_io_end (param=0x7ffdb2a8fb20) at /home/mysql/soft/percona-server-5.7.29-32/plugin/semisync/semisync_slave_plugin.cc:151
#2  0x00000000013ddaa4 in Binlog_relay_IO_delegate::thread_stop (this=0x2d1ca40 <delegates_init()::relay_io_mem>, thd=0x7ffdc8002bd0, mi=0x7ffddd16eaa0)
    at /home/mysql/soft/percona-server-5.7.29-32/sql/rpl_handler.cc:882
#3  0x0000000001837906 in handle_slave_io (arg=0x7ffddd16eaa0) at /home/mysql/soft/percona-server-5.7.29-32/sql/rpl_slave.cc:6076
#4  0x00000000018b97f2 in pfs_spawn_thread (arg=0x7ffddcd20ea0) at /home/mysql/soft/percona-server-5.7.29-32/storage/perfschema/pfs.cc:2198
#5  0x00007ffff7bc6ea5 in start_thread () from /lib64/libpthread.so.0
#6  0x00007ffff5f2b8dd in clone () from /lib64/libc.so.6

回调时机

handle_slave_io
 ->io线程循环退出
 ->RUN_HOOK(binlog_relay_io, thread_stop, (thd, mi)); //回调stop

具体过程

Binlog_relay_IO_delegate::thread_stop
 ->repl_semi_slave_io_end
   ->ReplSemiSyncSlave::slaveStop
     if (rpl_semi_sync_slave_status)
     rpl_semi_sync_slave_status= 0;

这个回调函数也很简单只是将 rpl_semi_sync_slave_status状态设置为OFF

六、回调函数repl_semi_slave_request_dump

调用者:IO线程

#0  repl_semi_slave_request_dump (param=0x7ffdb2a8f830, flags=0) at /home/mysql/soft/percona-server-5.7.29-32/plugin/semisync/semisync_slave_plugin.cc:52
#1  0x00000000013de14a in Binlog_relay_IO_delegate::before_request_transmit (this=0x2d1ca40 <delegates_init()::relay_io_mem>, thd=0x7ffdd0000a90, mi=0x7ffddd16eaa0, flags=0)
    at /home/mysql/soft/percona-server-5.7.29-32/sql/rpl_handler.cc:922
#2  0x0000000001832dd9 in request_dump (thd=0x7ffdd0000a90, mysql=0x7ffdd000e7e0, mi=0x7ffddd16eaa0, suppress_warnings=0x7ffdb2a8fccb)
    at /home/mysql/soft/percona-server-5.7.29-32/sql/rpl_slave.cc:4426
#3  0x0000000001836a71 in handle_slave_io (arg=0x7ffddd16eaa0) at /home/mysql/soft/percona-server-5.7.29-32/sql/rpl_slave.cc:5826
#4  0x00000000018b97f2 in pfs_spawn_thread (arg=0x7ffddc042190) at /home/mysql/soft/percona-server-5.7.29-32/storage/perfschema/pfs.cc:2198
#5  0x00007ffff7bc6ea5 in start_thread () from /lib64/lib

回调方式:

handle_slave_io
  ->初始化IO线程属性 init_slave_thread
  ->(RUN_HOOK(binlog_relay_io, thread_start, (thd, mi))
  ->safe_connect 进行连接主库
  ->request_dump 发送gtid,获取主库信息等语句
    ->首先回调RUN_HOOK(binlog_relay_io,before_request_transmit,(thd, mi, binlog_flags))
    ->其他参考https://www.jianshu.com/p/a81d62bf6b31

具体过程

Binlog_relay_IO_delegate::before_request_transmit
  param.server_id= thd->server_id;//获取server_id
  param.thread_id= thd->thread_id(); //获取dump线程processlist id
  ->repl_semi_slave_request_dump
    ->(!repl_semisync.getSlaveEnabled())
      根据slave_enabled_是否设置为1,如果没有则说明半同步没开启,
      直接返回
    ->SELECT @@global.rpl_semi_sync_master_enabled
      首先探测主库是否安装半同步插件,如果没有安装报错,也就是报错ER_UNKNOWN_SYSTEM_VARIABLE
      Master server does not support semi-sync, fallback to asynchronous replication
      并且关闭从库办同步
      rpl_semi_sync_slave_status=0
    ->然后告诉主库,从库使用的是半同步
      "SET @rpl_semi_sync_slave= 1"
     设置rpl_semi_sync_slave_status= 1

我们可以看到这里在和主库进行交互,实际探测主库是否安装了半同步插件,没有的话rpl_semi_sync_slave_status也会在io线程启动时刻设置为OFF。

七、回调函数repl_semi_slave_read_event

调用者:IO线程

(gdb) bt
#0  ReplSemiSyncSlave::slaveReadSyncHeader (this=0x7fffe82193b0 <repl_semisync>, header=0x7ffdb802f691 <incomplete sequence \357>, total_len=67, 
    need_reply=0x7fffe82193c8 <semi_sync_need_reply>, payload=0x7ffe00844cc0, payload_len=0x7ffe00844cb8) at /home/mysql/soft/percona-server-5.7.29-32/plugin/semisync/semisync_slave.cc:57
#1  0x00007fffe80177e4 in repl_semi_slave_read_event (param=0x7ffe00844b20, packet=0x7ffdb802f691 <incomplete sequence \357>, len=67, event_buf=0x7ffe00844cc0, event_len=0x7ffe00844cb8)
    at /home/mysql/soft/percona-server-5.7.29-32/plugin/semisync/semisync_slave_plugin.cc:119
#2  0x00000000013de3a1 in Binlog_relay_IO_delegate::after_read_event (this=0x2d1ca40 <delegates_init()::relay_io_mem>, thd=0x7ffdb801b630, mi=0x6f1f8d0, 
    packet=0x7ffdb802f691 <incomplete sequence \357>, len=67, event_buf=0x7ffe00844cc0, event_len=0x7ffe00844cb8) at /home/mysql/soft/percona-server-5.7.29-32/sql/rpl_handler.cc:937
#3  0x00000000018370ae in handle_slave_io (arg=0x6f1f8d0) at /home/mysql/soft/percona-server-5.7.29-32/sql/rpl_slave.cc:5929
#4  0x00000000018b97f2 in pfs_spawn_thread (arg=0x7ffdec0239b0) at /home/mysql/soft/percona-server-5.7.29-32/storage/perfschema/pfs.cc:2198
#5  0x00007ffff7bc6ea5 in start_thread () from /lib64/libpthread.so.0
#6  0x00007ffff5f2b8dd in clone () from /lib64/libc.so.6

回调方式

handle_slave_io
 ->读取event  read_event
 ->RUN_HOOK(binlog_relay_io, after_read_event,(thd, mi,(const char*)mysql->net.read_pos + 1,event_len, &event_buf, &event_len))
 ->写入relay log queue_event

具体过程

Binlog_relay_IO_delegate::after_read_event
 ->repl_semi_slave_read_event
  ->ReplSemiSyncSlave::slaveReadSyncHeader(const char *header,unsigned long total_len,bool  *need_reply,const char **payload,unsigned long *payload_len)
    ->((unsigned char)(header[0]) == kPacketMagicNum) //判断是否为半同步信息
     *need_reply  = (header[1] & kPacketFlagSync); //本event是否需要进行ACK
     *payload_len = total_len - 2;//长度总数减去2
     *payload     = header + 2;//偏移量+2
     如果trace_level_设置为16则对于需要回复的event的会打印日志如下
     sql_print_information("%s: reply - %d", kWho, *need_reply);

可以看到这个回调函数主要通过读取event,然后通过kPacketFlagSync去判断是否本event需要进行ack反馈。注意是否需要反馈是放在semi_sync_need_reply里面的,但是这是全局变量(多源复制如何处理?多源复制不支持半同步)

八、回调函数repl_semi_slave_queue_event

调用者:IO线程
注意本函数内部会做replay的判断,根据前面的semi_sync_need_reply进行判断如果不是事务的最后一个event则不需要反馈ack

(gdb) bt
#0  ReplSemiSyncSlave::slaveReply (this=0x7fffe82193b0 <repl_semisync>, mysql=0x7ffdb8022200, binlog_filename=0x6f21988 "log_bin.000002", binlog_filepos=1748)
    at /home/mysql/soft/percona-server-5.7.29-32/plugin/semisync/semisync_slave.cc:110
#1  0x00007fffe8017860 in repl_semi_slave_queue_event (param=0x7ffe00844b20, event_buf=0x7ffdb802f693 "\341\244 `\020w\002", event_len=31, flags=0)
    at /home/mysql/soft/percona-server-5.7.29-32/plugin/semisync/semisync_slave_plugin.cc:139
#2  0x00000000013de605 in Binlog_relay_IO_delegate::after_queue_event (this=0x2d1ca40 <delegates_init()::relay_io_mem>, thd=0x7ffdb801b630, mi=0x6f1f8d0, 
    event_buf=0x7ffdb802f693 "\341\244 `\020w\002", event_len=31, synced=false) at /home/mysql/soft/percona-server-5.7.29-32/sql/rpl_handler.cc:957
#3  0x00000000018371c1 in handle_slave_io (arg=0x6f1f8d0) at /home/mysql/soft/percona-server-5.7.29-32/sql/rpl_slave.cc:5949
#4  0x00000000018b97f2 in pfs_spawn_thread (arg=0x7ffdec0239b0) at /home/mysql/soft/percona-server-5.7.29-32/storage/perfschema/pfs.cc:2198
#5  0x00007ffff7bc6ea5 in start_thread () from /lib64/libpthread.so.0
#6  0x00007ffff5f2b8dd in clone () from /lib64/libc.so.6

回调方式

handle_slave_io
->读取event  read_event
->RUN_HOOK(binlog_relay_io, after_read_event,(thd, mi,(const char*)mysql->net.read_pos + 1,event_len, &event_buf, &event_len))
->写入relay log queue_event
->(RUN_HOOK(binlog_relay_io, after_queue_event,(thd, mi, event_buf, event_len, synced)))

具体过程:

Binlog_relay_IO_delegate::after_queue_event(THD *thd, Master_info *mi,const char *event_buf,ulong event_len,bool synced)
 此处传入的是event和是否写到了磁盘synced,但是synced并未使用
 ->repl_semi_slave_queue_event
   ->(rpl_semi_sync_slave_status && semi_sync_need_reply)
     判断是否开启的半同步,同时判断本event是否需要反馈ACK
     ->ReplSemiSyncSlave::slaveReply((MYSQL *mysql,const char *binlog_filename,my_off_t binlog_filepos))
       函数传入了反馈的主库binlog位点
       ->reply_buffer[REPLY_MAGIC_NUM_LEN + REPLY_BINLOG_POS_LEN + REPLY_BINLOG_NAME_LEN];
         构建反馈ACK的信息 1字节魔术数+8字节位点+最大(512+1)字节binlog文件名
       ->如果trace_level_设置为16输出日志
         "%s: reply (%s, %lu)", kWho,binlog_filename, (ulong)binlog_filepos)
       ->进行发送

可以看到这个回调函数,实际的构建了需要反馈的信息给主库的Ack_recevier线程。

相关文章

网友评论

      本文标题:MySQL:半同步(三)从库端初始化和回调函数

      本文链接:https://www.haomeiwen.com/subject/gvjkxltx.html