美文网首页
MySQL:8.0新的GTID持久化线程和GTID恢复方式

MySQL:8.0新的GTID持久化线程和GTID恢复方式

作者: 重庆八怪 | 来源:发表于2022-09-11 14:43 被阅读0次

对这一块早有耳闻,但是一直没有学习,就简单学习了一下。虽然线程本生很简单,但是涉及到purge线程,事务/UNDO等核心概念,主要是忘记了,因此还是用了点时间,擦。水平有限,仅供参考。


一、总体变化

我们这里说的GTID持久化线程,就是我们看到的如下:

| thread/innodb/clone_gtid_thread             |         6703 |

其实整个GTID持久化线程,依赖了数据结构Clone_persist_gtid,我们后面在看其中的重要元素,它在用户线程做innodb成提交的时候起到了作用,同时协调了GTID持久化线程和purge线程,是核心的数据结构。总的说来就是下面的变化,

  • 用户线程提交事务将gtid写入到undo header,并且写入到gtid刷新链表中。
  • GTID持久化线程每100毫秒进行gtid flush链表的批量刷新到gtid_executed表中,且GTID持久化线程代替了GTID压缩线程的功能,进行GTID的压缩。
  • purge线程不允许清理未写入到gtid_executed表中事务的undo,为恢复提供基础。
  • Crash recovery的时候gtid内存值的恢复也会读取undo header,因此不再是5.7的仅仅依赖binlog和gtid executed表。

这样带来的最直观的表现是,

  • 5.7中binlog开启的情况下gtid_executed表是binlog切换更新
  • 8.0中binlog开启的情况下gtid_executed表是实时(准实时)更新

此外这一块和clone有着紧密的关系,也会后面学习clone plugin提供一个基础。

二、核心数据结构Clone_persist_gtid中的重要元素

元素 解释
静态常量s_time_threshold_ms 硬编码100毫秒,gtid 持久化线程的执行周期
静态常量s_compression_threshold 硬编码50,这个50的单位是gtid持久化线程批量刷gtid_executed表的次数,通常binlog开启就是每50次gtid批量持久化后进行一次gtid_executed表压缩
静态常量s_gtid_threshold 硬编码1024,单位是事务个数,如果用户提交线程发现积压的gtid事务有多于1024个没有写到gtid_executed表,会主动唤醒gtid持久化线程
静态常量s_max_gtid_threshold 硬编码1024*1024,单位是事务个数,如果用户提交线程发现积压的gtid事务有多于1024*1024个没有写到gtid_executed表,会主动等待gitd持久化线程写gtid_executed表
m_gtids[2] gtid刷新链表,有2个,轮询使用,每批量刷一次gtid到gtid_executed表会切换一次,并且前一个链表清空
原子变量m_active_number 单位是gtid刷新链表切换的次数,主要用确认该刷哪个链表了,代表准备刷入
原子变量m_flush_number 单位是gtid刷新链表切换的次数,主要用确认该刷哪个链表了,代表刷链表完成
m_event 用户线程唤醒gtid持久化线程的条件通知
m_compression_counter 单位是gtid刷新链表切换的次数,每次切换会写入一批gtid,主要binlog开启的情况下判定是否需要压缩gtid_executed表
m_compression_gtid_counter 单位是事务个数,也就是gtid的个数,主要用于binlog关闭的情况下,多少个gtid可以进行压缩一次和参数m_compression_gtid_counter进行对比
原子变量m_gtid_trx_no 主要是供purge线程使用的,用户调整purge线程获取的oldest read view的判定下限,每次gtid持久化线程刷入gtid_executed表中过后会更新,因此保证没有持久化到gtid_executed表的gtid事务的undo不能清理
原子变量m_num_gtid_mem 单位为gtid事务的个数,主要由用户提交事务线程增加,gtid持久化线程持久化后清0,用于用户提交线程判定是否需要唤醒或者等待gtid持久化线程
原子变量m_flush_in_progress 为一个布尔值,每次gtid持久化线程进gtid刷入gtid_executed表工作的时候会将其设置为true,完成后设置为false

这里我们发现很多都是原子变量,这减少了线程间(这里主要是用户线程和gtid持久化线程)同步的复杂性,但是原子变量本生也有一定的开销。

