首先说明下,这里主要内容为整理总结网络搜索的零散信息。

写在最前面,mysql事务是在Innodb引擎中得以实现的,如果这点不了解的话,请自行了解。

事务直接数据的可见性通过MVCC(多版本并发控制)实现。对同一记录的修改会保存历史版本的数据,通过一系列的逻辑看判断当前事务应该获取的是那个版本的数据,也就是通常意义上的可见性。

Innodb会为每行记录添加三个隐形字段:6字节的事务ID(DB_TRX_ID)、7字节的回滚指针(DB_ROLL_PTR)、隐藏的ID。

MVCC 在mysql 中的实现依赖的是 undo log 与 read view。

a.undo log: undo log中记录的是数据表记录行的多个版本,也就是事务执行过程中的回滚段,其实就是MVCC 中的一行原始数据的多个版本镜像数据。

b.read view: 主要用来判断当前版本数据的可见性。

下面看下一条记录的更新过程:

1.初始数据行

F1~F6是某行列的名字,1~6是其对应的数据。后面三个隐含字段分别对应该行的事务号和回滚指针,假如这条数据是刚INSERT的,可以认为ID为1,其他两个字段为空。
2.事务1更改该行的各字段的值

当事务1更改该行的值时,会进行如下操作:
用排他锁锁定该行
记录redo log
把该行修改前的值Copy到undo log,即上图中下面的行
修改当前行的值,填写事务编号,使回滚指针指向undo log中的修改前的行
3.事务2修改该行的值

下面讲下调用select时mysql到底做了什么:

首先,看下read_view结构:

struct read_view_t{
// 由于是逆序排列,所以low/up有所颠倒
// 能看到当前行版本的高水位标识,>= low_limit_id皆不能看见,low_limit_id取值为max_trx_id(即尚未被分配的trx_id)
trx_id_t low_limit_id;
// 能看到当前行版本的低水位标识,< up_limit_id皆能看见,up_limit_id取值为当前活跃最小事务id
trx_id_t up_limit_id;
// 当前活跃事务(即未提交的事务)的数量
ulint n_trx_ids;
// 以逆序排列的当前获取活跃事务id的数组
// 其up_limit_id<tx_id<low_limit_id
trx_id_t* trx_ids;
// 创建当前视图的事务id
trx_id_t creator_trx_id;
// 事务系统中的一致性视图链表
UT_LIST_NODE_T(read_view_t) view_list;
};

read_view构建逻辑:

在mysql的trx_sys中,一直维护着一个全局的活跃的读写事务id(trx_sys->descriptors),id按照从小到大排序,表示在某个时间点,数据库中所有的活跃(已经开始但还没提交)的读写(必须是读写事务,只读事务不包含在内)事务。当需要一个一致性读的时候(即创建新的readview时),会把全局读写事务id拷贝一份到readview本地(read_view_t->trx_ids),当做当前事务的快照。read_view_t->up_limit_id是read_view_t->trx_ids这数组中最小的值,read_view_t->low_limit_id是创建readview时的max_trx_id(即尚未被分配的trx_id,这样在>=判断时就可以将读事务开启后提交的事务包含进来),即一定大于read_view_t->trx_ids中的最大值。当查询出一条记录后(记录上有一个trx_id,表示这条记录最后被修改时的事务id),可见性判断的逻辑如下(read_view_sees_trx_id):

1.如果记录上的trx_id小于read_view_t->up_limit_id,则说明这条记录的最后修改在readview创建之前,因此这条记录可以被看见。

2.如果记录上的trx_id大于等于read_view_t->low_limit_id,则说明这条记录的最后修改在readview创建之后,因此这条记录肯定不可以被看见。

3.如果记录上的trx_id在up_limit_id和low_limit_id之间,且trx_id在read_view_t->trx_ids之中,则表示这条记录的最后修改是在readview创建之时,被另外一个活跃事务所修改,所以这条记录也不可以被看见。如果trx_id不在read_view_t->trx_ids之中,则表示这条记录的最后修改在readview创建之后被提交,所以可以看到。

注意当隔离级别设置为READ UNCOMMITTED时,不会去构建老版本。

