MySQL主从复制从库IO线程源码分析

mysql主从复制通过binlog来同步数据,在从库上执行start slave,会开启两个线程,分别是io线程和sql线程。io线程负责从主库拉取binlog并存储到本地的relay log,sql线程负责把relay log中的事务在从库应用。本文将结合源码分析io线程的具体实现。

MySQL源码版本:5.7.19

1. io线程函数

mysql io线程函数在源码sql/rpl_slave.cc中实现,执行命令start slave或者start slave io_thread,就会创建io线程,io线程函数为:
extern "C" void *handle_slave_io(void *arg)

io线程函数的参数arg,实际上是一个Master_info类型的指针mi,保存了主库相关的一些信息,比如host,user,password,主库uuid,主库binlog文件名,主库binlog文件位置等等。

io线程函数中会创建一个THD的对象,一个THD的对象就相当于一个连接,这个连接是可以在show processlist中看到,实际上io线程也可以理解为一个通向主库的连接。

紧接着io线程函数初始化一个MYSQL结构体的指针,称之为mysql,主要作用是与主库通信,走的标准的MySQL客户端/服务器协议,后面io线程获取主库的信息,注册从库,拉取binlog,都将依赖这个指针。

2. 创建连接

有了mi和mysql,通过调用函数safe_connect(thd, mysql, mi),连接到主库。

3. 交换信息

调用get_master_version_and_clock(mysql, mi),获取主库的版本,时间,server_id,字符集校验规则,时区等等信息,如果发现主库的server_id与从库的server_id相同,则会报错。

调用get_master_uuid(mysql, mi),获取主库的server_uuid,如果主库的server_uuid与从库的server_uuid相同,也会报错。

调用io_thread_init_commands(mysql, mi),把从库的server_uuid作为session变量设置到连接上,发送给主库。

4. 注册从库

调用 register_slave_on_master(mysql, mi, &suppress_warnings),在主库上注册从库,注册过程实际上就是发送一个注册命令COM_REGISTER_SLAVE,以及一个特定结构的网络包到主库。这个包结构如下:

  • 从库server_id,4字节
  • 从库report_host,如果为空,占用1个字节,值为0
  • 从库report_user,如果为空,占用1个字节,值为0
  • 从库report_password,如果为空,占用1个字节,值为0
  • 从库report_port,2字节
  • rpl_recovery_rank,4字节,默认为0,兼容老版本
  • master_id,4字节,值为0,由主库来填充

这些结构通过抓包,可以清楚地看到包内容与上述结构完全吻合。

5. 拉取binlog

调用request_dump(thd, mysql, mi, &suppress_warnings),向主库发送binlog拉取的命令。如果开启GTID,命令为COM_BINLOG_DUMP_GTID,如果没有开启GTID,命令为COM_BINLOG_DUMP。

请求拉取binlog的网络包结构为:

  • binlog_flags,2字节
  • 从库server_id,4字节
  • binlog名称的大小,4字节
  • binlog名称,占用字节由3决定
  • binlog位置,8字节
  • binlog数据大小,4字节
  • gtid_executed的大小,4字节
  • uuid的数量,8字节
  • uuid1,16字节
  • gtid段数量,8字节
  • gtid1 start,8字节
  • gtid1 end,8字节
  • gtid2 start,8字节(可能没有)
  • gtid2 end,8字节(可能没有)
  • uuid2....等等(可能没有)

开启GTID之后,binlog文件名和binlog位置这两个参数都是固定值,binlog文件名全为0,binlog位置为4,gtid_executed才是真正起作用的。

6. 读取binlog

请求拉取binlog的命令发送完成之后,io线程在一个while循环中,不断调用read_event(mysql, mi, &suppress_warnings) 来读取主库发送的binlog数据,并写入到relay log文件中。

7. 总结

至此io线程的整体逻辑就完成了,从上述过程来看,io线程连接到主库,与主库交换一些必要的信息,在主库上注册从库,然后请求拉取binlog,最后循环调用read_event,将主库不断发送的binlog信息写入到relay log文件中。整个过程很简单,但是需要考虑的细节有很多。了解io线程的主要流程,我们甚至可以自己写一个程序,模拟io线程,从主库拉取binlog。当然前提是需要了解MySQL的client/server的协议,或者使用现成的mysql开发库。


附录:

  • io线程函数handle_slave_io定义:sql/rpl_slave.cc
  • Master_info类定义:sql/rpl_mi.h
  • MYSQL结构体定义:include/mysql.h

文章评论

0条评论