三、事务提交和GTID持久化的关系

这里我们主要考虑正常的事务,而不考虑外部XA事务,它们的处理还有不同。对于正常的事务来讲,
首先要确认的是当前的undo segment是否为insert 类型的,因为insert类型的undo是在提交后就可以清理的,但是显然我们前面说过gtid的undo是不能随意清理的,需要gtid持久化线程的处理,因此需要一个update类型的undo segment,这样才能有不被保留undo segment的条件。这个过程如下:

/* For gtid persistence we need update undo segment. */
      db_err = trx_undo_gtid_add_update_undo(trx, false, false); //分配UNDO 用于存储gtid
trx_undo_gtid_add_update_undo:
  if (undo_ptr->is_insert_only() || gtid_explicit) {
    ut_ad(!rollback);
    mutex_enter(&trx->undo_mutex);
    db_err = trx_undo_assign_undo(trx, undo_ptr, TRX_UNDO_UPDATE); //分配update类型undo
    mutex_exit(&trx->undo_mutex);
  }

当然分配了undo segment过后,也会将gtid写入到undo segment header的TRX_UNDO_FLAG_GTID中如下:

trx_undo_gtid_write:
  if (gtid_desc.m_is_set) {
    /* Persist gtid version */
    mlog_write_ulint(undo_header + TRX_UNDO_LOG_GTID_VERSION,
                     gtid_desc.m_version, MLOG_1BYTE, mtr); //写入GTID VERSION
    /* Persist fixed length gtid */
    ut_ad(TRX_UNDO_LOG_GTID_LEN == GTID_INFO_SIZE);
    mlog_write_string(undo_header + gtid_offset, &gtid_desc.m_info[0],
                      TRX_UNDO_LOG_GTID_LEN, mtr); //写入GTID 字符串到undo segment header中
    undo->flag |= gtid_flag;
  }

除此之外我们发现不光是gtid信息写入undo segment header中,binlog的position信息也会写入到如系统表空间ibdata的如下块中(write_binlog_position):

#define FSP_TRX_SYS_PAGE_NO \
  5 /*!< transaction        \
    system header, in       \
    tablespace 0 */

gtid写入到undo segment header中过后,接着就需要写入到Clone_persist_gtid的gtid 链表中了大概的函数调用如下:

trx_release_impl_and_expl_locks
 ->trx_erase_lists
   ->gtid_persistor.add

这里完成的任务包含了:

  • 没有写入到gtid_executed表的事务个数大于了s_gtid_threshold(1024)个,用户线程主动唤醒gtid持久化线程
  • 没有写入到gtid_executed表的事务个数大于了s_max_gtid_threshold(1024*1024)个,等待gtid持久化线程写入gtid到gtid_executed表
  • 将gtid写入到当前刷新gtid链表(m_gtids[2]中的一个),供gtid持久化线程使用

具体就不展开讨论了,当然事务的提交完成了很多任务,比如比较关键和这里有关系的,将事务的trx no写入到trx_sys的serialisation_list链表中,purge线程会通过oldest read view和TRX_RSEG_HISTORY(History List)的每个事务的trx no比较去决定哪些事务的undo 能够清理,

->trx_serialisation_number_get
             -> trx->no = trx_sys_get_new_trx_id();
                为事务分配trx no
             -> UT_LIST_ADD_LAST(trx_sys->serialisation_list, trx);
                加事务挂入trx_sys的序列化链表,待purge线程使用,和oldest read view比较
->trx_erase_lists
这里在完成了将事务undo移动到TRX_RSEG_HISTORY,并且将gtid挂载到flush gtid链表后从trx_sys->serialisation_list去掉。

当然为了获取oldest read view的下限,就需要和Clone_persist_gtid结构的m_gtid_trx_no再次比较,来更改下限。而在后面我们回看到Clone_persist_gtid结构的m_gtid_trx_no就来自最老的trx_sys->serialisation_list中的值,因为在提交的最后会将事务gtid放到到gtid flush链表中,一旦gtid持久化线程完成gtid写入到gtid_executed表后,那么这批gtid就已经持久到gtid_executed表的,可以清理了,如果没有完成则不能这些事物的update undo segment。
还有比如insert类型的undo segment,事务提交就会清理掉也是在commit期间进行的。

