MySQL隔离级别解析:数据一致性与高并发之间的平衡术!( 二 )


3事物的隔离级别在 MySQL 中,事务支持是在引擎层实现的 。你现在知道,MySQL 是一个支持多引擎的系统,但并不是所有的引擎都支持事务 。比如 MySQL 原生的 MyISAM 引擎就不支持事务 , 这也是 MyISAM 被 InnoDB 取代的重要原因之一
InnoDB实现了四个标准的隔离级别,每一种级别都规定了一个事务中所做的修改,哪些在事务内和事务间是可见的,哪些是不可见的 。低级别的隔离级一般支持更高的并发处理,并拥有更低的系统开销 。
查看当前数据库的事务隔离级别:
show variables like 'tx_isolation';
设置事务隔离级别:
set tx_isolation='REPEATABLE-READ';
查询mysql的长事务(大于60秒的事务): select * from information_schema.innodb_trx where TIME_TO_SEC(timediff(now(),trx_started))>60
Mysql默认的事务隔离级别是可重复读.
事务隔离级别在谈隔离级别之前,你首先要知道 , 你隔离得越严实,效率就会越低 。因此很多时候,我们都要在二者之间寻找一个平衡点 。
标准的事务隔离级别包括:

  • 读未提交:一个事务还没提交时,它做的变更就能被别的事务看到
  • 读提交:一个事务提交之后,它做的变更才会被其他事务看到
  • 可重复读:一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的 。当然在可重复读 隔离级别下,未提交变更对其他事务也是不可见的
  • 串行化:顾名思义是对于同一行记录,“写”会加“写锁” , “读”会加“读锁” 。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行
隔离级别是如何实现的呢?在实现上 , 数据库里面会创建一个视图 , 访问的时候以视图的逻辑结果为准;
在“可重复读”隔离级别下,这个视图是在事务启动时创建的,整个事务存在期间都用这个视图;
在“读提交”隔离级别下,这个视图是在每个 SQL 语句开始执行的时候创建的;
在“读未提交”隔离级别下直接返回记录上的最新值,没有视图概念;
在“串行化”隔离级别下直接用加锁的方式来避免并行访问 。
MySQL隔离级别解析:数据一致性与高并发之间的平衡术!

文章插图
图片
在 MySQL 中,不同时刻启动的事务会有不同的一致性视图read-view,并且每条记录在更新的时候都会同时记录一条回滚操作 。记录上的最新值,通过回滚操作 , 都可以得到前一个状态的值 , 这就意味着同一条数据在数据库中维护了多个版本 , 就是数据库的多版本并发控制(MVCC),我们后续详细讨论MVCC机制 。
从图中可以看到每种隔离级别可以解决的问题 , 我们可以看出可重复隔离级别下幻读是没有解决的 , 而且即便是加行锁也解决不了问题,我们上面说了幻读问题说的是新增删除造成的问题,而无论是可重复读隔离级别还是行锁操作的对象都是当前行,所以幻读问题需要其他的方式解决 。
注意:长事务会造成回滚日志不断增大,会有空间占用剧增的风险 , 尽量不要使用长事务 。
幻读的解决产生幻读的原因是,行锁只能锁住行,但是新插入记录这个动作,要更新的是记录之间的“间隙” 。因此,为了解决幻读问题 , InnoDB 只好引入新的锁,也就是间隙锁 (Gap Lock) , 间隙锁是在可重复读隔离级别下才会生效的 。所以,你如果把隔离级别设置为读提交的话,就没有间隙锁了,间隙锁是开区间 。间隙锁和行锁合称 next-key lock,每个 next-key lock 是前开后闭区间 。也就是说,我们的表 t 初始化以后,如果用 select * from t for update 要把整个表所有记录锁起来,就形成了 7 个 next-key lock,分别是 (-∞,0]、(0,5]、(5,10]、(10,15]、(15,20]、(20, 25]、(25, +supremum] 。间隙锁和 next-key lock 的引入 , 帮我们解决了幻读的问题 , 但同时也带来了一些“困扰” , 间隙锁的引入,可能会导致同样的语句锁住更大的范围,这其实是影响了并发度的 。
简单总结
解决上述问题其实就是依赖于mysql的MVCC机制和锁机制,我们后续分别讨论 。

【MySQL隔离级别解析:数据一致性与高并发之间的平衡术!】


推荐阅读