mysql 1539_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 , 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 , 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 , 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 , 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 , 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 , header=0x7ffdb802f691 , total_len=67,

need_reply=0x7fffe82193c8 , 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 , 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 , thd=0x7ffdb801b630, mi=0x6f1f8d0,

packet=0x7ffdb802f691 , 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 , 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 , 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线程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值