mysql内部的2PC

mysql开启binlog后实际上可以认为其数据有两份,binlog中一份,引擎中一份(这里先把存储引擎中数据看成整体的单独一份,另外也可以把binlog看成是一个引擎)。既然出现了副本,那么就不可避免的牵涉到了一致性问题,mysql在内核内部使用了经典的2PC协议实现数据一致性。

2PC协议需要一个协调者,而在binlog与引擎的2PC协议实现中,由binlog充当这一角色。

mysql事务的提交函数为ha_commit_trans

//sql/handler.cc
int ha_commit_trans(THD *thd, bool all, bool ignore_global_read_lock)
{
······

这个函数为mysql的提交函数,这个函数在事务提交时被调用,在内部实现了2PC的事务提交。众所周知,2PC分为两个阶段,prepare与commit,而在这个函数的代码中,实际上也可以看到以这两个阶段为名的调用 tc_log->prepare(thd, all)与tc_log->commit:

int ha_commit_trans(THD *thd, bool all, bool ignore_global_read_lock)
{
······
if (!trn_ctx->no_2pc(trx_scope) && (trn_ctx->rw_ha_count(trx_scope) > 1))
error= tc_log->prepare(thd, all);
······
if (error || (error= tc_log->commit(thd, all)))
{
ha_rollback_trans(thd, all);
error= 1;
goto end;
}
······
}

tc_log是一个全局指针:

//sql/tc_log.cc
TC_LOG *tc_log;

查看tc_log的定义:

/**
Transaction Coordinator Log. A base abstract class for three different implementations of the
transaction coordinator. The server uses the transaction coordinator to order transactions
correctly and there are three different implementations: one using
an in-memory structure, one dummy that does not do anything, and one
using the binary log for transaction coordination.
*/
class TC_LOG
{
public:
/**
Perform heuristic recovery, if --tc-heuristic-recover was used. @note no matter whether heuristic recovery was successful or not
mysqld must exit. So, return value is the same in both cases. @retval false no heuristic recovery was requested
@retval true heuristic recovery was performed
*/
bool using_heuristic_recover(); TC_LOG() {}
virtual ~TC_LOG() {} enum enum_result {
RESULT_SUCCESS,
RESULT_ABORTED,
RESULT_INCONSISTENT
}; /**
Initialize and open the coordinator log.
Do recovery if necessary. Called during server startup. @param opt_name Name of logfile. @retval 0 sucess
@retval 1 failed
*/
virtual int open(const char *opt_name)=0; /**
Close the transaction coordinator log and free any resources.
Called during server shutdown.
*/
virtual void close()=0; /**
Log a commit record of the transaction to the transaction
coordinator log. When the function returns, the transaction commit is properly
logged to the transaction coordinator log and can be committed in
the storage engines. @param thd Session to log transaction for.
@param all @c True if this is a "real" commit, @c false if it is a "statement" commit. @return Error code on failure, zero on success.
*/
virtual enum_result commit(THD *thd, bool all) = 0; /**
Log a rollback record of the transaction to the transaction
coordinator log. When the function returns, the transaction have been aborted in
the transaction coordinator log. @param thd Session to log transaction record for. @param all @c true if an explicit commit or an implicit commit
for a statement, @c false if an internal commit of the statement. @return Error code on failure, zero on success.
*/
virtual int rollback(THD *thd, bool all) = 0; /**
Log a prepare record of the transaction to the storage engines. @param thd Session to log transaction record for. @param all @c true if an explicit commit or an implicit commit
for a statement, @c false if an internal commit of the statement. @return Error code on failure, zero on success.
*/
virtual int prepare(THD *thd, bool all) = 0;
};

从源码和注释中可以看出,这是一个虚基类,作为事务提交的协调器,实现了事务的prepare、commit、rollback等接口。那么既然是一个虚基类,那么实际上tc_log指针在ha_commit_trans中应该是指向继承TC_LOG类的一个子类的对象。查看这个指针在何处初始化可以发现在init_server_components中,该指针被赋值:

//sql/mysqld.cc
static int init_server_components()
{
······
if (total_ha_2pc > 1 || (1 == total_ha_2pc && opt_bin_log))
{
if (opt_bin_log)
tc_log= &mysql_bin_log;
else
tc_log= &tc_log_mmap;
}
······
}

从代码中可以看出当有超过一个支持2PC的存储引擎或者只有一个但是开启了binlog的话,tc_log就会被设置,如果开启了binlog就会以binlog作为事务的协调器。因此,ha_commit_trans中调用的实际上就是MYSQL_BIN_LOG中实现的prepare与commit.查看MYSQL_BIN_LOG中实现的prepare:

int MYSQL_BIN_LOG::prepare(THD *thd, bool all)
{
DBUG_ENTER("MYSQL_BIN_LOG::prepare"); DBUG_ASSERT(opt_bin_log);
/*
The applier thread explicitly overrides the value of sql_log_bin
with the value of log_slave_updates.
*/
DBUG_ASSERT(thd->slave_thread ?
opt_log_slave_updates : thd->variables.sql_log_bin); /*
Set HA_IGNORE_DURABILITY to not flush the prepared record of the
transaction to the log of storage engine (for example, InnoDB
redo log) during the prepare phase. So that we can flush prepared
records of transactions to the log of storage engine in a group
right before flushing them to binary log during binlog group
commit flush stage. Reset to HA_REGULAR_DURABILITY at the
beginning of parsing next command.
*/
thd->durability_property= HA_IGNORE_DURABILITY; int error= ha_prepare_low(thd, all); DBUG_RETURN(error);
}

可以看出,这个函数实际上就只是调用了存储引擎接口的ha_prepare_low接口,使得引擎处于prepare状态。对于binlog而言,实际上只需要写入文件即可,因此可以认为本身就处于prepare状态。事务处于prepare状态后,调用MYSQL_BIN_LOG的commit函数进行提交。

TC_LOG::enum_result MYSQL_BIN_LOG::commit(THD *thd, bool all)
{
······
if (ordered_commit(thd, all, skip_commit))
DBUG_RETURN(RESULT_INCONSISTENT);
······
}

这个函数比较长,先省略其他部分,只看进行的提交的调用,实际上提交操作是在ordered_commit中实现。ordered_commit入口是mysql组提交的入口,暂时不深入,在这个函数中会将binlog写入,并且调用ha_commit_low在引擎进行提交。

将几个主要的函数调用栈总结如下:

ha_commit_trans
|
|_ _ _ _ MYSQL_BIN_LOG::prepare
| |
| |_ _ _ _ ha_prepare_low
|
|_ _ _ _ MYSQL_BIN_LOG::commit
|
| _ _ _ _MYSQL_BIN_LOG::ordered_commit
|
|_ _ _ _ha_commit_low

mysql中binlog与存储引擎的2PC的更多相关文章

  1. java面试一日一题:mysql中常用的存储引擎有哪些?

    问题:请讲下mysql中常用的引擎有哪些? 分析:该问题主要考察对mysql存储引擎的理解,及区别是什么? 回答要点: 主要从以下几点去考虑, 1.mysql的存储引擎的基本概念? 2.mysql中常 ...

  2. mysql中四种存储引擎的区别和选择

    前言 数据库存储引擎是数据库底层软件组织,数据库管理系统(DBMS)使用数据引擎进行创建.查询.更新和删除数据.不同的存储引擎提供不同的存储机制.索引技巧.锁定水平等功能,使用不同的存储引擎,还可以 ...

  3. mysql中常见的存储引擎和索引类型

    存储引擎 1.      定义 存储引擎说白了就是如何存储数据.如何为存储的数据建立索引和如何更新.查询数据等技术的实现方法.因为在关系数据库中数据的存储是以表的形式存储的,所以存储引擎也可以称为表类 ...

  4. 【3.3】mysql中的Federated存储引擎,远程表,相当于sql server的linked server

    MySQL中针对不同的功能需求提供了不同的存储引擎.所谓的存储引擎也就是MySQL下特定接口的具体实现. FEDERATED是其中一个专门针对远程数据库的实现.一般情况下在本地数据库中建表会在数据库目 ...

  5. MySql中启用InnoDB数据引擎的方法

    1.存储引擎是什么? Mysql中的数据用各种不同的技术存储在文件(或者内存)中.这些技术中的每一种技术都使用不同的存储机制.索引技巧.锁定水平并且最终提供广泛的不同的功能和能力.通过选择不同的技术, ...

  6. [转帖]一文看懂mysql数据库本质及存储引擎innodb+myisam

    一文看懂mysql数据库本质及存储引擎innodb+myisam https://www.toutiao.com/i6740201316745740807/ 原创 波波说运维 2019-09-29 0 ...

  7. MySQL存储引擎的实际应用以及对MySQL数据库中各主要存储引擎的独特特点的描述

    MySQL存储引擎的实际应用以及对MySQL数据库中各主要存储引擎的独特特点的描述: 1.MySQL有多种存储引擎: MyISAM.InnoDB.MERGE.MEMORY(HEAP).BDB(Berk ...

  8. 【MySQL】MySQL(四)存储引擎、索引、锁、集群

    MySQL存储引擎 MySQL体系结构 体系结构的概念 任何一套系统当中,每个部件都能起到一定的作用! MySQL的体系结构 体系结构详解 客户端连接 支持接口:支持的客户端连接,例如C.Java.P ...

  9. MySQL内核:InnoDB存储引擎 卷1

    MySQL内核:InnoDB存储引擎卷1(MySQL领域Oracle ACE专家力作,众多MySQL Oracle ACE力捧,深入MySQL数据库内核源码分析,InnoDB内核开发与优化必备宝典) ...

随机推荐

  1. 延迟确认和Nagle算法

    前篇文章介绍了三次握手和四次挥手,了解了TCP是如何建立和断开连接的,文末还提到了抓包挥手时的一个“异常”现象,当时无法解释,特地查了资料,知道了数据传输中的延迟确认策略. 何谓延迟确认策略? WIK ...

  2. 利用封装、继承对Java代码进行优化

    注:本文实例分别可以在oldcastle(未优化的代码)和newcastle(优化后的代码)中查看,网址见文末 城堡游戏: 城堡中有多个房间,用户通过输入north, south, east, wes ...

  3. EasyUI Datagrid 分页的情况下实现点击表头的小三角图标对数据库中所有数据重新排序

    说明一下: 当点击 datagrid 表头某一列的小三角图标时,easyui 本身是有排序的,但是在当我们对 datagrid 进行了分页的情况下,点击排序只是对当前页的数据进行排序,而需求需要我对数 ...

  4. 第三章 jQuery中的事件与动画

    第三章jQuery中的事件与动画 一. jQuery中的事件 jQuery事件是对javaScript事件的封装. 1.基础事件 在javaScript中,常用的基础事件有鼠标事件.键盘事件.wind ...

  5. Web Api 接收图片

    public async Task<HttpResponseMessage> Upload() { if (!Request.Content.IsMimeMultipartContent( ...

  6. Python内置函数(26)——enumerate

    英文文档: enumerate(iterable, start=0) Return an enumerate object. iterable must be a sequence, an itera ...

  7. Nginx负载均衡(架构之路)

    [前言] 在大型网站中,负载均衡是有想当必要的.尤其是在同一时间访问量比较大的大型网站,例如网上商城,新闻等CMS系统,为了减轻单个服务器的处理压力,我们引进了负载均衡这一个概念,将一个服务器的压力分 ...

  8. 原生js的一些研究和总结(1)

    数据类型 基本类型值包括: undefined,null,Boolean,Number和String,这些类型分别在内存中占有固定的大小空间,它们的值保存在栈空间,我们通过按值来访问的. 引用类型包括 ...

  9. border三角形阴影(不规则图形阴影)和多重边框的制作

    前言:这是笔者学习之后自己的理解与整理.如果有错误或者疑问的地方,请大家指正,我会持续更新! 1. border的组合写法 border:border-width border-style borde ...

  10. Linux OpenGL 实践篇-4 坐标系统

    OpenGL中顶点经过顶点着色器后会变为标准设备坐标系.标准设备坐标系的各坐标的取值范围是[-1,1],超过这个范围的点将会被剔除.而这个变换的过程可描述为顶点在几个坐标系统的变换,这几个坐标系统为: ...