四、gtid持久化线程的相关处理

整个线程的入口函数为,

->clone_gtid_thread
  ->Clone_persist_gtid::periodic_write

每100ms进行一次Clone_persist_gtid::flush_gtids的调用,我们主要来看这个函数的功能,因为这是核心函数。

首先第一步需要从trx_sys->serialisation_list中获取最老的trx no,也就是说拿到已经写入到gtid flush链表中的事务的trx no的最大值,那么小于这个值的都在我们的gtid flush链表中(这个问题困扰了我比较久,来回的翻了一阵逻辑),如下

 ->oldest_trx_no = trx_sys_oldest_trx_no(); 
   获取最老的 trx no

接着如果Clone_persist_gtid结构的m_num_gtid_mem不为0,我们前面说过只要有事务的提交就会增加这个值,则需要进行gtid的持久化。然后切换gtid flush链表因为我们前面说了这里有2个链表,并且将计数器m_num_gtid_mem清0,并且维护Clone_persist_gtid结构的m_compression_gtid_counter进行按事务的增加。

接下来就是写入gtid flush链表中的gtid值了,其中每次批量写入后还会进行Clone_persist_gtid结构的m_compression_counter的增加,而这个值是压缩判定的标准,实际上主要调用还是

gtid_table_persistor->save(&write_gtid_set, false)
注意这里的false代表不进行,gtid 压缩线程的唤醒。

进行写入,完成后本gtid flush链表清空。下来就是关键一步,前面从trx_sys->serialisation_list获取了最老的trx no,现在写入gtid_executed表完成了,就需要更新Clone_persist_gtid结构的m_gtid_trx_no,代表小于这个值的事务的gtid都持久化了,而前面你说过这个和purge线程定义oldest read view有关如下:

->MVCC::clone_oldest_view
  /* Update view to block purging transaction till gtid is persisted. */
  auto &gtid_persistor = clone_sys->get_gtid_persistor();
  auto gtid_oldest_trxno = gtid_persistor.get_oldest_trx_no();
  view->reduce_low_limit(gtid_oldest_trxno); //调整low_limit

调整了oldest read view,自然保证了undo的存在。

再然后就是判定是否进行压缩了,这里我们可以看到gtid持久化线程几乎代替了gtid压缩线程的功能,这也少了多线程并发控制的烦恼。压缩判定如下(Clone_persist_gtid::check_compress):

  • 如果没有开启binlog,和参数gtid_executed_compression_period有关,使用GTID个数进行判定,也就是m_compression_gtid_counter
  • 如果开启了binlog,和写入gtid_executed表的的刷新次数有关,也就是m_compression_counter

如果需要则进行gtid_executed表的压缩。

五、恢复读取undo中的gtid

这个部分就没有仔细的推敲,但是我们从如下栈中可以看到启动阶段获取了undo的gtid信息,如下:

#0  trx_undo_gtid_read_and_persist (undo_header=0x7fff425b0056 "") at /newdata/mysql-8.0.23/storage/innobase/trx/trx0undo.cc:666
#1  0x000000000524a7e0 in trx_rseg_persist_gtid (rseg=0x7fffe1e1e350, gtid_trx_no=3957552) at /newdata/mysql-8.0.23/storage/innobase/trx/trx0rseg.cc:212
#2  0x000000000524abed in trx_rseg_mem_create (id=16, space_id=4294967152, page_no=20, page_size=..., gtid_trx_no=3957552, purge_queue=0x7fffe1e160f0, mtr=0x7fffe7136590)
    at /newdata/mysql-8.0.23/storage/innobase/trx/trx0rseg.cc:264
#3  0x000000000524b289 in trx_rsegs_init (purge_queue=0x7fffe1e160f0) at /newdata/mysql-8.0.23/storage/innobase/trx/trx0rseg.cc:408
#4  0x000000000524ffbd in trx_sys_init_at_db_start () at /newdata/mysql-8.0.23/storage/innobase/trx/trx0sys.cc:429
#5  0x00000000051e1451 in srv_start (create_new_db=false) at /newdata/mysql-8.0.23/storage/innobase/srv/srv0start.cc:2682

