MySQL之 事务和MVCC
文章目录
- 事务
- What?Why?
- 事务的四大特性
- 事务并发存在的问题
- 事务的隔离级别
- MVCC
- 隐藏字段
- undo log
- 版本链
- 快照读和当前读
- Read View
- Read Committed 下的 MVCC
- Repeatable Read 下的 MVCC
事务
What?Why?
什么是事务?为什么要有事务?
事务,由一个有限的数据库操作序列构成,这些操作要么全部执行,要么全部不执行,是一个不可分割的工作单位。
假如A转账给B 100 元,先从A的账户里扣除 100 元,再在 B 的账户上加上 100 元。如果扣完A的100元后,还没来得及给B加上,银行系统异常了,最后导致A的余额减少了,B的余额却没有增加,这肯定是不行的。所以就需要事务,将A的钱回滚回去。
为什么要有事务呢? 就是为了保证数据的最终一致性。
事务的四大特性
事务四大特性,即ACID,原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)。
- 原子性:事务作为一个整体被执行,包含在其中的对数据库的操作要么全部都执行,要么都不执行。
- 一致性:指在事务开始之前和事务结束以后,数据不会被破坏,假如A账户给B账户转10块钱,不管成功与否,A和B的总金额是不变的。
- 隔离性:多个事务并发访问时,事务之间是相互隔离的,一个事务不应该被其他事务干扰,多个并发事务之间要相互隔离。
- 持久性:表示事务完成提交后,该事务对数据库所作的操作更改,将持久地保存在数据库之中。
好像歪果仁都有拼凑单词的习惯…其实事务主要是为了实现 C ,也就是一致性,具体是通过AID,即原子性、隔离性和持久性来达到一致性的目的,所以这四个不应该相提并论,但是他们就想拼成单词,就把它们排好序搞在一起来念了…
事务并发存在的问题
- 脏读:一个事务读取到了另一个未提交事务修改过的数据
- 重复读:同一个事务内,前后多次读取一条记录,读取到的内容不一致(主要是针对update)
- 幻读:同一个事务内,前后多次读取一个结果集,读取到的内容不一致(主要是针对insert、delete)
事务的隔离级别
- Read Uncommitted:读未提交,一个事务可以读取其他事务没有提交的数据。存在脏读、重复读、幻读问题。
- Read Committed:读已提交,一个事务只能读取其他事务已经提交的数据。解决了脏读问题,但是无法解决重复读和幻读问题。(Oracle的默认隔离级别)
- Repeatable Read:可重复读,一个事务在读取数据的时候,其他事务不可以修改。解决了重复读问题,但是无法解决幻读问题。(MySQL的默认隔离级别)
- Serializable:串行化,事务最高的隔离级别,在该级别下,所有事务都是进行串行化顺序执行的。可以避免脏读、不可重复读与幻读等所有并发问题。但是这种事务隔离级别下,事务执行很耗性能。
MVCC
多版本并发控制(Multi-Version Concurrency Control,MVCC):指的是一种提高并发的技术。最早的数据库系统,只有读读之间可以并发,读写,写读,写写都要阻塞。引入多版本之后,只有写写之间相互阻塞,其他三种操作都可以并行,这样大幅度提高了InnoDB的并发度。在内部实现中,InnoDB通过undolog保存每条数据的多个版本,并且能够找回数据历史版本提供给用户读,每个事务读到的数据版本可能是不一样的。在同一个事务中,用户只能看到该事务创建快照之前已经提交的修改和该事务本身做的修改。
MVCC 在 Read Committed 和 Repeatable Read 两个隔离级别下工作。
MySQL的InnoDB存储引擎默认事务隔离级别是RR(可重复读),是通过“行级锁+MVCC”一起实现的,正常读的时候不加锁,写的时候加锁。而 MVCC 的实现依赖:隐藏字段、undolog、版本链、Read View。
隐藏字段
InnoDB存储引擎在每行数据的后面添加了三个隐藏字段:
- DB_TRX_ID(6字节):表示最近一次对本记录行作修改(insert | update)的事务ID。至于delete操作,InnoDB认为是一个update操作,不过会更新一个另外的删除位,将行表示为deleted。并非真正删除
- DB_ROLL_PTR(7字节):回滚指针,指向当前记录行的undo log信息
- DB_ROW_ID(6字节):随着新行插入而单调递增的行ID。即当表没有主键或唯一非空索引时,InnoDB就会使用这个行ID自动产生聚簇索引。如果表有主键或唯一非空索引,聚簇索引就不会包含这个行ID了(这个DB_ROW_ID跟MVCC关系不大…)
undo log
undo log,回滚日志,用于记录数据被修改前的信息。在表记录修改之前,会先把数据拷贝到undo log里,如果事务回滚,即可以通过undolog来还原数据。
可以这样认为,当delete一条记录时,undo log中会记录一条对应的insert记录,当update一条记录时,它记录一条对应相反的update记录。
版本链
多个事务并行操作某一行数据时,不同事务对该行数据的修改会产生多个版本,然后通过回滚指针(roll_pointer),连成一个链表,这个链表就称为版本链。如下:
- 假设现在有一张user表,表里面有一条数据,id为1,名字为张三:
- 现在开启一个事务,对user表执行
update user set name = '李四' where id = 1
,会进行如下流程操作:
- 首先获得事务ID=100
- 把user表修改前的数据拷贝到undo log
- 修改user表中,id为1的数据,名字改为李四
- 把修改后的数据事务Id=101改成当前事务版本号,并把roll_pointer指向undo log数据地址
快照读和当前读
- 快照读:读取的是记录数据的可见版本(有旧的版本)。不加锁的普通的select语句都是快照读。
- 当前读:读取的是记录数据的最新版本,显式加锁的都是当前读(
for update
、lock in share mode
)。
Read View
- Read View是什么呢? 它就是事务执行SQL语句时,产生的读视图。实际上在InnoDB中,每个SQL语句执行前都会得到一个Read View。
- Read View有什么用呢? 它主要是用来做可见性判断的,即判断当前事务可见哪个版本的数据。
Read view 的几个重要属性:
- m_ids:当前系统中那些活跃(未提交)的事务ID,它数据结构为一个List。
- min_limit_id:表示在生成Read View时,当前系统中活跃的读写事务中最小的事务id,即m_ids中的最小值。
- max_limit_id:表示生成Read View时,系统中应该分配给下一个事务的id值。
- creator_trx_id:创建当前Read View的事务ID。
对于可见版本的判断是从最新版本开始沿着版本链逐渐寻找老的版本,如果遇到符合条件的版本就返回。
判断条件如下:
- 如果当前数据版本的
trx_id < min_limit_id
,说明修改这条数据的事务在当前事务生成 Read View 的时候已提交,所以可见。 - 如果当前数据版本的
trx_id >= max_limit_id
,说明修改这条数据的事务在当前事务生成 Read View 的时候还未启动,所以不可见(事务ID的生成是递增的)。 - 如果
min_limit_id <= trx_id < max_limit_id
:- 如果
m_ids包含trx_id
,则表示在Read View生成的时刻,这个事务还没有提交,但是如果trx_id == creator_trx_id
,说明这条数据是自己生成的,因此是可见的,否则(trx_id != creator_trx_id
)就是不可见的。 - 如果
m_ids不包含trx_id
,说明这个事务在Read View生成之前就已经提交了,因此是可见的。
- 如果
Read Committed 下的 MVCC
事务A在user表中插入一条数据后再多次执行查询,期间事务B有执行更新操作并提交。
事务A | 事务B |
---|---|
1. begin | |
2. insert into user(id, name) value(1, ‘张三’) | |
3. begin | |
4. select * from user where id = 1; // 得到 张三 | |
5. update user set name = ‘李四’ where id = 1; | |
6. commit | |
7. select * from user where id = 1; // 得到 李四 | |
… |
最后事务A查询到的结果是name=李四的记录,我们基于MVCC,来分析一下执行流程:
A开启事务,trx_id为100,插入“张三”后:
B开启事务,trx_id为101
事务A生成一个Read View
变量 值 m_ids [100, 101] max_limit_id 102 min_limit_id 100 creator_trx_id 100 然后回到版本链,开始从版本链中挑选可见的记录:
最新版本的列name的内容是张三,该版本的trx_id值为100。(creator_trx_id == trx_id == 100
)事务B进行修改操作然后提交
事务A再次执行查询操作,新生成一个Read View
变量 值 m_ids [100] max_limit_id 102 min_limit_id 100 creator_trx_id 100 然后去上图的版本链中挑选可见的记录,因为
trx_id == 101
不在 m_ids 集合中(说明该记录已提交)并且min_limit_id == 100 < trx_id == 101 < max_limit_id == 102
,说明该记录对当前事务是可见的,因此就会返回 name = “李四” 的记录。
如此就可以看出,在读已提交(RC)隔离级别下,同一个事务里,两个相同的查询,读取同一条记录(id=1),却返回了不同的数据(第一次查出来是张三,第二次查出来是李四),因此RC隔离级别,存在不可重复读并发问题。
Repeatable Read 下的 MVCC
还是上面的例子,在RR的隔离级别下,最终查询出来的结果是name=张三的记录。
各种事务隔离级别下的Read view工作方式是不一样的,RR可以解决不可重复读问题,就是跟Read View工作方式有关:
在RC的隔离级别下,同一个事务里面,每一次查询都会产生一个新的Read View副本,这样就可能造成同一个事务里前后读取数据可能不一致的问题:
变量 值 m_ids [100, 101] max_limit_id 102 min_limit_id 100 creator_trx_id 100 变量 值 m_ids [100] max_limit_id 102 min_limit_id 100 creator_trx_id 100 两次查询基于两个不同的Read View,因此第一次返回
trx_id == 100
的记录,而第二次查询却返回trx_id == 101
的新的提交记录。而在RR的隔离级别下,一个事务里只会获取一次Read View,都是副本共用的,从而保证每次查询的数据都是一样的:
变量 值 m_ids [100, 101] max_limit_id 102 min_limit_id 100 creator_trx_id 100 第一次查询时生成一个Read View后,之后的查询都会使用这个Read View,因此查询出来的都是
trx_id == 100
的记录(creator_trx_id == trx_id == 100
)。
MySQL之 事务和MVCC相关推荐
- limit实现原理 mysql_解读数据库:深入分析MySQL中事务以及MVCC的实现原理
什么是事务 事务(Transaction)是由一系列对数据库中的数据进行访问与更新的操作所组成的一个程序执行单元. 在同一个事务中所进行的操作,要么都成功,要么就什么都不做.理想中的事务必须满足四大特 ...
- mysql id还原_一次线上DB问题排查(MySQL、事务、MVCC)
背景 在司机数据库中,有一张用于存储司机车型的表,暂且称之为表t.该表结构如下所示: MySQL [comp_epower]> show create table t \G; ********* ...
- mysql 默认事务隔离级别_一文读懂MySQL的事务隔离级别及MVCC机制
回顾前文: <一文学会MySQL的explain工具> <一文读懂MySQL的索引结构及查询优化> (同时再次强调,这几篇关于MySQL的探究都是基于5.7版本,相关总结与结论 ...
- 腾讯面试:MySQL事务与MVCC如何实现的隔离级别?
有情怀,有干货,微信搜索[三太子敖丙]关注这个不一样的程序员. 本文 GitHub https://github.com/JavaFamily 已收录,有一线大厂面试完整考点.资料以及我的系列文章. ...
- MySQL事务以及MVCC详解
文章目录 什么是事务 事务的特性(ACID) ACID之间的关系: Innodb的隔离性有哪些 每个隔离性会造成什么问题 事务怎么保证ACID 事务怎么保证一致性 事务怎么保证原子性 事务怎么保证持久 ...
- Mysql事务隔离MVCC机制
MVCC是Mysql保证可重复读和读已提交两个级别的隔离性用到的一套机制,串行化执行是通过加锁来实现的,而MVCC机制下在对同一行数据进行读和写时,不会直接加锁互斥. MVCC主要由undo日志版本链 ...
- 通俗易懂的MySQL事务及MVCC原理,我先收藏了!
一.事务简介与四大特性 事务指的是一组命令操作,在执行的过程中,要么全部成功,要么全部失败. 由引擎层支持事务,MyISAM就不支持事务,而InnoDB是支持事务的. 事务具有以下四大特性(ACID) ...
- 秒杀 mysql 事务_秒杀怎么样才可以防止超卖?基于mysql的事务和锁实现
Reference: http://blog.ruaby.com/?p=256 并发事务处理带来的问题? 相对于串行处理来说,并发事务处理能大大增加数据库资源的利用率,提高数据库系统的事务吞吐量,从 ...
- mysql中decimal不能为空吗_程序员,知道Mysql中事务ACID的原理吗?
点击上方"linkoffer", 选择关注公众号高薪职位第一时间送达 引言 照例,我们先来一个场景~ 面试官:"知道事务的四大特性么?" 你:"懂,A ...
最新文章
- C++算术运算符与算术表达式
- TabLayout+Viewpager+Fragment实现分页滚动
- Java常见排序算法之直接选择排序
- android开发java环境_搭建Android开发环境 - Android - mobile - JavaEye论坛
- QueryWrapper查询
- Oracle关于时间/日期的操作
- 消息称京东方通过苹果认证 本月开始向iPhone 12供货OLED面板
- 借助拳王虚拟项目公社,自动发货系统,卖虚拟教程产品,实现全自动化赚钱的秘密
- 维护老客户,比发展新客户,成本要低得多
- Unique Functions in MATLAB
- JAVA电影院售票系统毕业设计 开题报告
- 6.4两种给定两个均不超过9的正整数k和n,要求编写程序求k+kk+kkk++…+kk…k (n个k,不是n个k乘积)之和
- 升级IE11时,失败报错如何解决
- java毕业生设计药品管理系统演示录像 2021计算机源码+系统+mysql+调试部署+lw
- 《逻辑:你认为正确,就一定正确吗?》
- 个人网站音乐服务器,自己的私人音乐流媒体服务,这才是多少音乐者的梦寐以求的...
- react中配置less-loader报错
- 为什么支付宝跨行转账可以不收费?支付宝为什么要做这个业务?盈利模式是什么?
- Mac电脑使用:隐藏苹果电脑桌面的硬盘图标
- 解锁!玩转 HelloGitHub 的新姿势
热门文章
- cookie 中的 PHPSESSID
- MacBook Air M1和MateBook X Pro区别
- Oracle WorkFlow基础笔记
- MikroTik RouterOS路由搭建的虚拟专用网络PPTP分支连不上
- d3中图表大小自适应区域大小
- 水果车:写作测试,第一部分
- vue报错:ERR_CONNECTION_REFUSED
- 使用vs2008c++语言开发activex控件教程,使用VS2008C_开发ActiveX控件.pdf
- ubuntu系统安装教程,从u盘制作开始
- datepick二格式 化时间_DateTimePicker中自定义时间或日期显示格式