判断行记录可见行源码如下:

/*********************************************************************//**
Checks if a read view sees the specified transaction.
@return true if sees */
UNIV_INLINE
bool
read_view_sees_trx_id(
/*==================*/
const read_view_t* view, /*!< in: read view */
trx_id_t trx_id) /*!< in: trx id */
{
if (trx_id < view->up_limit_id) { return(true);
} else if (trx_id >= view->low_limit_id) { return(false);
} else {
ulint lower = ;
ulint upper = view->n_trx_ids - ; ut_a(view->n_trx_ids > ); do {
ulint mid = (lower + upper) >> ;
trx_id_t mid_id = view->trx_ids[mid]; if (mid_id == trx_id) {
return(FALSE);
} else if (mid_id < trx_id) {
if (mid > ) {
upper = mid - ;
} else {
break;
}
} else {
lower = mid + ;
}
} while (lower <= upper);
} return(true);
}

4.基于上述判断,如果记录不可见,则尝试使用undo去构建老的版本(row_vers_build_for_consistent_read),直到找到可以被看见的记录或者解析完所有的undo,代码如下:

dberr_t row_vers_build_for_consistent_read(...)
{
......
for(;;){
err = trx_undo_prev_version_build(rec, mtr,version,index,*offsets, heap,&prev_version);
......
trx_id = row_get_rec_trx_id(prev_version, index, *offsets);
// 如果当前row版本符合一致性视图,则返回
if (read_view_sees_trx_id(view, trx_id)) {
......
break;
}
// 如果当前row版本不符合,则继续回溯上一个版本(回到for循环的地方)
version = prev_version;
}
......
}

可见性分析如上已经差不多了,那么,不同隔离级别是怎么利用readview达到效果的呢?

针对RR隔离级别,在第一次创建readview(第一次调用select(不加锁))后,这个readview就会一直持续到事务结束,也就是说在事务执行过程中,数据的可见性不会变,所以在事务内部不会出现不一致的情况。针对RC隔离级别,事务中的每个查询语句都单独构建一个readview,所以如果两个查询之间有事务提交了,两个查询读出来的结果就不一样。从这里可以看出,在InnoDB中,RR隔离级别的效率是比RC隔离级别的高。此外,针对RU隔离级别,由于不会去检查可见性,所以在一条SQL中也会读到不一致的数据。针对串行化隔离级别,InnoDB是通过锁机制来实现的,而不是通过多版本控制的机制,所以性能很差。

由下面代码可知,只有单纯的select才创建readview,select for update会加锁所以不会创建readview。

// 只有非锁模式的select才创建一致性视图
else if (prebuilt->select_lock_type == LOCK_NONE) { // 创建一致性视图
trx_assign_read_view(trx);
prebuilt->sql_stat_start = FALSE;
}

也可参考下面描述:

参考资料:

https://my.oschina.net/alchemystar/blog/1927425

http://mysql.taobao.org/monthly/2017/12/01/

http://mysql.taobao.org/monthly/2015/12/01/

https://yq.aliyun.com/articles/560506