也就是函数trx_undo_gtid_read_and_persist ,如果要深入学习可以以此为入口,实际上打开函数看看就能清晰的发现读取gtid这个事实。前面说过undo不清理为Crash recovery后获取到undo header中的gtid提供的了条件,这部分可以参考好友温正湖的文章:
从MySQL 8.0故障恢复看InnoDB及Binlog角色变化

其他参考:
庖丁解InnoDB之UNDO LOG
如何在Innodb层维护GTID?

六、代码相关

Clone_persist_gtid


private:
  /** Time threshold to trigger persisting GTID. Insert GTID once per 1k
  transactions or every 100 millisecond. */
  const static uint32_t s_time_threshold_ms = 100;

  /** Threshold for the count for compressing GTID. */
  const static uint32_t s_compression_threshold = 50;

  /** Number of transaction/GTID threshold for writing to disk table. */
  const static int s_gtid_threshold = 1024;

  /** Maximum Number of transaction/GTID to hold. Transaction commits
  must wait beyond this point. Not expected to happen as GTIDs are
  compressed and written together. */
  const static int s_max_gtid_threshold = 1024 * 1024;

  /** Two lists of GTID. One of them is active where running transactions
  add their GTIDs. Other list is used to persist them to table from time
  to time. */
  Gitd_info_list m_gtids[2]; //两个list 

  /** Number of the current GTID list. Increased when list is switched */
  std::atomic<uint64_t> m_active_number;    链表切换的次数,主要用确认该刷哪个链表了,代表准备刷入。

  /** Number up to which GTIDs are flushed. Increased when list is flushed.*/
  std::atomic<uint64_t> m_flush_number;     刷入表中的GTID 数量,代表刷链表完成,因此完成后断言ut_ad((m_flush_number + 1) == flush_list_number);

  /** If explicit request to flush is made. */
  std::atomic<bool> m_explicit_request; 不考虑

  /** Number for which last flush request was made. */
  uint64_t m_flush_request_number{0};   不考虑

  /** Event for GTID background thread. */
  os_event_t m_event;                   条件通知事件

  /** Counter to keep track of the number of writes till it reaches
  compression threshold. */
  uint32_t m_compression_counter{0};     单位为刷新flush链表次数,写入gtid_executed表次数

  /** Counter to keep number of GTIDs flushed before compression. */
  uint32_t m_compression_gtid_counter{0}; 单位gtid个数,也就是事务个数 

  /* Oldest transaction number for which GTID is not persisted. */
  std::atomic<uint64_t> m_gtid_trx_no;  //主要供purge线程使用

  /** Number of GTID accumulated in memory */
  std::atomic<int> m_num_gtid_mem;      //GTID 计数,每次一个链表GTID写入到表中后归0,单位为事务

  /** Flush of GTID is in progress. */
  std::atomic<bool> m_flush_in_progress;  Clone_persist_gtid::flush_gtids 设置,仅仅在正在刷新的时候设置

  /** Set to true, when the background thread is asked to exit. */
  std::atomic<bool> m_close_thread;   和close有关

  /** TRUE, if background thread is active.*/
  std::atomic<bool> m_thread_active;  和recovery gtid有关

  /** TRUE, if GTID persistence is active.*/
  std::atomic<bool> m_active;   在clone线程启动后设置为true,在clone线程关闭我设置为flase
  
  
  
Clone_persist_gtid::check_gtid_commit












trx_prepare_for_mysql      (可能没有分配)
 ->trx_undo_gtid_add_update_undo
   ->Clone_persist_gtid::trx_check_set 
     根据阶段不同prepare和commit检查方式不一样,调用函数不一样
     还有rollback 等  
   如果仅仅是insert segment,则需要分配update undo segment  
   ->trx_undo_assign_undo




trx_commit_for_mysql
 ->trx_undo_gtid_add_update_undo
   ->Clone_persist_gtid::trx_check_set 
     根据阶段不同prepare和commit检查方式不一样,调用函数不一样
     还有rollback 等  
   如果仅仅是insert segment,则需要分配update undo segment,
   ->trx_undo_assign_undo
   ->trx_update_mod_tables_timestamp
     ->更新table的uptime,需要确认是 information_schema.tables中的uptime?
 ->trx_commit
     ->if (trx_is_rseg_updated(trx))
       是否为update rollback segment,如果是则需要建立MTR,因为涉及到undo的修改
     ->trx_commit_low(trx, mtr)
       ->trx_write_serialisation_history 
         ->trx->rsegs.m_redo.insert_undo != nullptr 
           如果insert undo不为空调用
           ->trx_undo_set_state_at_finish(下同)
             如果undo 是 insert undo 则直接设置标记直接清理
             TRX_UNDO_TO_FREE
             如果 update undo则使用
             TRX_UNDO_TO_PURGE
             写入到undo segment header
         ->trx->rsegs.m_redo.update_undo != nullptr 
           如果update undo不为空
           序列化,分配trx no (注意 上锁 trx_sys_mutex_enter)
           ->trx_serialisation_number_get
             -> trx->no = trx_sys_get_new_trx_id();
                为事务分配trx no
             -> UT_LIST_ADD_LAST(trx_sys->serialisation_list, trx);
                加事务挂入trx_sys的序列化链表,在结束序列划的时候去掉
           ->trx_undo_set_state_at_finish(同上)
           ->trx_undo_gtid_set
             设置GTID标记,没细看,后面会用到这个标记
           ->trx_undo_update_cleanup
             ->trx_purge_add_update_undo_to_history
               ->获取事务层面的undo log(undo_trx_t)
               ->flst_add_first(rseg_header + TRX_RSEG_HISTORY,undo_header + TRX_UNDO_HISTORY_NODE, mtr)
                 ->将undo log加入到history 链表
               ->trx_sys->rseg_history_len.fetch_add(n_added_logs)
                 history length 统计值增加
               ->如果trx_sys->rseg_history_len大于了
                 srv_n_purge_threads * srv_purge_batch_size
                 则唤醒purge线程
               ->trx_undo_gtid_write
                 ->获取GTID 应该写入到 undo header的类型    
                 ->(undo->flag & gtid_flag) == 0
                  是否要在prepare阶段进行 GTID的写入,这是根据undo的flag 的 TRX_UNDO_FLAG_XA_PREPARE_GTID : TRX_UNDO_FLAG_GTID 标记来决定的
                  对于普通事务来讲 不会再prepare阶段进行GTID的写入到UNDO,外部XA事务不一样,不做考虑
                 -> gtid_persistor.get_gtid_info(trx, gtid_desc);
                   Clone_persist_gtid::get_gtid_info
                   ->获取owned_gtid
                   ->放入变量Gtid_desc变量gtid_desc中
                   ->写入GTID VERSION
                     mlog_write_string(undo_header + gtid_offset, &gtid_desc.m_info[0],TRX_UNDO_LOG_GTID_LEN, mtr); //写入GTID 字符串
                   ->写入GTID字符串到undo header
             ->UT_LIST_REMOVE(rseg->update_undo_list, undo)
               从rollback segment链表中去掉这个undo segment
             ->trx_undo_mem_free(undo)
               free 内存
         ->trx_sys_update_mysql_binlog_offset(trx, mtr)     
           将binlog信息写入到innodb层,位于TRX_SYS_PAGE_NO(ibdata page 5)的TRX_SYS_MYSQL_LOG_INFO中
           ->write_binlog_position
             写入binlog位置,主要是clone的时候会用到(Clone_Snapshot)                         
       ->trx_commit_in_memory(trx, mtr, serialised)
         ->(trx_is_autocommit_non_locking(trx)) { 
           是否是read only 事务,DDL事务在mysql层会设置blocking
           ->trx_sys->mvcc->view_close(trx->read_view, false)
             关闭视图,设置标记TRX_ID_MAX,从mvcc全局管理系统的m_views中去除,加入到m_free中
           ->trx->state = TRX_STATE_NOT_STARTED;
             将事务指定为NOT_STARTED 
         ->否则为 读写事务
           ->trx_release_impl_and_expl_locks
             ->gtid_persistor.get_gtid_info(trx, gtid_desc);
               获取GTID
             ->trx_erase_lists(trx, serialized, gtid_desc);
               ->trx_erase_lists
                 ->gtid_persistor.add(gtid_desc)  Clone_persist_gtid::add
                   将GTID写入到活跃链表 
                   ->check_max_gtid_threshold
                     累积的gtid个数是否大于了1024*1024
                   如果大于则
                    ->释放锁trx_sys_mutex_exit
                    ->Clone_persist_gtid::wait_flush(false, false, nullptr)
                       注意本处不需要压缩,等待期间打印日志
                       Waiting for Clone GTID thread       
                    ->加锁  trx_sys_mutex_enter   
                   ->current_gtids.push_back(gtid_desc.m_info);
                     将GTID加入到current_gtids,活跃gtid链表
                   ->m_num_gtid_mem
                     累积m_num_gtid_mem 增加
                   ->current_value == s_gtid_threshold  1024
                     唤醒os_event_set(m_event) GTID持久化线程      
                 ->UT_LIST_REMOVE(trx_sys->serialisation_list, trx);
                   前面已经做了序列化,将事务去掉,
                 ->trx_sys->rw_trx_ids.erase(it); 
                   活跃RW事务数组中拿掉这个事务
                 ->UT_LIST_REMOVE(trx_sys->rw_trx_list, trx);
                   活跃RW事务链表中拿到这个事务
                 ->trx_sys->rw_trx_set.erase(TrxTrack(trx->id));
                   活跃RW事务的集合
                 ->trx_id_t min_id = trx_sys->rw_trx_ids.empty() ? trx_sys->max_trx_id: trx_sys->rw_trx_ids.front()
                   因为链表信息修改了,需要维护最小事务ID
                 ->trx_sys->min_active_id.store(min_id)
                   更新最小事务ID
         ->rollback segment的事务引用--
         ->Clone_persist_gtid::set_persist_gtid
          ->reset SE_GTID_PERSIST/reset SE_GTID_PERSIST_EXPLICIT
          ->设置persists_gtid为false
            trx->persists_gtid = false;
          ...
         ->trx_undo_insert_cleanup
           清理insert的undo信息
         ->trx_flush_log_if_needed
           flush redo log
         ->srv_active_wake_master_thread
         ->trx_roll_savepoints_free
           释放所有的savepoint
         ->trx->dict_operation = TRX_DICT_OP_NONE
           设置字典操作为NONE,可以看看什么时候设置的
         -> 如果事务是trx->abort
           设置事务状态
           trx->state = TRX_STATE_FORCED_ROLLBACK;
           如果事务是正常结束(读写事务)
           trx->state = TRX_STATE_NOT_STARTED; 
           设置事务状态 为 NOT_STARTED
         -> 初始化事务
            trx_init(trx);
           
           


Clone_persist_gtid::flush_gtids
 ->oldest_trx_no = trx_sys_oldest_trx_no(); 
   获取最老的 TRX_NO,也就是说拿到已经写入到gtid flush链表中的事务的trx no的最大值,那么小于这个值的都在我们的gtid flush链表中
 ->m_num_gtid_mem.load() != 0 
   如果有需要刷新的的GTID,每次GTID写入增加,flush gtid链表后归0
   ->m_flush_in_progress.store(true); 
   正在将GTID写入到表中的标记
   ->flush_list_number = switch_active_list() 重点
   进行list切换,切换后返回应该flush的gtid链表的数量                                                          
     ->++m_active_number
      切换一次+1
     ->m_compression_gtid_counter += m_num_gtid_mem
       增加压缩gtid的指标,用于判定是否可以压缩,单位就是事务。
     ->m_num_gtid_mem
       计数器清0,本计数器每次GTID提交后增加1,
       每次清理某个list后清0
     ->返回值为需要flush的gtid数量
 ->err = write_to_table(flush_list_number, table_gtid_set, sid_map)
   将切换的GTID 写入到 gtid_executed表中
   ->flush_list = get_list(flush_list_number)
     获取GTID刷新链表
   ->for (auto &gtid_info : flush_list)
     循环整个链表的gtid,写入到write_gtid_set集合
     write_gtid_set.add_gtid_text(gtid_str); 
   ->Gtid_state::update_prev_gtids
     维护previous_gtids_logged,并且write_gtid_set中去掉
     previous_gtids_logged
   ->++m_compression_counter
     增加压缩计数器,用于判定是否需要在gtid executed表中进行压缩
     每次写入到gtid executed表进行一次增加,单位就是刷新链表(写入GTID)的次数
   ->gtid_table_persistor->save(&write_gtid_set, false)
     存入到gtid_executed表中
   ->刷完后清理本次刷入磁盘的gtid链表
     flush_list.clear();
   ->m_flush_number.store(flush_list_number)
     增加 m_flush_number 代表刷新次数,每次刷新一批,
     刷完gtid后增加
 ->m_flush_in_progress.store(false)
   本次刷gtid list 完成
 ->Clone_persist_gtid::update_gtid_trx_no(oldest_trx_no); 
   写入最老的 trx no
 ->Clone_persist_gtid::check_compress
   也负责压缩,进行检查
   -> !opt_bin_log && gtid_executed_compression_period != 0 && m_compression_gtid_counter > gtid_executed_compression_period   
      和参数gtid_executed_compression_period有关,如果没有开启binlog,使用GTID个数进行判定
   -> (m_compression_counter >= s_compression_threshold)
      硬编码50,和写入gtid table的次数有关        
 ->如果需要压缩gtid_execute表
   m_compression_gtid_counter和m_compression_counter
   归0
   且进行压缩
   Gtid_table_persistor::compress
   
     


当前压缩主要由gtid clone线程负责了,而不是以前的gtid压缩线程



 p (clone_sys->get_gtid_persistor())->m_explicit_request
 



trx_undo_gtid_read_and_persist



         
           

https://bugs.mysql.com/bug.php?id=107991


Clone_persist_gtid::update_gtid_trx_no
gtid_persistor.get_gtid_info



GTID的更新在这之后在commit之后


#0  Gtid_state::update_gtids_impl_own_gtid(THD*, bool) () at /pxc/source/mysql-8.0.28/sql/sql_class.h:2402
#1  0x0000000001ba35be in Gtid_state::update_commit_group(THD*) () at /pxc/source/mysql-8.0.28/sql/rpl_gtid_state.cc:184
#2  0x0000000001b55972 in MYSQL_BIN_LOG::process_commit_stage_queue(THD*, THD*) () at /pxc/source/mysql-8.0.28/sql/binlog.cc:8459
#3  0x0000000001b66836 in MYSQL_BIN_LOG::ordered_commit(THD*, bool, bool) () at /pxc/source/mysql-8.0.28/sql/binlog.cc:8968
#4  0x0000000001b67d9e in MYSQL_BIN_LOG::commit(THD*, bool) () at /pxc/source/mysql-8.0.28/sql/binlog.cc:8210
#5  0x0000000001105b4c in ha_commit_trans (thd=thd@entry=0x7fff2c000d20, all=all@entry=false, ignore_global_read_lock=<optimized out>) at /pxc/source/mysql-8.0.28/sql/handler.cc:1765
#6  0x0000000000fcf954 in trans_commit_stmt(THD*, bool) () at /pxc/source/mysql-8.0.28/sql/transaction.cc:532
#7  0x0000000000ec0101 in mysql_execute_command(THD*, bool) () at /pxc/source/mysql-8.0.28/sql/sql_parse.cc:4746
#8  0x0000000000ec1a1b in dispatch_sql_command (thd=thd@entry=0x7fff2c000d20, parser_state=parser_state@entry=0x7fff7c785510) at /pxc/source/mysql-8.0.28/sql/sql_parse.cc:5174
#9  0x0000000000ec2d30 in dispatch_command(THD*, COM_DATA const*, enum_server_command) () at /pxc/source/mysql-8.0.28/sql/sql_parse.cc:1938
#10 0x0000000000ec4eb5 in do_command (thd=thd@entry=0x7fff2c000d20) at /pxc/source/mysql-8.0.28/sql/sql_parse.cc:1352
#11 0x0000000000ff64f8 in handle_connection (arg=arg@entry=0x6282190) at /pxc/source/mysql-8.0.28/sql/conn_handler/connection_handler_per_thread.cc:302
#12 0x0000000002409133 in pfs_spawn_thread (arg=0x6361060) at /pxc/source/mysql-8.0.28/storage/perfschema/pfs.cc:2947
#13 0x00007ffff7bc6ea5 in start_thread () from /lib64/libpthread.so.0
#14 0x00007ffff62f7b0d in clone () from /lib64/libc.so.6




#0  0x00007ffff7bcaa35 in pthread_cond_wait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0
#1  0x0000000004dedc81 in native_cond_wait (cond=0x84261c0 <COND_compress_gtid_table>, mutex=0xa951d08) at /newdata/mysql-8.0.23/include/thr_cond.h:109
#2  0x0000000004deddec in safe_cond_wait (cond=0x84261c0 <COND_compress_gtid_table>, mp=0xa951ce0, file=0x6862390 "/newdata/mysql-8.0.23/sql/rpl_gtid_persist.cc", line=768)
    at /newdata/mysql-8.0.23/mysys/thr_cond.cc:71
#3  0x0000000004a16fe2 in my_cond_wait (cond=0x84261c0 <COND_compress_gtid_table>, mp=0x8426180 <LOCK_compress_gtid_table>, file=0x6862390 "/newdata/mysql-8.0.23/sql/rpl_gtid_persist.cc", 
    line=768) at /newdata/mysql-8.0.23/include/thr_cond.h:160
