MySQL事务隔离级别和MVCC

Springboot设置事务隔离等级
1、@EnableTransactionManagement 来启用注解式事务管理
2、@Transactional(isolation = Isolation.DEFAULT,propagation = Propagation.REQUIRED)设置事务隔离级别和传播行为


这两篇文章可以结合起来一起理解:
面试官:谈谈你对Mysql的MVCC的理解?
这一篇我觉得是写MVCC写的最好理解的一篇:ps-里面的版本链举例-事务id为70应该改为事务id为90
总结一下关键点:
如何实现MVCC版本并发控制
1、MVCC只针对读已提交RC和可重复度RR级别
2、由版本链、ReadView实现
3、版本链由事务id、回滚指针构成,这两个字段为聚簇索引的隐藏列
4、ReadView可以理解为一个列表(实际上不是,由几个关键字段组成,详见下一个文章链接),如[70,80,90,100],记录了当前活跃中的事务(即已经begin但未commit的事务)
5、如果有一个新事务要读某一行数据,会从版本链的最上面往下找,版本链的版本如110->100->90->80->70->60

  • a. 比一下110,比ReadView里面最大的一个活跃版本还大,所以是在ReadView之后发生的,不能访问
  • b. 比一下100,在ReadView列表中,还未commit,也不能访问
  • c. 依次比一下90,80,70,同b,都不能访问(如果在ReadView的最大值100和最小值70之间,又不在ReadView的列表中,那说明已经提交了,则可以访问,读这个版本)
  • d. 最后比一下60,比ReadView里面最小的70还小,说明这个事务在之前就提交了,可以读这个版本

6、读已提交RC和可重复读RR的区别在于,生成ReadView的策略不同:

  • a. RC事务在每次select的时候,都会重新生成一个新的ReadView;
  • b. RR事务在第一次select的时候生成一个ReadView,之后一直到该事务结束都是用这个ReadView

Innodb中的事务隔离级别和锁以及MVCC之间的关系
这一篇介绍比较详细
MVCC的意义只是替代读锁,写依旧是加锁的,这样避免了脏写
Mysql的RR隔离级别解决了幻读
ps:
快照读,读取的是历史数据
select * from table where ?;
当前读,读取的是当前数据:特殊的读操作,插入/更新/删除操作,属于当前读,处理的都是当前的数据,需要加锁。
select * from table where ? lock in share mode;
select * from table where ? for update;
insert into table values (…);
update table set ? where ?;
delete from table where ?;
对于快照读来说,幻读的解决是依赖mvcc解决。而对于当前读则依赖于Next-Key锁解决。

  • MVCC解决了快照读的幻读问题
  • 对于当前读的幻读问题,需要用MVCC+Next-Key锁(Next-Key锁是行锁和GAP锁(间隙锁)的合并)解决

MySQL 到底是怎么解决幻读的?
快照(ReadView)读的幻读-mvcc 解决
当前读的幻读-Next-Key锁解决

当前读与快照读
首先读分为:
快照读,读取的是历史数据
select * from table where ?;
当前读,读取的是当前数据:特殊的读操作,插入/更新/删除操作,属于当前读,需要加锁。
select * from table where ? lock in share mode;
select * from table where ? for update;
insert into table values (…);
update table set ? where ?;
delete from table where ?;
对于快照读来说,幻读的解决是依赖mvcc解决。而对于当前读则依赖于Next-Key锁解决。


MySQL事务隔离级别和实现原理(看这一篇文章就够了!)
MySQL 中是如何实现事务隔离的

  • 首先说读未提交,它是性能最好,也可以说它是最野蛮的方式,因为它压根儿就不加锁,所以根本谈不上什么隔离效果,可以理解为没有隔离。
  • 再来说串行化。读的时候加共享锁,也就是其他事务可以并发读,但是不能写。写的时候加排它锁,其他事务不能并发写也不能并发读。
  • 最后说读提交RC和可重复读RR,是采用了MVCC (多版本并发控制)的方式
  • RR级别在一般的标准上是不能解决幻读,但是在Mysql里面已经解决了RR级别幻读的问题

并发写的时候加锁问题:
加锁的过程要分有索引无索引两种情况,比如下面这条语句
update user set age=11 where id = 1
id 是这张表的主键,是有索引的情况,那么 MySQL 直接就在索引数中找到了这行数据,然后干净利落的加上行锁就可以了。
而下面这条语句
update user set age=11 where age=10
表中并没有为 age 字段设置索引,所以, MySQL 无法直接定位到这行数据。那怎么办呢,当然也不是加表锁了。MySQL 会为这张表中所有行加行锁,没错,是所有行。但是呢,在加上行锁后,MySQL 会进行一遍过滤,发现不满足的行就释放锁,最终只留下符合条件的行。虽然最终只为符合条件的行加了锁,但是这一锁一释放的过程对性能也是影响极大的。所以,如果是大表的话,建议合理设计索引,如果真的出现这种情况,那很难保证并发度。


mysql在RR隔离级别下有MVCC,那还有共享锁吗?
回答一:对于具备多版本控制能力的数据库引擎确实不需要对表的读取加锁, MVCC能保障读取的完整性. MVCC 仅于单行(或单个字段)起作用, 锁可以实现多行多表多操作的事务锁定. 锁用途和 MVCC 是不太一样的, 可以说事务的实现依赖于 MVCC 特性, 但事务要比 MVCC 本身复杂得多. 如果不希望在读取时记录被修改, 锁才是唯一可行的。

回答二:共享锁还是存在的哦,在mysql5.6 innodb,RR隔离级别中。
使用主键索引去查询一条不存在的数据,就会加上s锁了。
你可以试一下:
开启两个事务用主键查询同一条不存在数据(使用select … for update)。
两个事务都会返回一个空结果。
此时,两个事务无论是谁都没办法以这个主键insert数据。


深入分析事务的隔离级别----非MySQL的处理方法,标准SQL事务规定的处理方法,用锁处理的


Redo log, bin log, Undo log

InnoDB中通过undo log实现了数据的多版本,而并发控制通过锁来实现。

undo log除了实现MVCC外,还用于事务的回滚。MySQL Innodb中存在多种日志,除了错误日志、查询日志外,还有很多和数据持久性、一致性有关的日志。

binlog,是mysql服务层产生的日志,常用来进行数据恢复、数据库复制,常见的mysql主从架构,就是采用slave同步master的binlog实现的, 另外通过解析binlog能够实现mysql到其他数据源(如ElasticSearch)的数据复制。

redo log记录了数据操作在物理层面的修改,mysql中使用了大量缓存,缓存存在于内存中,修改操作时会直接修改内存,而不是立刻修改磁盘,当内存和磁盘的数据不一致时,称内存中的数据为脏页(dirty page)。为了保证数据的安全性,事务进行中时会不断的产生redo log,在事务提交时进行一次flush操作,保存到磁盘中, redo log是按照顺序写入的,磁盘的顺序读写的速度远大于随机读写。当数据库或主机失效重启时,会根据redo log进行数据的恢复,如果redo log中有事务提交,则进行事务提交修改数据。这样实现了事务的原子性、一致性和持久性。

undo log: 除了记录redo log外,当进行数据修改时还会记录undo log,undo log用于数据的撤回操作,它记录了修改的反向操作,比如,插入对应删除,修改对应修改为原来的数据,通过undo log可以实现事务回滚,并且可以根据undo log回溯到某个特定的版本的数据,实现MVCC。


  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值