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


源码版本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线程。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容