#4  0x0000000004a17097 in inline_mysql_cond_wait (that=0x84261c0 <COND_compress_gtid_table>, mutex=0x8426180 <LOCK_compress_gtid_table>, 
    src_file=0x6862390 "/newdata/mysql-8.0.23/sql/rpl_gtid_persist.cc", src_line=768) at /newdata/mysql-8.0.23/include/mysql/psi/mysql_cond.h:201
#5  0x0000000004a19ddd in compress_gtid_table (p_thd=0xad69f40) at /newdata/mysql-8.0.23/sql/rpl_gtid_persist.cc:768
#6  0x000000000562ce9c in pfs_spawn_thread (arg=0xab15500) at /newdata/mysql-8.0.23/storage/perfschema/pfs.cc:2900
#7  0x00007ffff7bc6ea5 in start_thread () from /lib64/libpthread.so.0
#8  0x00007ffff5e388dd in clone () from /lib64/libc.so.6




Checkable_rwlock *global_sid_lock = nullptr;
Sid_map *global_sid_map = nullptr;
Gtid_state *gtid_state = nullptr;
Gtid_table_persistor *gtid_table_persistor = nullptr;

相关文章

  • MySQL:8.0新的GTID持久化线程和GTID恢复方式

    对这一块早有耳闻,但是一直没有学习,就简单学习了一下。虽然线程本生很简单,但是涉及到purge线程,事务/UNDO...

  • Mysql 的GTID主从复制方式

    Mysql 的GTID主从复制方式 GTID的作用 GTID 是‘全局事务ID’的意思,在 MySQL5.6 中被...

  • Mysql - GTID原理

    Mysql - GTID原理 GTID是MySQL 5.6的新特性,其全称是Global Transaction ...

  • MYSQL主从复制gtid浅析

    Gtid概念 从 MySQL 5.6.5 开始新增了一种基于 GTID 的复制方式。通过 GTID保证了每个在主库...

  • MYSQL--进阶

    MYSQL主从复制 类别 基于日志点的复制支持MMM和MHA架构 基于GTID方式的复制GTID= source_...

  • 2019-07-15 MySQL GTID复制实践

    1. MySQL GTID复制 1.1 GTID复制简介 GTID(global transaction iden...

  • Mysql的gtid详解

    原文:MySQL GTID详解_woailyoo0000的博客-CSDN博客_mysql的gtid[https:/...

  • MySQL GTID

    MySQL GTID简介 GTID( Global Transaction Identifier)全局事务标识,由...

  • MySQL GTID详解

    参考:MySQL GTID详解_woailyoo0000的博客-CSDN博客_mysql 查看gtid[https...

  • MySQL主从复制之GTID模式介绍

    GTID概述 MySQL5.6 在原有主从复制的基础上增加了一个新的复制方式,即基于GTID的复制方式,它由UUI...

网友评论

      本文标题:MySQL:8.0新的GTID持久化线程和GTID恢复方式

      本文链接:https://www.haomeiwen.com/subject/vezxortx.html