Mysql事务的基本要素(ACID)
A:原子性:要么全部成功,要么全部失败
C:一致性:事务开始前和结束后,数据库的完整性约束没有被破坏(保证数据的正确性)
I:隔离性:一个事务的执行影响另一个事务的运行
D:持久性:更新后的数据会永久的留存在数据库中
所有要素最终都是为了数据一致性
事务并发产生的问题
多事务并发有三种异常情况,违反了隔离性
- 脏读:一个事务读取到了另一个事务未提交的数据
- 不可重复读:一个事务中同一查询语句执行多次,存在一个记录却有两个不同的内容(一个事务做查询操作,另一个事务做更新操作)
3. 第二类更新丢失(两个事务对同一条数据都在做更新操作)
4.幻读:一个事务中同一查询语句的记录条数不一致(A事务做查询操作,B事务做新增操作)
幻读不不可重复读的区别(一个是由更新导致,一个是由新增或删除导致)
数据库是如何保证事务的隔离性的呢?
数据库可以通过加锁来保证事务的隔离性。但是加锁后性能问题影响过大。MYSQL使用MVCC解决脏读,不可重复读,幻读问题
MySQL的锁类型
SQL标准定义的隔离级别
- 未提交读:
- 已提交读:解决脏读问题
- 可重复读:解决不可重复度问题(mysql默认隔离级别)
- 序列化:解决幻读问题
MYSQL通过锁和MVCC实现以上隔离级别
MVCC原理(多版本并发控制)
MVCC是在并发访问数据库时,通过对数据做多版本管理,避免因为写数据时要加写锁而阻塞读取数据的请求,造成写数据时无法读取数据的问题。。MVCC解决的更多的是如何读的问题。并不能取代数据库的锁和数据不能和数据库中的锁混为一谈。当多个事务对数据资源做出并发修改时还是需要锁的控制
MVCC核心概念
了解MVCC之前我们需要知道以下概念
1. 事务版本号
每次事务开启前都会从数据库获得一个自增长的事务ID,可以从事务ID判断事务的执行先后顺序
2. 表格中的隐藏列
对于InnoDB存储引擎,每一行记录都有两个隐藏列**trx_id**(事务版本号)、**roll_pointer**(回滚指针,roll_pointer存储的是旧数据的地址),如果表中没有主键和非NULL唯一键时,则还会有第三个隐藏的主键列**row_id**。
3. Undo log
用于记录数据被修改前的信息
作用:
- 当事务回滚的时候通过此日志还原数据
- 通过读取undo log的历史版本数据可以实现**不同事务版本号都拥有自己独立的快照数据版本。**
4.版本链
多个事务并行操作某一行数据时,不同事务对该行数据的修改会产生多个版本,然后通过回滚指针(roll_pointer),连成一个链表,这个链表就称为**版本链**
详细介绍:
5. ReadView
它就是事务执行SQL语句时,产生的读视图。实际上在innodb中,每个SQL语句执行前都会得到一个Read View。它主要是用来做可见性判断的,即判断当前事务可见哪个版本的数据
在innodb 中每个事务开启后都会得到一个read_view。副本主要保存了当前数据库系统中正处于活跃(没有commit)的事务的ID号
**Read view 的几个重要属性:**
trx_ids: 当前系统活跃(未提交)事务版本号集合 |
low_limit_id: 创建当前read view 时“当前系统最大事务版本号+1” |
up_limit_id: 创建当前read view 时“系统正处于活跃事务最小版本号” |
creator_trx_id: 创建当前read view的事务版本号 |
**Read view**可见性判断
1. 若事务ID < up_limit_id || 事务ID=creator_trx_id 则显示
表明read view产生后,事务就已经commit
2. 若事务ID ≥ low_limit_id 则不显示
表明read view产生后事务才开始。不能显示
3. up_limit_id≤事务ID<low_limit_id则需要和trx_ids里的数据进行匹配
1. 如果在trx_ids 集合中则不显示
表明read view产生后,事务ID的这个事务还没commit
2. 如果不在trx_ids 集合中则显示
read view产生的时候事务已经commit了
通过ReadView、版本链、undo log来返回快照读数据
核心:判断版本链中的那个版本是当前事务是否可见来返回数据
MVCC实现已提交读
RC级别下事务开启后,在每次执行快照读时生成ReadView,根据**Read view可见性判断**和**版本链undo log**返回结果
MVCC实现可重复读
RR级别下事务开启后,只在第一次快照读时生成ReadView,根据**Read view可见性判断**和**版本链undo log**返回结果
快照读和当前读
快照读:只有select单纯的读取操作,会在读取的时候生成ReadView快照数据
当前读:读取的是最新版本,并且对数据加锁,会阻塞其他操作修改记录。比如select……lock in share mod(加共享锁,又称S锁),select……for update(加排它锁,又称X锁),update,delete,insert这类操作都是当前读
MVCC 在RR级别下 可以解决部分幻读
MVCC 在RR级别下 可以解决部分幻读, 但不能完全解决.
Mysql官方给出的幻读解释是:
只要在一个事务中,第二次select多出了row就算幻读。
1. a事务先select,b事务 insert确实会加一个gap锁,但是如果b事务commit,这个gap锁就会释放(释放后a事务可以随意dml操作)。
2. a事务再select出来的结果在MVCC下还和第一次select一样;这一步没有出现幻读。
3. 接着a事务不加条件地update,这个update会作用在所有行上(b事务已经提交,包括b事务新加的)。
4. a事务再次 select就会出现b事务中的新行(幻读出现),并且这个新行已经被 update修改了。
参考链接:https://zhuanlan.zhihu.com/p/421769708
https://zhuanlan.zhihu.com/p/52977862