技术头条 - 一个快速在微博传播文章的方式     搜索本站
您现在的位置首页 --> MySQL --> MariaDB与Percona XtraDB的Group Commit实现原理分析

MariaDB与Percona XtraDB的Group Commit实现原理分析

浏览:1260次  出处信息

MySQL数据库InnoDB存储引擎一直有一个Bug,就是当开启binlog时,无法进行group commit。究其原因,是为了保证InnoDB存储引擎的事务日志与mysqlbinlog日志的顺序一致性。

在prepare前需要获取mutex,直到commit完成之后释放,这也禁用了group commit的功能。

对于MySQL数据库InnoDB存储引擎 group commit的分析,可参考系列文章:MySQL/InnoDB和Group Commit(1)MySQLl/InnoDB和Group Commit(2)

对于MariaDB / PerconaXtraDB如何实现Group commit,可参考MariaDB官方网站的3篇WorkLog:

 

Percona 5.5.19-rel24代码分析:

以下分析Group Commit的具体实现,基于Percona 5.5.19-rel24。

以下提到的功能,在WorkLog WL#116中都有提及。建议先看WL#116,然后有针对性的看下面的函数实现,绝对事半功倍。

prepare_ordered功能:

Log.cc::MYSQL_BIN_LOG::write_transaction_to_binlog_events(group_commit_entry *entry)

mysql_mutex_tLOCK_group_commit_queue;

事务由mutex保护,加入queue,第一个加入queue的事务负责余下操作;其余事务进入等待。

if (orig_queue != NULL)
entry->thd->wait_for_wakeup_ready();
else
trx_group_commit_leader(entry);
commit_ordered功能:
ha_innodb.cc::innobase_init ->innobase_hton->commit_ordered = innobase_commit_ordered

XA事务总流程:

handler.cc::ha_commit_trans()函数分析
    for (Ha_trx_info *hi = ha_info; hi; hi = hi->next())
        handlerton *ht = hi->ht();
  
        // XA事务第一阶段,group fsync prepare日志
        // 其中,binlog的prepare方法为空实现
        err = ht->prepare();
        need_commit_ordered |= (ht->commit_ordered != NULL);
  
    // Binlog group commit,然后调用commit_ordered进行排序
    cookie = tc_log->log_and_order();
    // XA事务第二阶段,fsync commit日志
    error = commit_one_phase_low();
  
TC_LOG_BINLOG::log_and_order()函数分析
    binlog_commit_flush_stmt/trx_cache();
        binlog_flush_cache();
            write_transaction_to_binlog();
  
                // 事务首先进入binlog group commit queue
                // 第一个进入的事务进行binlog group commit
                // 其余事务进入等待
  
                write_transaction_to_binlog_events();
                    trx_group_commit_leader(); (entry->thd->wait_for_wakeup_ready())
  
                        
  
                        // binlog完成fsync之后,调用此函数对事物进行排序
                        // 排序前,需要获取LOCK_commit_orderedmutex
  
                        run_commit_ordered();
                            for ( … )
                                ht->commit_ordered();
                                // InnoDB提供了此函数,实现排序功能
                                innobase_commit_ordered();
  
Log.cc::MYSQL_BIN_LOG::trx_group_commit_leader()函数分析
  
    // 获取当前queue,并且重新开启一个queue
    // 本queue中的binlog,都由当前事务group commit
  
    // 新queue中的binlog,由新queue中的第一个事务等待LOCK_logmutex
    mysql_mutex_lock(&LOCK_log);
    mysql_mutex_lock(&LOCK_group_commit_queue);
    current = group_commit_queue;
    group_commit_queue = NULL;
    mysql_mutex_unlock(&LOCK_group_commit_queue);
  
    // 写binlog,最后执行一次fsync操作
  
    for ( … )
        write_transaction();
    flush_and_sync();
  
    // 获取commit_orderedmutex,然后才能释放LOCK_logmutex
    // 保证下一个queue,不会在当前queue之前调用commit_ordered
  
    mysql_mutex_lock(&LOCK_commit_ordered);
    mysql_mutex_unlock(&LOCK_log);
  
    // 按照prepare的顺序,调用引擎提供的commit_ordered方法
    // 由于是按序逐个执行commit_ordered方法,因此能够保证事务
    // commit的顺序与binlog commit的顺序是完全一致的
    // 在commit_ordered方法调用完成之后,才能唤醒对应的线程
    // 事务线程被唤醒之后,才能够进入2PC的第二阶段
    // 返回handler.cc::ha_commit_trans()函数,执行commit_one_phase_low
  
    current = queue;
    while (current != NULL)
        run_commit_ordered(current->thd, current->all);
            if (ht->commit_ordered)
               ht->commit_ordered(ht, thd, all);
        current->thd->signal_wakeup_ready();
        current = next;
  
    // 最后释放commit ordered mutex
  
    mysql_mutex_unlock(&LOCK_commit_ordered);
  
