关于MySQL事务实现原理和mysql事务实现原理详解的问题就给大家分享到这里,感谢你花时间阅读本站内容,更多关于7mysql事务(包括redolog,undolog,MVCC)及事务实现原理、mys
关于MySQL事务实现原理和mysql事务实现原理详解的问题就给大家分享到这里,感谢你花时间阅读本站内容,更多关于7 mysql事务(包括redo log,undo log,MVCC)及事务实现原理、mysql MVCC事务实现原理、MySQL RC事务隔离的实现原理、MySQL 事务的实现原理等相关知识的信息别忘了在本站进行查找喔。
本文目录一览:- MySQL事务实现原理(mysql事务实现原理详解)
- 7 mysql事务(包括redo log,undo log,MVCC)及事务实现原理
- mysql MVCC事务实现原理
- MySQL RC事务隔离的实现原理
- MySQL 事务的实现原理
MySQL事务实现原理(mysql事务实现原理详解)
<h2 id="MysqL事务隔离级别的实现原理">MysqL事务隔离级别的实现原理
<h3 id="知识储备">知识储备
- 只有InnoDB支持事务,所以这里说的事务隔离级别是指InnoDB下的事务隔离级别
- 隔离级别
- 读未提交:一个事务可以读取到另一个事务未提交的修改。这会带来脏读,幻读,不可重复读问题
- 读已提交:一个事务只能读取另一个事务已经提交的修改。其避免了脏读,仍然存在不可以重复读和幻读问题
- 可重复读:同一个事务中多次读取相同的数据返回的结果是一样的。其避免了脏读和不可重复读问题,但是幻读依然存在
- 串行化:事务串行之行。避免了以上所有问题
以上是sql-92标准中定义的四种隔离级别。在MysqL中,默认的隔离级别是REPEATABLE-READ(可重复读),并且解决了幻读问题。
不可重复读重点在于Update和delete,而幻读的重点在于insert
- MVCC的全称是多版本并发控制。MVCC使得InnoDB的事务隔离级别下执行一致性读操作有了保证。简单说就是为了查询一些正在被另一个事务更新的行,并且可以看到它们被更新之前的值。这是一个用来增强并发性的强大技术,可以使得查询不用等待另一个事务释放锁。
MVCC会给每一行增加三个字段。分别是DB-TRX-ID DB-ROLL-PTR,DB-ROW-ID
-
增删查改
在InnoDB中,给每行增加两个隐藏字段来实现MVCC,一个用来记录数据行的创建时间,另一个用来记录行的过期时间,在实际操作中,存储的并不是时间,而是事务版本号,每开启一个新事务,事务的版本号就会递增。所以增删改查中对版本号的作用如下:
- select:
- 读取创建版本小于或等于当前事务版本号,并且删除版本为空或大于当前事务版本的记录。这样可以保证在读取之前记录都是存在的
- insert:
- 将当前事务的版本号保存至行的创建版本号
- update
- 新插入一行,并以当前事务版本号作为新行的创建版本号,同时将原记录行的删除版本号设置为当前事务版本号
- delete
- 将当前事务版本号保存至行的删除版本号
- 快照读和当前读
- 快照读:读取的是快照版本,也就是历史版本
- 当前读:读取的是最新版版
- 普通的select就是快照读,而update,delete,insert,select...LOCK In SHARE MODE,SELECT...for update就是当前读
MVCC会给每一行增加三个字段。分别是DB-TRX-ID DB-ROLL-PTR,DB-ROW-ID
- select:
- 读取创建版本小于或等于当前事务版本号,并且删除版本为空或大于当前事务版本的记录。这样可以保证在读取之前记录都是存在的
- insert:
- 将当前事务的版本号保存至行的创建版本号
- update
- 新插入一行,并以当前事务版本号作为新行的创建版本号,同时将原记录行的删除版本号设置为当前事务版本号
- delete
- 将当前事务版本号保存至行的删除版本号
- 快照读:读取的是快照版本,也就是历史版本
- 当前读:读取的是最新版版
- 普通的select就是快照读,而update,delete,insert,select...LOCK In SHARE MODE,SELECT...for update就是当前读
- 在一个事务中,标准的SELECT语句是不会加锁,但是有两种情况例外。SELECT ... LOCK IN SHARE MODE 和 SELECT ... FOR UPDATE。
- SELECT ... LOCK IN SHARE MODE:给记录假设共享锁,这样其他事务职能读不能修改,直到当前事务提交
- SELECT ... FOR UPDATE:给索引记录加锁,这种情况跟UPDATE的加锁情况是一样的
- SELECT ... LOCK IN SHARE MODE:给记录假设共享锁,这样其他事务职能读不能修改,直到当前事务提交
- SELECT ... FOR UPDATE:给索引记录加锁,这种情况跟UPDATE的加锁情况是一样的
- consistent read(一致性读),InnoDB用多版本来提供查询数据库在某个时间点的快照。如果隔离级别是REPEATABLE READ,那么在同一个事务中的所有一致性读都读的是事务中第一个的读读到的快照;如果是READ COMMITTED,那么一个事务中的每一个一致性读都会读到它自己刷新的快照版本。Consistent read(一致性读)是READ COMMITTED和REPEATABLE READ隔离级别下普通SELECT语句默认的模式。一致性读不会给它锁访问的表加任何形式的锁,因此其他事务可以同时并发的修改它们
- Record Locks(记录锁):在索引记录上加锁
- Gap Locks(间隙锁):在索引记录之间加锁,或者在第一个索引记录之前加锁,或者在最后一个索引记录之后加锁
- Next-Key Locks:在索引记录上加锁,并且在索引记录之前的间隙加锁。相当于Record Locks与Gap Locks的一个结合
假如一个索引包含以下几个值:10,11,13,20.那么这个索引的next-key锁将会覆盖以下区间: (negative infinity,10] (10,11] (11,13] (13,20] (20,positive infinity)
- 在默认的隔离级别中,普通的SELECT用的是一致性读不加锁。而对于锁定读,UPDATE和DELETE,则需要加锁,至于加什么锁是有不同情况的。如果对一个唯一索引使用了唯一的检索条件,那么只需要锁定相应的索引记录就好;如果是没有使用唯一索引作为检索条件,或者用到了索引范围扫描,那么将会使用间隙锁或者next-key锁来以此阻塞其他会话向这个范围内的间隙插入数据
- 利用MVCC实现一致性非锁定读,保证在同一个事务中多次读取相同的数据返回的结果是一样的,解决了不可重复读问题
- 利用Gap Locks和Next-key可以阻止其他事务在锁定区间内插入数据,解决了幻读问题
- 利用MVCC实现一致性非锁定读,保证在同一个事务中多次读取相同的数据返回的结果是一样的,解决了不可重复读问题
- 利用Gap Locks和Next-key可以阻止其他事务在锁定区间内插入数据,解决了幻读问题
综上所述,MysqL的默认隔离级别的实现依赖于MVCC和锁,准确点说就是一致性读和锁
- 客户端A开始一个事务,并以主键唯一索引作为检索条件进行更新
- 客户端B开始一个事务,由于客户端A已经开始了事务并以主键索引作为检索条件,所以会造成该索引被锁定。其他索引以及其他范围则不会被锁定可以正常操作
- 当客户端A重新开始一个事务并没有使用唯一索引作为检索条件
- 客户端B开始一个事务,由于客户端A的事务操作,那么MysqL会使用next-key和间隙锁以此阻塞其他会话对表的操作
7 mysql事务(包括redo log,undo log,MVCC)及事务实现原理
之前写了几篇mysql存储原理的文章。
6 Innodb_buffer_pool
5 b+ tree和每个page存储结构
4 innodb文件系统基本结构(段、簇、页面)
3 innodb文件系统初步入门
2 表对象缓存
1 连接层
这一篇终于到事务了,事务大家都知道ACID概念,那么mysql是如何完成事务的呢?
先来看结论——
原子性:
一次事务中的所有操作,要么全部完成要么全部不执行。这里是通过undo log来实现的。
undo log又是什么呢,可以理解为要执行的sql的反向sql,也就是回滚sql。譬如你insert了一条,undo log里就会保存一个delete xxx id = xxx。
持久性:
一旦一个事务被提交,就算服务器崩溃,仍然不能丢数据,在下次启动时需要能自动恢复。这里是通过redo log实现的。
那么如何才能持久呢,很简单,写入硬盘就行了。通过前面几篇的学习,我们知道mysql大部分时间是在Innodb_buffer_pool里做内存读写的,特定情况下才会落盘。这样如果突然服务器崩溃,没来得及落盘的数据就会丢失。所以有了redo log顺序写磁盘(顺序写速度极快,后续的落盘是随机写,速度慢),在事务提交后,事务日志会顺序写入磁盘,然后写入pool内存里。然后才是后续的那些按规则将索引数据落盘。
隔离性:
事务的隔离性是通过读写锁+MVCC来实现的。mysql在为了并发量和数据隔离方面做了很多的尝试,其中MVCC就是比较好的解决方案。
也就是面试最常见的4大隔离级别。
1.读未提交 其它事务未提交就可以读
2.读已提交 其它事务只有提交了才能读
3.可重复读 只管自己启动事务时候的状态,不受其它事务的影响(mysql默认)
4.事务串行 按照顺序提交事务保证了数据的安全性,但无法实现并发
一致性:
即数据一致性,是通过上面的三种加起来联合实现的。
ACID只是个概念,最终目的就是保证数据的一致性和可靠不丢。
Undo Log
所谓的undo log就是回滚日志,当进行插入、删除、修改操作时,一定会生成undo log,并且一定优先于修改后的数据落盘。
我直接借用别人的图了,zhangsan的银行账户有1000元,他要转400元到理财账户。
可以看到,每一条变更数据的操作,都伴随一条undo log的生成。undo log就是记录数据的原始状态。这一点在一些其他的分布式事务的框架里也有所使用,譬如seata就是采用自己解析sql并生成反向sql存储下来,将来抛异常时就执行回滚语句的方式来做的分布式事务,而不借助于mysql自身的undo log机制。
有了undo log,如果事务执行失败要回滚,那很简单,直接将undo log里的回滚语句执行一遍就好了。mysql就是通过这种方式完成的原子性。
Redo Log
redo log是完成数据持久性的,事务一旦提交,其所做的修改就会永久保存到数据库中,而不能丢失,即便是mysql服务器挂掉了也不能丢。
mysql数据是存储到磁盘的,但读写其实都是操作的buffer pool缓存(前面几篇讲过insert buffer)。这样就会造成写入时,如果仅仅写入到buffer pool了,还没来得及刷入数据页,那么mysql突然宕机,就会丢失数据。
此时redo log的作用就出来了,在写入buffer pool后会同时写入到redo log(顺序写磁盘)一份,redo log有固定的大小,会被重复使用。
MVCC及隔离级别
mvcc是什么
MVCC (MultiVersion Concurrency Control) 叫做多版本并发控制。理解为:事务对数据库的任何修改的提交都不会直接覆盖之前的数据,而是产生一个新的版本与老版本共存,使得读取时可以完全不加锁。
有点抽象是吗,再来详细解释一下。同一行数据会有多个版本,某事务对该数据的修改并不会直接覆盖老版本,而是产生一个新版本和老版共存。然后在该行追加两个虚拟的列,列就是进行数据操作的事务的ID(created_by_txn_id),是一个单调递增的ID;还有一个deleted_by_txn_id,将来用来做删除的。
那么在另一个事务在读取该行数据时,由具体的隔离级别来控制到底读取该行的哪个版本。同时,在读取过程中完全不加锁,除非用select * xxx for update强行加锁。
譬如read committed级别,每次读取,总是取事务ID最大的那个就好了。
对于Repeatable read,每次读取时,总是取事务ID小于等于当前事务的ID的那些数据记录。在这个范围内,如果某一数据有多个版本,则取最新的。
MVCC在mysql中的实现依赖的是undo log与read view
undo log记录某行数据的多个版本的数据;read view用来判断当前版本数据的可见性。
mysql就是用MVCC来实现读写分离不加锁的。
那么MVCC里多出来的那些版本的数据最终是要删除的,支持MVCC的数据库套路一般差不多,都会有一个后台线程来定时清理那些肯定没用的数据。只要一个数据的deleted_by_txn_id不为空,并且比当前还没结束的事务ID最小的一个还小,该数据就可以被清理掉了。在PostgreSQL中,该清理任务叫“vacuum”,在Innodb中,叫做“purge”。
隔离级别
隔离级别目的很明确,管理多个并发读写请求的访问顺序,包括串行和并行,要在并发性能和读取数据的正确性上做个权衡。
其中的两个隔离级别Serializable和 Read Uncommited几乎用不上,这里不谈。
Read Committed
能读到其他事务已提交的内容,这是Springboot默认的隔离级别。一个事务在他提交之前的所有修改,对其他事务不可见。提交后,其他事务就能读到了。在很多场景下这种逻辑是可以接受的。
在这个隔离级别下,读取数据不加锁而是使用MVCC机制,写入数据就是排他锁。该级别会产生不可重复读和幻读问题。
不可重复读就是在一个事务内多次读取的结果不一样,这个很容易理解,上面讲MVCC时也说了,该级别每次select时都会去读取最新的版本,所以同一个事务内,也就是代码前面一行select了,后面又select了,可能会select到不同的值。因为这两次select过程中,有其他事务对select的行进行了事务提交,就会被select出来最新的。
幻读,即一个事务能够读取到插入的新数据。会出现幻读也是一样的道理,第一次select时还没值,再次select时又有值了。
Repeatable Read
这个级别名字就是可重复读,这是mysql默认的隔离级别。
为什么能重复读,前面讲MVCC时也说了,这个级别下,一旦读到某个版本,后续都是这个版本了,好比是一次快照,就不关心其他事务对该行数据的提交了,它只认第一次读取时的版本号。
这个级别在一些场景下很重要,如
数据备份:
例如数据库S从数据库M中复制数据,但是M又不停的在修改数据。S需要拿到M的一个数据快照,但又不能停M。
数据合法性校验:
例如有两张表,一张记录了当时的交易总额,另一张记录了每个交易的金额。那么在读取数据时,如果没有快照的存在,交易总额就可能和当时的交易总额对不上。
该级别依然会出现幻读的问题,repeatable是可以出现幻读的,一个事务虽然不能读取其他事务对现有数据的修改,但是能够读取到插入的新数据。
即便是MVCC也解决不了幻读的问题,这里有一篇讲的原因。
写前提困境
尽管在MVCC的加持下Read Committed和Repeatable Read都可以得到很好的实现。但是对于某些业务代码来讲,在当前事务中看到/看不到其他的事务已经提交的修改的意义不是很大。这种业务代码一般是这样的:
先读取一段现有的数据
在这个数据的基础上做逻辑判断或者计算;
将计算的结果写回数据库。
这样第三步的写入就会依赖第一步的读取。但是在1和3之间,不管业务代码离得有多近,都无法避免其他事务的并发修改。换句话说,步骤1的数据正确是步骤3能够在业务上正确的前提,这样其实与MVCC都没什么关系了,因为我们想象中的要操作的数据和实际值并不一样,无论怎么步骤3的结果其实都不对了。
无论你用哪种隔离,你都无法解决第一步读取的数据和第三步操作之间,别的事务对它的修改。
解决方法:
结论
虽然上面写了很多,也很复杂,貌似不上锁怎么都难以解决写前提困境。而事实上,我们几乎不用考虑这样的场景,极少有可能说多个客户端同时操作同一条数据,又刚好碰上需要抉择read committed还是Repeatable read的困境。
结论很简单,不管他就好,你几乎没机会碰到这样的选择困境。
mysql MVCC事务实现原理
大家知道MySQL中的事务是基于MVCC版本链实现的,但是MySQL对于我们来说是一个黑盒,对于底层的实现了解的不是很多。本文主要介绍MySQL中的InnoDB引擎的MVCC的实现原理,由浅到深带领大家从根上理解MySQL
InnoDB行格式
InnoDB存储引擎中记录是以行的形式存储的,这就意味着数据页(page)中保存的是一行行的数据,我们把记录在磁盘上的存放方式被称为行格式或者记录格式。到目前为止设计了4种不同类型的行格式,分别为Compact、Redundant、Dynamic和Compressed。本文只简单的介绍Compact行格式(其他的行格式大同小异,暂不做介绍)。可以通过下列命令修改、查看行格式
## 创建表设置行格式
CREATE TABLE 表名 (列的信息) ROW_FORMAT=行格式名称
## 修改行格式
ALTER TABLE 表名 ROW_FORMAT=行格式名称
##查看表行格式
SHOW TABLE STATUS LIKE "表名"
COMPACT行格式
上图为compact行格式的结构示意图,其中跟事务(MVCC)有关联的是隐藏列的内容
变长字段长度列表
mysql支持一些变长字段类型比如:VARCHAR、TEXT、BLOB等。变长字段中存储多少字节的数据是不固定的,所以我们在存储真实数据的时候需要顺便把这些数据占用的字节数也存起来。
null值列表
表中的某些列可能存储NULL值,如果把这些NULL值都放到记录的真实数据中存储会很占地方,所以Compact行格式把这些值为NULL的列统一管理起来,存储到NULL值列表
记录头信息
隐藏列
名称 | 描述 |
---|---|
row_id | 列id(如果表没有指定主键,该列为隐藏主键) |
trx_id | 事务id |
roll_pointer | 回滚指针、指向undo日志 |
SQL标准中的四种隔离级别
- READ UNCOMMITTED:未提交读。
- READ COMMITTED:已提交读。
- REPEATABLE READ:可重复读。
- SERIALIZABLE:可串行化。
事务隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
READ UNCOMMITTED | 是 | 是 | 是 |
READ COMMITTED | 否 | 是 | 是 |
REPEATABLE READ | 否 | 否 | 是 |
SERIALIZABLE | 否 | 否 | 否 |
MVCC原理
版本链
上面介绍过行格式中有个隐藏的列(row_id,trx_id,roll_pointer),其中row_id不是必须的。
- trx_id:每次一个事务对某条聚簇索引记录进行改动时,都会把该事务的事务id赋值给trx_id隐藏列。
- roll_pointer:每次对某条聚簇索引记录进行改动时,都会把旧的版本写入到undo日志中,然后这个隐藏列就相当于一个指针,可以通过它来找到该记录修改前的信息。
备注:事务执行过程中,只有在第一次真正修改记录时(比如使用INSERT、DELETE、UPDATE语句),才会被分配一个单独的事务id,这个事务id是递增的
当前有个hero的表,查询结果下图:
假设插入该记录的事务id为80,那么此刻该条记录的示意图如下所示
之后两个事务id分别为100、200的事务对这条记录进行UPDATE操作,操作流程如下:
事务trx_id 100 | 事务trx_id 200 |
---|---|
begin | |
begin | |
UPDATE hero set name="关羽" | |
UPDATE hero set name="张飞" | |
commit | |
UPDATE hero set name="赵云" | |
UPDATE hero set name="诸葛亮" | |
commit |
此时的版本链就如下图所示,可以看到记录的修改组成了一个链表,链表中每个节点都记录了当前记录的事务id(trx_id),MVCC也是基于这些链表去实现的事务级别的4种隔离级别,也就是下面介绍的ReadView。
ReadView
对于使用READ UNCOMMITTED隔离级别的事务来说,由于可以读到未提交事务修改过的记录,所以直接读取记录的最新版本就好了;对于使用SERIALIZABLE隔离级别的事务来说,规定使用加锁的方式来访问记录;对于使用READ COMMITTED和REPEATABLE READ隔离级别的事务来说,都必须保证读到已经提交了的事务修改过的记录,也就是说假如另一个事务已经修改了记录但是尚未提交,是不能直接读取最新版本的记录的,核心问题就是:需要判断一下版本链中的哪个版本是当前事务可见的。为此mysql设计出了ReadView的概念,ReadView中有4个比较重要的属性:
- m_ids:表示在生成ReadView时当前系统中活跃的读写事务的事务id列表。
- min_trx_id:表示在生成ReadView时当前系统中活跃的读写事务中最小的事务id,也就是m_ids中的最小值。
- max_trx_id:表示生成ReadView时系统中应该分配给下一个事务的id值。
- creator_trx_id:表示生成该ReadView的事务的事务id。
有了这个ReadView,这样在访问某条记录时,只需要按照下边的步骤判断记录的某个版本是否可见:
- 如果被访问版本的trx_id属性值与ReadView中的creator_trx_id值相同,意味着当前事务在访问它自己修改过的记录,所以该版本可以被当前事务访问。
- 如果被访问版本的trx_id属性值小于ReadView中的min_trx_id值,表明生成该版本的事务在当前事务生成ReadView前已经提交,所以该版本可以被当前事务访问。
- 如果被访问版本的trx_id属性值大于或等于ReadView中的max_trx_id值,表明生成该版本的事务在当前事务生成ReadView后才开启,所以该版本不可以被当前事务访问。
- 如果被访问版本的trx_id属性值在ReadView的min_trx_id和max_trx_id之间,那就需要判断一下trx_id属性值是不是在m_ids列表中,如果在,说明创建ReadView时生成该版本的事务还是活跃的,该版本不可以被访问;如果不在,说明创建ReadView时生成该版本的事务已经被提交,该版本可以被访问。
基于上面的ReadView的规则,READ COMMITTED和REPEATABLE READ有什么不同呢?
READ COMMITTED —— 每次读取数据前都生成一个ReadView
REPEATABLE READ —— 在第一次读取数据时生成一个ReadView
参考资料:
MySQL技术内幕
MySQL是怎样运行的
MySQL RC事务隔离的实现原理
摘要:Read Committed
事务运行期间,只要别的事务修改数据并提交,即可读到人家修改的数据,所以会有不可重复读、幻读问题。ReadView机制基于undo log版本链条实现的一套读视图机制,事务生成一个ReadView:若为事务自己更新的数据,自己可以读到或在你生成ReadView之前提交的事务所修改的值,也可读到但若你生成ReadView时,就已经活跃的事务,但如果它在你生成Read...
ReadView机制基于undo log版本链条实现的一套读视图机制,事务生成一个ReadView:
- 若为事务自己更新的数据,自己可以读到
- 或在你生成
ReadView
之前提交的事务所修改的值,也可读到 - 但若你生成
ReadView
时,就已经活跃的事务,但如果它在你生成ReadView之后修改的数据并提交了,此时你读不到 - 或你生成
ReadView
以后再开启的事务修改了数据,还提交了,也读不到
所以上面那套机制就是ReadView
的一个原理如何基于ReadView实现RC?核心设计:当一个事务设置RC,他是每次发起查询,都重新生成一个ReadView!
数据库里有一行数据,是事务id=50的一个事务,很久以前就插入的,当前活跃事务:
- 事务A(id=60)
- 事务B(id=70)
现在事务B发起update,更新这条数据为b,所以此时数据的trx_id会变为事务B的id=70,同时生成一条undo log:
这时,事务A要发起一次查询操作,就会生成一个ReadView
这时事务A发起查询,发现当前这条数据的trx_id=70。即属于ReadView的事务id范围之间,说明是他生成ReadView之前就有这个活跃的事务,是这个事务修改了这条数据的值,但此时事务B还没提交,所以ReadView的m_ids活跃事务列表里,有[60, 70]两个id,此时根据ReadView
机制,事务A无法查到事务B修改的值b。
接着就顺着undo log版本链条往下查找,就会找到一个原始值,发现其trx_id是50,小于当前ReadView里的min_trx_id,说明是他生成ReadView
之前,就有一个事务插入了这个值并且早就提交了,因此可以查到这个原始值。
接着,假设事务B提交,提交了就说明事务B不会活跃于数据库里了。事务A下次再查询,就可以读到事务B修改过的值了。那到底是怎么让事务A能够读到提交的事务B修改过的值呢?
让事务A下次发起查询,再生成一个ReadView,数据库内活跃的事务只有事务A,因此:
min_trx_id
是60mac_trx_id
是71m_ids=60
,事务B的id=70不会出现在m_ids
活跃事务列表
此时事务A再次基于这个ReadView去查询,会发现这条数据的trx_id=70,虽然在ReadView
的min_trx_id
和max_trx_id
范围之间,但是此时并不在m_ids列表内,说明事务B在生成本次ReadView
之前就已提交。说明这次你查询就可以查到事务B修改过的这个值了, 此时事务A就会查到值B。
到此这篇关于MySQL RC事务隔离的实现的文章就介绍到这了,更多相关MySQL RC事务隔离内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!
- 解决MySql8.0 查看事务隔离级别报错的问题
- 深入理解MySQL事务的4种隔离级别
- Mysql事务并发脏读+不可重复读+幻读详解
- 一文搞懂MySQL脏读,幻读和不可重复读
- MYSQL事务的隔离级别与MVCC
- Mysql数据库事务的脏读幻读及不可重复读详解
- 一文了解MySQL事务隔离级别
- MySQL脏读幻读不可重复读及事务的隔离级别和MVCC、LBCC实现
MySQL 事务的实现原理
1.Redo log
什么是redo log redo log也叫重做日志,用来实现事务的持久性。当事务提交之后会把所有修改信息存放在改日志中。mysql为了提升性能不会把每次的修改同步到磁盘中,而是先存到Buffer Pool中,把这个当做缓存来用,然后使用后台的线程去缓冲池和磁盘之前的同步。
如果在写入Buffer Pool中后,突然断电或者宕机,还没来得及进行同步,这样会导致丢失已经提交事务的修改信息。所以引入了redo log来记录已经提交事务的修改信息,并且会把redo log持久化到磁盘,系统重启之后会再读取redo log回复最新数据。
2.Undo log
undo log在回滚日志,用于记录数据被修改前的信息。他正好跟前面所说的重做日志记录相反。undo log记录修改前的数据,为了在发送错误回滚之前的操作,需要将之前的操作都记录下来,然后在发生错误时才可以回滚。undo记录了事务修改之前版本的信息,假设由于系统错误或者rollback操作而回滚的话可以根据undo log的信息进行回滚到被修改前的状态。
MySQL的锁技术
当同一个时刻有多个请求来修改表中数据的时候,需要一种措施来进行并发控制,不然很可能会造成不一致。
通过读、写锁两种锁来对读写请求进行处理。
MVCC 多版本并发控制
InnoDB的MVVC,是通过在每行记录里面保存三列隐藏列来实现的。记录隐式ID、事务ID和回滚指针(指向undo log的记录)
MVCC在mysql中实现的依赖的是undo log与read view
undo log: 用来记录某行数据的多个版本。
read view: 用来判断当前版本的可见性。
版本链的机制: readView
生成ReadView的时机不同, READ COMMITTD在每一次进行普通SELECT操作前都会生成一个ReadView,而REPEATABLE READ只在第一次进行普通SELECT操作前生成一个。
1.用一个单链表的结构存放每个事务对应的行的数据,通过事务id可以获取对应的数据。
2.配合readview的活跃事务组,可以判断当前应该读哪个节点的数据。如果当前事务不在版本链上就像前读取节点,知道节点的事务版本号不在活跃事务组,那么这个版本的数据就是最新已经提交数据。
版本链,readview的使用场景
【创建事务节点】当我创建一个新的事务需要读取一个数据时候。需要去查询活跃事务列表,假设当前我的事务id是200,当前活跃的事务id没有是200的。需要去拷贝一个最新的不活跃事务并将版本链最后插入一个新节点200.mysql会去对比版本链和readview,假设版本链的数据为[1,50,100,150],活跃列表为[100,150].所以事务200拷贝事务50并插入版本链最后,且将200加入活跃链表最后一个元素。
【使用事务节点】当我们再次使用事务200查询或者修改数据,需要读版本链上的数据,因为上次操作已经咋版本链上做了200号节点。因此次吃事务读到的都是200号节点的数据,这样就隔离了其他为未提交的事务,全部的增删改查都在200号版本上进行。
【readview实现事务隔离级别】以上两点都是基于隔离级别:读已提交来进行的。当mysql设置为可重复读的时候,不同事务任然要保存在版本链的不同节点上。只不过新的事务创建的时候拷贝了当下的readview列表,只要当前事务不提交。 就一直使用这个拷贝的活跃列表。假设此时事务100已经提交,我们在新事务下执行了select ,查询到的活跃列表100还是未提交,读取的还是50号事务提交的记录。
事务的实现:
事务的原子性是通过undo log来实现的。
事务的持久性是通过redo log来现实的。
事务的隔离性是通过(读写锁+MVCC)来实现的
而事务的终极大boss一致性是通过原子性,一致性,隔离性来实现的。
今天的关于MySQL事务实现原理和mysql事务实现原理详解的分享已经结束,谢谢您的关注,如果想了解更多关于7 mysql事务(包括redo log,undo log,MVCC)及事务实现原理、mysql MVCC事务实现原理、MySQL RC事务隔离的实现原理、MySQL 事务的实现原理的相关知识,请在本站进行查询。
本文标签: