searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

主从复制关系

2024-09-04 09:41:43
4
0

1. 配置主从关系

  1. 在从库执行change master命令
  2. 从库收到SQLCOM_CHANGE_MASTER最后执行主要函数change_master()
    1. 主要是配置主从信息Master_info(IO线程相关信息),Relay_log_info(SQL线程相关信息)
    2. 配置slave_master_info,auto_position,relay_log_purge

2. 建立连接

从库执行start slave,然后从库会开启两个线程,io线程与sql线程;io线程负责拉取binlog存储到本地relaylog中,sql则负责把拉取过来的relay log应用到从库中

mysql_execute_command()
	|start_slave_cmd()
		|start_slave()
			|start_slave_threads()
				|start_slave_thread(handle_slave_io) 	// io线程
				|start_slave_thread(handle_slave_sql) 	// sql线程

3. IO线程:

  1. 入口函数handle_slave_io:

传入的参数为配置主从关系中的Master_info(mi),保存主库相关信息,如host,user,passwd,uuid等

在io线程中会创建一个thd,以及一个MYSQL, 用于连接到主库

  1. 创建连接

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

  1. 获取主库信息
    1. get_master_version_and_clock获取主库版本,serverid(确保主从的不一致),时区等
    2. get_master_uuid获取主库uuid
    3. io_thread_init_commands:初始化一些(会话)变量,如slave_uuid(在主库的连接中),用于后面的连接
  1. 注册从库

register_slave_on_master:主要是发送一个COM_REGISTER_SLAVE命令给主库,以及包含从库信息的包

  1. 注册完成后,

通过request_dump函数向主库发送COM_BINLOG_DUMP命令,后续主库会创建dump线程用来发送binlog

  1. 随后从库开始通过read_event()读取binlog,并写入到relaylog文件中

4. SQL线程:

  1. 入口函数handle_slave_sql

传入的参数为配置主从关系中的Master_info(mi),但是用的是Relay_log_info(mi->rli),主要包含relay日志相关的信息,如offset,master log name等

  1. 大致逻辑如下:
handle_slave_sql()
	|slave_start_workers() // 开启工作线程,用于多线程处理
	|init_relay_log_pos() // 打开并初始化relaylog

	while (!sql_slave_killed(thd,rli)) { // 进入循环
	|exec_relay_log_event() // 执行并处理relaylog
		|next_event() // 获取下一个事件
		|apply_event_and_update_pos()
			|ev->apply_event() // 应用事件
				|do_apply_event() // apply
			|ev->update_pos()  // 更新日志位置
	}

5. 主库dump线程:

  1. 注册从库

在收到从库发来的COM_REGISTER_SLAVE后,会调用register_slave来注册从库,主要把从主库发来的数据放到

(SLAVE_INFO)si中,并把si添加到slave_list中;slave_list是一个全局变量, 是一个HASH表,用来存放从库的信息

这里在添加sislave_list前会先做一次unregister_replica, 把存在于slave_listserver_id相同的从库移除

typedef struct st_slave_info
{
  uint32 server_id;
  uint32 rpl_recovery_rank, master_id;
  char host[HOSTNAME_LENGTH+1];
  char user[USERNAME_LENGTH+1];
  char password[MAX_PASSWORD_LENGTH+1];
  uint16 port;
  THD* thd;
} SLAVE_INFO;
  1. 开始发送binlog

主库收到COM_BINLOG_DUMP_GTID命令后,与注册的是同一个连接(thd),会调用com_binlog_dump_gtid函数处理从库拉取binlog的请求, 主要逻辑如下:

    1. 读取从库的信息,如serverid,binlog位置等
    2. kill_zombie_dump_threads():kill掉僵尸线程,指的是uuid与当前从库相同的dump线程。为什么要清理?
      1. 因为如果主库空闲的话,dump线程会等待binlog更新,如果在这期间,io线程重连,那么就会存在旧的dump线程 。
      2. 如果主库长时间空闲,此时从库又触发重连,那么会出现两个dump线程,直到主库有binlog写入
    1. 发送binlog, 这里调用mysql_binlog_send来执行
sender.run()
  while {
    file= open_binlog_file // 打开binlog文件
    send_binlog()
      while {
        |get_binlog_end_pos() // 获取binlog pos
        | while {
        |    mysql_bin_log.get_binlog_end_pos() // 从binlog中获取
        |    wait_new_events() // 未获取到,等待新binlog
        |      while {
        |       // 与heartbeat_period参数有关,
        |        wait_with_heartbeat() // 每heartbeat_period则给从库发送一次心跳
        |        wait_without_heartbeat() // timeout = 0
        |          mysql_bin_log.wait_for_update_bin_log(thd, timeout)
        |          // 等待binlog,直到超时 这里是通过update_cond事件来实现的
        |      }
        |  }
        |send_events()
        |  while {
        |    read_event() // 读取event
        |    // 会先判断是否event已经在从库中应用
        |    send_packet() // 发送数据
        | }
      }
  }
  1. dump线程退出

这里可能是从库执行stop slave,或者是Binlog_sender内部出错

然后会调用unregister_slave()函数注销从库,主要是把sislave_list中移除

如果是通过stop slave退出,在执行完stop slave后,dump线程不会立马退出,而是在给从库发送binlog或者心跳包时,超时后才会退出循环; 所以在stop s

0条评论
0 / 1000
qinyl
6文章数
0粉丝数
qinyl
6 文章 | 0 粉丝
qinyl
6文章数
0粉丝数
qinyl
6 文章 | 0 粉丝
原创