mysql事务隔离分析的更多相关文章

  1. Mysql 事务隔离级别分析

    Mysql默认事务隔离级别是:REPEATABLE-READ --查询当前会话事务隔离级别mysql> select @@tx_isolation; +-----------------+ | ...

  2. 一文讲清楚MySQL事务隔离级别和实现原理,开发人员必备知识点

    经常提到数据库的事务,那你知道数据库还有事务隔离的说法吗,事务隔离还有隔离级别,那什么是事务隔离,隔离级别又是什么呢?本文就帮大家梳理一下. MySQL 事务 本文所说的 MySQL 事务都是指在 I ...

  3. [51CTO]新说MySQL事务隔离级别!

    新说MySQL事务隔离级别! 事务隔离级别这个问题,无论是校招还是社招,面试官都爱问!然而目前网上很多文章,说句实在话啊,我看了后我都怀疑作者弄懂没!本文所讲大部分内容,皆有官网作为佐证,因此对本文内 ...

  4. 查询mysql事务隔离级别

    查询mysql事务隔离级别 查询mysql事务隔离级别 分类: DB2011-11-26 13:12 2517人阅读 评论(0) 收藏 举报 mysqlsessionjava   1.查看当前会话隔离 ...

  5. MySQL事务隔离级别测试实例

    https://www.cnblogs.com/huanongying/p/7021555.html MySQL事务隔离级别 事务隔离级别 脏读 不可重复读 幻读 读未提交(read-uncommit ...

  6. Mysql事务-隔离级别

    MYSQL事务-隔离级别 事务是什么? 事务简言之就是一组SQL执行要么全部成功,要么全部失败.MYSQL的事务在存储引擎层实现. 事务都有ACID特性: 原子性(Atomicity):一个事务必须被 ...

  7. MySQL事务隔离级别 解决并发问题

    MySQL事务隔离级别 1. 脏读: 骗钱的手段, 两个窗口或线程分别调用数据库转账表,转账后未提交,对方查看到账后,rollback,实际钱没转. 演示方法: mysql默认的事务隔离级别为repe ...

  8. mysql事务隔离级别、脏读、幻读

    Mysql事务隔离级别本身很重要,再加上可能是因为各大公司面试必问的缘故,在博客中出现的概率非常高,但不幸的是,中国的技术博客要么是转载,要么是照抄,质量参差不齐,好多结论都是错的,对于心怀好奇之心想 ...

  9. mysql事务隔离级别与设置

    mysql数据库,当且仅当引擎是InnoDB,才支持事务: 1.隔离级别 事务的隔离级别分为:未提交读(read uncommitted).已提交读(read committed).可重复读(repe ...

随机推荐

  1. 「题解」「UOJ-164」「清华集训2015」V

    目录 题目 原题目 简要题目 正解 这道题题目简洁新颖,吸引读者阅读兴趣... 题目 原题目 点这里 简要题目 需要你维护长度为n的序列并支持下列操作: 区间加法: 区间赋值: 区间每个 \(a_i\ ...

  2. JAXB--obj2xml&xml2obj

    参考: https://www.cnblogs.com/mumuxinfei/p/8948299.html  去掉 xsi:type="echoBody" xmlns:xsi=&q ...

  3. 引用opencv静态库的makefile写法

    参考博客:https://blog.csdn.net/baidu_31872269/article/details/91985846 参考博客:https://blog.csdn.net/Felaim ...

  4. python的logging库

    logging库 简介 logging库提供日志打印功能. 值得一提的是,不仅能打印到日志文件,还能打印到控制台. 日志级别 logging一共分为5个级别,从低到高依次为:  DEBUG<IN ...

  5. Unity UGUI事件接口概述

    UGUI 系统虽然提供了很多封装好的组件,但是要实现一些特定的功能还是显得非常有限,这时候就需要使用事件接口来完成UI功能的实现.比如我们想实现鼠标移动到图片上时自动显示图片的文字介绍,一般思路会想到 ...

  6. 清晰架构(Clean Architecture)的Go微服务: 程序设计

    我使用Go和gRPC创建了一个微服务,并将程序设计和编程的最佳实践应用于该项目. 我写了一系列关于在项目工作中做出的设计决策和取舍的文章,此篇是关于程序设计. 程序的设计遵循清晰架构(Clean Ar ...

  7. JS高级---体会面向对象和面向过程的编程思想

    体会面向对象和面向过程的编程思想 ChangeStyle是自定义的构造函数,再通过原型添加方法的函数. 实例化对象,导入json参数,和创建cs,调用原型添加的方法函数 过渡,先熟悉记忆 <!D ...

  8. css与js基础

    CSS样式 1 宽高设置 块元素可使用 wid 1字体 font-family :  文本类型 font-size     字体大小 font-style 字体样式 斜体italic   正常norm ...

  9. Web.config和App.config配置连接字符串

    读取配置文件,获取连接字符串 <!-- 第一种 --> <connectionStrings> <add name="connString" conn ...

  10. python中的基本类型

    字符串类型 字符串相当于羊肉串,而字符串就是一串被串起来的字符,在单引号.双引号或三引号内包裹一串字符.需要注意的是:三引号内的字符串可以换行,而单引号内的字符不行 name='anny' name= ...