ha_innodb.cc::innobase_commit_ordered ->innobase_commit_ordered_low函数分析
    // 获取事务对应的binlog日志的位置
    mysql_bin_log_commit_pos();
    // 设置当前事务标识为flush log later,写commit日志,但是不flush
    trx->flush_log_later = TRUE;
    innobase_commit_low(trx);
    trx->flush_log_later = FALSE;

在函数ha_innodb.cc::innobase_commit_ordered执行完成之后,逐层返回到handler.cc::ha_commit_trans()函数,执行2PC的第二阶段,commit_one_phase_low(),对Commit日志进行group commit。

WL#132

  • 二进制日志Binlog在MySQL数据库中起着TC(Transaction Coordinator)功能二进制日志(Binlog只是TC的一种方案,另一种是Mmap TC);
  • TC在crash恢复时作为控制中心,决定各引擎参与事务的提交与回滚(在WL#164之前,binlog只能与innodb-flush-log-at-trx-commit = 1设置同时使用,二进制日志binlog中的事务一定commit;不存在与binlog中的事务一定rollback;不存在re-play binlog的操作);
  • MySQL数据库为TC做了所谓“middle engine”XA优化(在参与XA事务的所有参与者中,有且仅有一个参与者可以将【prepare,commit】组合简化为commit即可,但是前提是此参与者的commit要在其他参与者完成prepare,未进行commit操作时进行,middle engine),Binlog不需要prepare阶段,其他引擎prepare之后直接写binlog即可;
  • 二进制日志Binlog新增log_and_order方法,控制事务提交顺序;目前,InnoDB存储引擎实现了commit_ordered方法,未实现prepare_ordered方法。

 

WL#164

在Group Commit的基础上,MariaDB数据库再接再厉,推出WL#164。将一次事务提交需要的3次fsync,降低为1次- binlog fsync。用户可以在将参数innodb-flush-log-at-trx-commit设置为{0,2}时,达到与该参数为1时同样的可靠性,不会丢失已提交更新。实现方案也较为简洁,在原有XA recover的基础上,新增了一个处理【二进制日志binlog存在,但是InnoDB prepare log不存在】的情况,此时需要根据binlog重做(re-play)一遍即可(类似于slave根据binlog恢复的情形)。

在WL#164之后,binlog与InnoDB redo log之间的关系,存在以下几种组合:

  • Binlog与commit log同时存在 ——》no operation in crash recovery
  • Binlog与prepare log同时存在 ——》commit
  • Binlog存在,prepare log不存在 ——》re-play binlog
  • Binlog不存在,prepare log存在 ——》rollback

 

MySQL数据库外部XA事务支持,语法见上面的博客。

经过测试,

  • 在调用xa prepare命令时,同样不写binlog,或者说是binlog的prepare为空。但是会写InnoDB存储引擎的prepare log。
  • 在调用xa commit命令时,会写binlog,同时写InnoDB存储引擎的commit log。
  • 问题?二进制日志binlog不写prepare日志,如何恢复?

建议继续学习:

  1. MariaDB常见问题FAQ    (阅读:7570)
  2. MariaDB数据库5.5.27 HASH JOIN源码解读    (阅读:1642)
QQ技术交流群:445447336,欢迎加入!
扫一扫订阅我的微信号:IT技术博客大学习
© 2009 - 2024 by blogread.cn 微博:@IT技术博客大学习

京ICP备15002552号-1