智能摘要文章源自JAVA秀-https://www.javaxiu.com/42763.html
多个版本的记录,以无锁的方式解决并发读写冲突。数据表里另外一个隐藏字段,DB_ROLL_PTR回滚指针,指向这条记录的上一个版本在undo log中的数据。读到了别的事物已提交的数据,在当前事务中,前后两次的读取可能数据不一致。也是读到了别的事物已提交的数据,在当前事务中,前后两次的读取可能数据不一致。在RC级别下,事物每一次快照读都会创建一个新的ReadView,这样就会造成不可重复的和幻读的问题。文章源自JAVA秀-https://www.javaxiu.com/42763.html
原文约 2323 字 | 图片 4 张 | 建议阅读 5 分钟 | 评价反馈文章源自JAVA秀-https://www.javaxiu.com/42763.html
Mysql中的MVCC机制
菜鸟要飞 文章源自JAVA秀-https://www.javaxiu.com/42763.html
原文:https://www.jianshu.com/p/06ab99fd68a8文章源自JAVA秀-https://www.javaxiu.com/42763.html
MVCC(Multi-Version Concurrency Control),多版本并发控制。文章源自JAVA秀-https://www.javaxiu.com/42763.html
MVCC是一种并发控制的方法,通过维护数据 多个版本的记录,以无锁的方式解决并发读写冲突。目的就是规避在读写冲突的时候进行加锁的操作。Mysql的Innodb引擎就使用了MVCC。文章源自JAVA秀-https://www.javaxiu.com/42763.html
相关概念
要了解MVCC的实现机制,需要先知道Mysql中以下几个概念。文章源自JAVA秀-https://www.javaxiu.com/42763.html
事物ID和DB_TRX_ID
事物ID文章源自JAVA秀-https://www.javaxiu.com/42763.html
我们都知道innodb是支持事物的,在innodb中每一个事物创建时都会分配一个自增的ID作为事物为唯一标志,也就是事物ID。文章源自JAVA秀-https://www.javaxiu.com/42763.html
DB_TRX_ID文章源自JAVA秀-https://www.javaxiu.com/42763.html
数据表里每一行数据都会有一个隐藏字段DB_TRX_ID,用来存储创建或者最后一次修改此记录的事物ID文章源自JAVA秀-https://www.javaxiu.com/42763.html
undo log和DB_ROLL_PTR
DB_ROLL_PTR文章源自JAVA秀-https://www.javaxiu.com/42763.html
数据表里另外一个隐藏字段,DB_ROLL_PTR回滚指针,指向这条记录的上一个版本在undo log中的数据。文章源自JAVA秀-https://www.javaxiu.com/42763.html
undo log文章源自JAVA秀-https://www.javaxiu.com/42763.html
undo log存储每行记录的修改历史。可以简单理解undo log是一个与数据表结构相同的另外一张表,数据表的数据行字段它都有,数据表的行记录每修改一次,就将这行数据的当前记录写到undo log中,并将返回的undo log指针地址写入DB_ROLL_PTR,然后修改数据行数据、更新事物ID。文章源自JAVA秀-https://www.javaxiu.com/42763.html
undo log主要分为两种:文章源自JAVA秀-https://www.javaxiu.com/42763.html
insert undo log代表事务在insert新记录时产生的undo log, 只在事务回滚时需要,并且在事务提交后可以被立即丢弃update undo log事务在进行update或delete时产生的undo log; 不仅在事务回滚时需要,在快照读时也需要;所以不能随便删除,只有在快速读或事务回滚不涉及该日志时,对应的日志才会被purge线程统一清除文章源自JAVA秀-https://www.javaxiu.com/42763.html
结构大概如下图所示:文章源自JAVA秀-https://www.javaxiu.com/42763.html
文章源自JAVA秀-https://www.javaxiu.com/42763.html
undo log可以作为mvcc中查找对应可读记录,也可以作为当前事物的rollback依据。undo log也并不是无限增长的,会有另外一个线程会尝试清除早期的undo log记录,因为他们已经没有用处了。文章源自JAVA秀-https://www.javaxiu.com/42763.html
当前读与快照读
当前读文章源自JAVA秀-https://www.javaxiu.com/42763.html
当前读指的是读取数据当前最新数据。update、insert、delete、select for update(排他锁)、select lock in share mode。读取数据需要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。文章源自JAVA秀-https://www.javaxiu.com/42763.html
快照读文章源自JAVA秀-https://www.javaxiu.com/42763.html
快照读指的是在读取数据时,生成读取快照,在同一个事物中可能会一直读取此快照的数据。快照读读到的数据可能不是最新的,可能是历史版本的数据,这些历史版本的数据就是从undo log中获取的。事物中的select 不加锁的情况会执行快照读,快照读依赖readview来实现。快照读的前提是隔离级别不是串行级别,串行级别下的快照读会退化成当前读。文章源自JAVA秀-https://www.javaxiu.com/42763.html
ReadView
读视图,由当前活跃事物ID的列表trx_list、当前活跃最小事物ID low_limit_id、下一个即将分配的事物ID up_limit_id,三个部分组成。文章源自JAVA秀-https://www.javaxiu.com/42763.html
事物间可见性分析
基于ReadView的可见性分析逻辑
执行快照读的时候,会创建一个ReadView。定义被读取行的DB_TRX_ID 为trx_id。文章源自JAVA秀-https://www.javaxiu.com/42763.html
比较 trx_id是否小于low_limit_id 或者为当前事物ID,如果为true,则代表修改此行数据的事物早已提交或者就是当前事务进行的修改,当前记录可见。否则进入下一步判断。文章源自JAVA秀-https://www.javaxiu.com/42763.html
比较trx_id是否大于等于up_limit_id,如果为true,则代表修改此行记录的事物晚于当前读视图创建,当前记录不可见,根据DB_ROLL_PTR undo log指针找到上一条记录,从新进行可见性分析。否则进入下一步判断文章源自JAVA秀-https://www.javaxiu.com/42763.html
判断trx_id是否在trx_list列表中,如果在,代表修改此行记录的事物还未提交,当前事务不可以读取当前记录,根据DB_ROLL_PTR undo log指针找到上一条记录,从新进行可见性分析。否则说明数据在readview生成的时候已经提交,当期事物可以读取当前记录。文章源自JAVA秀-https://www.javaxiu.com/42763.html
数据库事物隔离级别与MVCC
脏读读到了别的事物未提交的数据,由于别的事物有可能会回滚,相当于读到了错误的数据。文章源自JAVA秀-https://www.javaxiu.com/42763.html
不可重复读读到了别的事物已提交的数据,在当前事务中,前后两次的读取可能数据不一致。文章源自JAVA秀-https://www.javaxiu.com/42763.html
幻读也是读到了别的事物已提交的数据,在当前事务中,前后两次的读取可能数据不一致。与不重复读区别是,幻读指的是insert或delete产生的不一致,而不可重读指的是update产生的不一致。文章源自JAVA秀-https://www.javaxiu.com/42763.html
数据库为了解决脏读、不可重复读、幻读,定义了事物间的隔离级别。文章源自JAVA秀-https://www.javaxiu.com/42763.html
事物隔离级别文章源自JAVA秀-https://www.javaxiu.com/42763.html
串行化Serializable一切指令同步执行,也就没有以上的问题了。可以解决脏读、幻读、不可重读。文章源自JAVA秀-https://www.javaxiu.com/42763.html
可重复读Repeat Read、RR在同一事物中读取被修改的记录,总是一致的。可以解决脏读、不可重复读。文章源自JAVA秀-https://www.javaxiu.com/42763.html
读已提交Read Committed、RC可以读取别的事物已经提交的数据。可以解决脏读。文章源自JAVA秀-https://www.javaxiu.com/42763.html
读未提交Read UnCommitted可以读到别的事物未提交的数据。啥问题都没解决。文章源自JAVA秀-https://www.javaxiu.com/42763.html
隔离级别是约严格需要的约束越多,相对的性能就会越差。Mysql Innodb的默认数据库隔离级别为RR。Oracle的隔离级别只支持Serializable和RC,另外提供了一种只读的模式,只允许select。文章源自JAVA秀-https://www.javaxiu.com/42763.html
事物隔离级别与MVCC文章源自JAVA秀-https://www.javaxiu.com/42763.html
在RR级别下,事物进行快照读时会检查当前事物是否已经创建过ReadView,如果存在,则使用已经创建的,这也是实现可重复的方法。在RC级别下,事物每一次快照读都会创建一个新的ReadView,这样就会造成不可重复的和幻读的问题。文章源自JAVA秀-https://www.javaxiu.com/42763.html
Innodb利用MVCC解决了RR级别下快照读中的幻读问题,当前读中的幻读问题需要使用GAP lock解决,也就是间隙锁。举个例子:文章源自JAVA秀-https://www.javaxiu.com/42763.html
创建user表,自增主键ID、name、age,三个字段;插入两条数据1-张三-10、2-李四-20;文章源自JAVA秀-https://www.javaxiu.com/42763.html
文章源自JAVA秀-https://www.javaxiu.com/42763.html
启动事物1,查询name=张三的记录,会返回1-张三;启动事物2,新增记录3-张三-30;在事物1中再次查询name=张三的记录,仍然返回1-张三;在事物1中修改name=张三的age=45,提交;会发现1、3的age都会变为45。文章源自JAVA秀-https://www.javaxiu.com/42763.html
文章源自JAVA秀-https://www.javaxiu.com/42763.html
这就是解决了快照读的幻读,而修改操作属于当前读,仍然有幻读的问题。但是如果这里将事物2的提交事务延迟到事物1修改数据之后,会发现事物1的修改数据会被卡住。文章源自JAVA秀-https://www.javaxiu.com/42763.html
这里就使用了间隙锁进行防止写的幻读。文章源自JAVA秀-https://www.javaxiu.com/42763.html
文章源自JAVA秀-https://www.javaxiu.com/42763.html
推荐阅读文章源自JAVA秀-https://www.javaxiu.com/42763.html
嘘!刚刚发现了一个山寨版某库....文章源自JAVA秀-https://www.javaxiu.com/42763.html
大学期间开发了一款App!让我月薪破万文章源自JAVA秀-https://www.javaxiu.com/42763.html
发小被绿,我竭尽所学黑科技,动用云控捉奸寻找证据….文章源自JAVA秀-https://www.javaxiu.com/42763.html
终于把废旧电脑变成了服务器!差点被女票拿去换洗脸盆,真香!
文章源自JAVA秀-https://www.javaxiu.com/42763.html

评论