MySQL:MGR节点上线恢复简析

2023年 8月 15日 25.4k 0

作者简介:高鹏,笔名八怪。《深入理解MySQL主从原理》图书作者,同时运营个人公众号“MySQL学习”,持续分享遇到的有趣case以及代码解析!

仅仅作为自己的学习笔记,不做其他用途。原文中有大图,参考https://www.jianshu.com/p/d27f46975453

一、恢复的主要阶段

实际上节点的加入会经历几个主要的流程

  • 发起一个Control_notification调用Gcs_xcom_control::retry_do_join连接到种子节点, 发送加入的请求给xcom线程,xom线程检测到有节点加入后,然后由xcom线程下发Global_view_notification给各个节点(包括加入节点)表示有节点申请加入。
  • 各个节点收到Global_view_notification的消息后,获取本节点的一些信息,比如本节点执行的gtid信息、视图更新后的节点信息等。完成后发送一个信息给xcom线程 (Cargo_type::CT_INTERNAL_STATE_EXCHANGE)
  • xcom线程下发Data_notification ,交由xcom engine线程处理,主要就是安装视图
  1. 对于现有节点而言,主要更新视图信息,将加入节点的状态更改为recovery
  2. 对于加入节点而言,也是先将自己的状态设置为recovery然后进行恢复。
  • 加入节点恢复完成后,会发送online消息给各个节点,各个节点将节点状态状态 设置为online。

在加入的过程中视图版本会加1,由于view change event会封装在一个事务里面,因此 GTID也会加1,封装的过程主要处于Certification_handler::handle_event中对于view change event的处理。

二、加入阶段数据恢复的简图

这里我们主要看看加入节点的恢复流程,这里主要涉及到3个线程的协同工作,用图表示:

恢复简图.jpg

本处小结:

  • 很显然由于group_replication_clone_threshold参数默认巨大,因此很难选择clone插件,除非GTID为空。
  • 节点处于recovery状态是比较早的,如果其中流程出错,看到的视图信息依旧是recovery,比如选择donor节点恢复出现问题,这是常见的。
  • 节点上线依赖参数group_replication_recovery_complete_at的设置,默认设置为TRANSACTIONS_APPLIED,如果有大量的事务应用,那么节点上线可能推迟,因此加入节点选择低峰期进行。
  • incoming队列的缓存的是否如果过多,可能出现问题。因此加入节点选择低峰期。

三、代码部分笔记

<code>Gcs_xcom_control::process_control_message
  视图版本+1  
  -&gt;Gcs_xcom_control::install_view
      根据新的节点信息建立新的视图   
      -&gt;Plugin_gcs_events_handler::on_view_changed
          -&gt;Plugin_gcs_events_handler::handle_joining_members
               -&gt; is_joining == ture
                      先学习这个分支       
                      -&gt;状态在恢复当中 MEMBER_IN_RECOVERY       
                      update_member_status(
                              new_view.get_joined_members(), Group_member_info::MEMBER_IN_RECOVERY,        
                              Group_member_info::MEMBER_OFFLINE, Group_member_info::MEMBER_END);              
                      
                      -&gt;设置super read only  如果设置出错       
                      enable_server_read_mode(PSESSION_DEDICATED_THREAD)       
                      -&gt;如果是多主需要考虑自增是否符合要求       
                      new_view.get_members().size() &gt; auto_increment_increment
                              挂起applier通道-----&gt;applier通道处于挂起状态       
                      -&gt;根据下发的view id进行View_change_packet的构建,并且推送给
                               applier通道的pipeline 进行处理 Applier_module.add_view_change_packet          
                               View_change_packet *view_change_packet = new View_change_packet(view_id);         
                               applier_module-&gt;add_view_change_packet(view_change_packet);       
                      -&gt;进行恢复方式的选择
                              -&gt;是否使用clone          
                              recovery_strategy = remote_clone_handler-&gt;check_clone_preconditions(); 
                                       -&gt;这里根据参数来判定,到底是使用clone还是recovery的方式       
                      -&gt;如果是clone则新建clone线程
                               remote_clone_handler-&gt;clone_server 先不考虑clone       
                      -&gt;如果是recovery则新建recovery线程
                               recovery_module-&gt;start_recovery(new_view.get_group_id().get_group_id(),new_view.get_view_id().get_representation())         
                               启动恢复线程,加入的是group id和view id         
                               见下面的流程         
                               
进行恢复
Recovery_module::start_recovery
   -&gt;初始化group_name和view_id
        this-&gt;group_name = group_name;     
        recovery_state_transfer.initialize(rec_view_id);    
   -&gt;启动新线程
        -&gt;launch_handler_thread
              -&gt;handler-&gt;recovery_thread_handle()      
              实际调用为Recovery_module::recovery_thread_handle
                      -&gt;step0                  
                      
                      -&gt;step1          applier_module-&gt;wait_for_applier_complete_suspension(&recovery_aborted);          等待applier线程的SQL线程应用完现有的relay log的事务,等待是通过收到的GTID          为最终等待的GTID,完成后进入下一步        
                      -&gt;step2          如果只有一个节点,直接进入上线流程,跳过步骤3        
                      -&gt;step3          进行recovery流程          recovery_state_transfer.state_transfer(stage_handler);           -&gt;establish_donor_connection            建立donor的连接           -&gt;选择donor节点?这里看的和前期看的不同,需要确认             这里暴露了一些构建的方法,比如必须是online。           -&gt;尝试连接donor节点,并且进行不同的报错             initialize_donor_connection           -&gt;进行恢复             start_recovery_donor_threads             这里带入了view_id,也就是进行恢复结束的位置为view change event的来到             -&gt;donor_connection_interface.start_threads(true, true, &view_id, true);        
                      -&gt;step4                -&gt;applier_module-&gt;awake_applier_module();             唤醒apllier通道继续应用缓存的event,这里前面挂起了applier通道,只是event和消息             缓存到了incoming队列。!!!!           -&gt;Recovery_module::wait_for_applier_module_recovery             等待上线,开启循环             while (!recovery_aborted && applier_monitoring)             -&gt;获取incoming 队列大小               applier_module-&gt;get_message_queue_size();             -&gt;获取每秒恢复应用事务的数量             -&gt;如果参数设置为 cerify,则上线条件为               -&gt;恢复期间认证为0则继续进行处理                 根据需要认证的事务总数和已经认证的事务数量进行统计,其中已经认证的事务数量在Certifier::certify的末尾进行增加。             -&gt;如果参数设置为applier,则上线条件为               -&gt;1.如果剩余需要过pipeline流程的事务,数量少于每秒恢复的事务数量,说明已经很少了,下一秒即可执行完成               -&gt;2.如果incoming队列为0 、每秒恢复的事务数量为0 、applier的SQL线程处于空闲状态了                 如果满足上面其中一个1个条件,进入等待,等待为接受的GTID全部应用完成。             -&gt;如果不满足上面的条件则进行循环等待               否则进行睡眠 incoming队列大小有关,但最大睡眠 为 100*5000 微秒                        
                      -&gt;step5           -&gt;发送节点恢复完成的消息           notify_group_recovery_end();           这个消息是发给xcom的,因此是全员消息,应该是通知各个节点更改本节点状态为online         
                      -&gt;step7           终止recovery线程                                                   
                      
applier_module-&gt;add_view_change_packet(view_change_packet);          xcom engine推送view change packet给 applier进行pipeline处理    Applier_module::applier_thread_handle   -&gt; packet_application_error = apply_view_change_packet((View_change_packet *)packet, fde_evt, cont);    -&gt; Applier_module::apply_view_change_packet      -&gt; Applier_module::inject_event_into_pipeline        -&gt;Event_cataloger::handle_event          -&gt;Event_handler::next            -&gt;Certification_handler::handle_event              -&gt; Certification_handler::extract_certification_info                -&gt;Certification_handler::log_view_change_event_in_order                  -&gt;Certification_handler::inject_transactional_events                     生成一个gtid事务,包含事务的所有event,并且会分配gtid                     -&gt;Event_handler::next

参考:https://zhuanlan.zhihu.com/p/40627399

相关文章

Oracle如何使用授予和撤销权限的语法和示例
Awesome Project: 探索 MatrixOrigin 云原生分布式数据库
下载丨66页PDF,云和恩墨技术通讯(2024年7月刊)
社区版oceanbase安装
Oracle 导出CSV工具-sqluldr2
ETL数据集成丨快速将MySQL数据迁移至Doris数据库

发布评论