主从复制关系

2024-09-04 09:41:43
4
0

1. 配置主从关系

  1. 在从库执行change master命令
  2. 从库收到SQLCOM_CHANGE_MASTER最后执行主要函数change_master()
    1. 主要是配置主从信息Master_info(IO线程相关信息),Relay_log_info(SQL线程相关信息)
    2. 配置slave_master_info,auto_position,relay_log_purge

2. 建立连接

从库执行start slave,然后从库会开启两个线程,io线程与sql线程;io线程负责拉取binlog存储到本地relaylog中,sql则负责把拉取过来的relay log应用到从库中

mysql_execute_command()
	|start_slave_cmd()
		|start_slave()
			|start_slave_threads()
				|start_slave_thread(handle_slave_io) 	// io线程
				|start_slave_thread(handle_slave_sql) 	// sql线程

3. IO线程:

  1. 入口函数handle_slave_io:

传入的参数为配置主从关系中的Master_info(mi),保存主库相关信息,如host,user,passwd,uuid等

在io线程中会创建一个thd,以及一个MYSQL, 用于连接到主库

  1. 创建连接

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

  1. 获取主库信息
    1. get_master_version_and_clock获取主库版本,serverid(确保主从的不一致),时区等
    2. get_master_uuid获取主库uuid
    3. io_thread_init_commands:初始化一些(会话)变量,如slave_uuid(在主库的连接中),用于后面的连接
  1. 注册从库

register_slave_on_master:主要是发送一个COM_REGISTER_SLAVE命令给主库,以及包含从库信息的包

  1. 注册完成后,

通过request_dump函数向主库发送COM_BINLOG_DUMP命令,后续主库会创建dump线程用来发送binlog

  1. 随后从库开始通过read_event()读取binlog,并写入到relaylog文件中

4. SQL线程:

  1. 入口函数handle_slave_sql

传入的参数为配置主从关系中的Master_info(mi),但是用的是Relay_log_info(mi->rli),主要包含relay日志相关的信息,如offset,master log name等

  1. 大致逻辑如下:
handle_slave_sql()
	|slave_start_workers() // 开启工作线程,用于多线程处理
	|init_relay_log_pos() // 打开并初始化relaylog

	while (!sql_slave_killed(thd,rli)) { // 进入循环
	|exec_relay_log_event() // 执行并处理relaylog
		|next_event() // 获取下一个事件
		|apply_event_and_update_pos()
			|ev->apply_event() // 应用事件
				|do_apply_event() // apply
			|ev->update_pos()  // 更新日志位置
	}

5. 主库dump线程:

  1. 注册从库

在收到从库发来的COM_REGISTER_SLAVE后,会调用register_slave来注册从库,主要把从主库发来的数据放到

(SLAVE_INFO)si中,并把si添加到slave_list中;slave_list是一个全局变量, 是一个HASH表,用来存放从库的信息

这里在添加sislave_list前会先做一次unregister_replica, 把存在于slave_listserver_id相同的从库移除

typedef struct st_slave_info
{
  uint32 server_id;
  uint32 rpl_recovery_rank, master_id;
  char host[HOSTNAME_LENGTH+1];
  char user[USERNAME_LENGTH+1];
  char password[MAX_PASSWORD_LENGTH+1];
  uint16 port;
  THD* thd;
} SLAVE_INFO;
  1. 开始发送binlog

主库收到COM_BINLOG_DUMP_GTID命令后,与注册的是同一个连接(thd),会调用com_binlog_dump_gtid函数处理从库拉取binlog的请求, 主要逻辑如下:

    1. 读取从库的信息,如serverid,binlog位置等
    2. kill_zombie_dump_threads():kill掉僵尸线程,指的是uuid与当前从库相同的dump线程。为什么要清理?
      1. 因为如果主库空闲的话,dump线程会等待binlog更新,如果在这期间,io线程重连,那么就会存在旧的dump线程 。
      2. 如果主库长时间空闲,此时从库又触发重连,那么会出现两个dump线程,直到主库有binlog写入
    1. 发送binlog, 这里调用mysql_binlog_send来执行
sender.run()
  while {
    file= open_binlog_file // 打开binlog文件
    send_binlog()
      while {
        |get_binlog_end_pos() // 获取binlog pos
        | while {
        |    mysql_bin_log.get_binlog_end_pos() // 从binlog中获取
        |    wait_new_events() // 未获取到,等待新binlog
        |      while {
        |       // 与heartbeat_period参数有关,
        |        wait_with_heartbeat() // 每heartbeat_period则给从库发送一次心跳
        |        wait_without_heartbeat() // timeout = 0
        |          mysql_bin_log.wait_for_update_bin_log(thd, timeout)
        |          // 等待binlog,直到超时 这里是通过update_cond事件来实现的
        |      }
        |  }
        |send_events()
        |  while {
        |    read_event() // 读取event
        |    // 会先判断是否event已经在从库中应用
        |    send_packet() // 发送数据
        | }
      }
  }
  1. dump线程退出

这里可能是从库执行stop slave,或者是Binlog_sender内部出错

然后会调用unregister_slave()函数注销从库,主要是把sislave_list中移除

如果是通过stop slave退出,在执行完stop slave后,dump线程不会立马退出,而是在给从库发送binlog或者心跳包时,超时后才会退出循环; 所以在stop s

文章来自个人专栏
文章
6 文章 | 1 订阅
0条评论
0 / 1000
请输入你的评论
0
0