小窥数据库事务(下)
2026/4/6 6:48:04 网站建设 项目流程
小窥数据库事务下前言3.2 事务的隔离级别3.2.1 读未提交Read uncommitted隔离级别3.2.2 读提交Read committed隔离级别3.2.3 可重复读Repeatable read隔离级别3.2.4 可串行化Serializable隔离级别四、总结参照前言上篇说到并发读写造成的一些影响这儿接着上篇没说完的咱继续讨论 下面该说说对应具体的解决方案了。3.2 事务的隔离级别不管不顾完全并发的事务读写可能会产生一些预料不到的问题该如何解决呢 说起来也很简单让事务们操作的时候“悠着点”别一股脑的卡卡卡往前冲多关注关注有没有和自己冲突的其他事务们。下面的隔离级别主要以Mysql InnoDB的为例。3.2.1 读未提交Read uncommitted隔离级别最弱的隔离级别大概要数这个级别了。这个级别的隔离在实现上主要是用写锁X 锁、排它锁来进行事务间的隔离操作所谓隔离在具体的体现上简单理解就是两个相关联的事务在冲突的时候要分开执行它本质上是在事务修改数据之前加上X锁,直到事务结束后才释放,事务结束包括正常结束(commit)与非正常结束(rollback).使用这个隔离级别所造成的的结果是可以防止脏写、更新丢失等几个写写冲突但是无法防止脏读、不可重复读和幻读等几个读写冲突。这里稍微提一下这个隔离级别不能解决“脏读”的问题。如下图所示事务A在写入X之前要把数据加X锁但其未提交之前事务B通过select读取了X值然后A发生了出错回归B因此读到了脏数据。这里面要强调的重点是 为什么事务A已经加了X锁直到事务结束之后才会释放事务A还是可以读数据呢 X锁难道不和读冲突了吗你要是想到这儿理解的相当准确。 但是需要说明的是select操作是阻塞的是不需要加锁的至少mysql InnoDB中如此详细的原因我也不是非常清楚大概是因为其使用了MVCC机制使得普通的select 都是从快照中进行读取的为了加快读写速度。详细的内容可以参照【1】【2】。所以如果脱离出数据库事务这个层次单单从数据的并发读写来说加了写锁之后是可以防止其他操作比如说其他的线程、进程的脏读的。对了顺便说一点上面的隔离级别对应的是一级封锁协议。3.2.2 读提交Read committed隔离级别简单的说这个级别对于锁的使用是在读未提交的基础上增加了对读取的加锁即在读数据时加上读锁S锁,读完后立即释放S锁。有个这个限制之后这个级别的隔离就可以防止脏读因为在读取数据之前加了S锁而S锁是和X锁发生冲突的但是有可能会出现幻读和不可重复读。出现幻读和不可重复读简单来说是因为这两种情况都需要对数据读取两次出现有问题的情况在于两次读取的数据发生了某种程度上的不一致或者不符合“预期”状态 而S锁仅仅作用在一次读取上因此不能解决这个问题。DDIA中说现在的数据库一般不用读锁来显示读提交功能因为这会严重影响系统的效率。 所以大多数现在数据库引擎都是使用类似MVCC来实现这个级别的隔离在Mysql中读已提交一般就是使用MVCC来实现的简单来说就是在事务执行的时候每次Select的时候生成一个当前系统的ReadView(可以理解为当前系统的版本快照) 每次查询的时候只能查到可见的快照版本。3.2.3 可重复读Repeatable read隔离级别注意上面说的 Read(X) 不仅仅针对于 select 只要涉及到查询当前 x 的都会有读取操作比如说 update from test_table where X 100 这种其实也是会触发当前读的。上面的隔离级别没有解决可重复读的问题对于可重复读的解决方案最常见的方式是使用快照级别的隔离。关于快照级别的隔离我从DDIA上摘了一段文字帮助理解。具体的MVCC是什么东东网上的资料很多这里就不赘述了。这里简单说下问什么使用快照级别的隔离就可以重复读了。对于Mysql来说在读取时select会根据当前事务的version 号来进行快照读取即只读取当前事务version的数据版本即使在两次读取的过程中发生了更高version的数据修改,也没关系。 借此可以解决不可重复读的问题。注意这里和读已提交的在生成版本快照处理的区别读已提交是事务中的每次Select都生成一个新的快照版本而可重复读是在事务开始的时候生成一个全局的快照版本。因为前者可能会在后续的Select中(如果事务有多个Select的话) 读到其他事务提交的版本因此是不可重复读的。虽然可以解决不可重复读的问题但是单纯使用MVCC技术却无法解决幻读的问题它需要更强的隔离级别控制即可串行化 准确的说在InnoDB引擎中采用MVCC间隙锁就可以解决幻读问题请参照【4】这里不过多的涉及太细的部分暂且略过。3.2.4 可串行化Serializable隔离级别可串行化隔离应该算是最强的隔离级别了它保证即使事务可能会并发执行但最终的结果与每次一个也就是串行执行的结果相同。对于串行化的实现DDIA上总结了三种目前使用的技术1、实际串行执行。 简单来说就是直接避免并发采用类似单线程的手段来解决并发事务问题。 比如说Redis中就采用这种串行化方式执行事务还别说效果还不错呢。2、 2PL。事务必须分为两个阶段对数据进行加锁与解锁,所有的加锁都在解锁之前进行.。就我目前了解mysql 的InnoDB引擎就是采用2PL的方式实现的。关于2PL按照我目前的理解基本上已经可以解决幻读问题了但是很多资料上显示还需要什么gap 锁 next-key locking锁等等给我这个还没有深入探究InnoDB的人搞得有些蒙蒙的。现在就不深入探究下去了否则的话感觉学无止境没完没了了以后有时间再深入了解吧。 如果有了解的小伙伴也希望可以给我解释下。3、可串行化的快照隔离SSI。最后一种看起来比较牛逼的方式就是这个可串行化的快照隔离方式。它是一种乐观并发机制相对来说2PL是一种悲观控制机制在这种情况下如果可能发生潜在的冲突事务会继续执行而不是中止 而当事物提交时只有可串行化的事务才被允许提交数据库会检查是否确实发生了冲突如果是的话中止事务并接下来重试而SSI 基于快照隔离事务中的所有读取操作都是基于数据库的一致性快照。同时在这个基础之上SSI增加了相关算法来检测写入之间的串行化冲突从而决定终止哪些事务。好了实话说上面的两段文字摘自DDIA, 关于具体的可串行化的快照隔离方式来实现数据库的可串行化目前我还没有深入研究还不是很了解暂且记到这儿等以后有机会在好好研究研究。最后放一个图片总结一下事务的隔离级别及其具体的实现方式。四、总结好了终于差不多写完了这个部分的总结。原来的设想是稍微总结一下并发读写的会产生什么影响 大概也就一两个小时的时间吧没想到一写起来发现牵扯到的内容越来越多知识越扩越大ACID、隔离级别、快照、 串行化、锁…, 一个半天过去了又一个半天过去了又一个下午过去了虽然我不断DFS下去还能学到不少内容但目前还是暂且到这儿吧。 毕竟骨头还得慢慢啃…参照【1】、为什么二级封锁协议不能保证可重复读一级封锁协议不能保证可重复读和读脏数据?【2】、MySQL查询不需要锁InnoDB的非一致性锁定读【3】、Innodb中的事务隔离级别和锁的关系【4】、深入理解MySQL中事务隔离级别的实现原理【5】、从0到1理解数据库事务上并发问题与隔离级别【6】、从0到1理解数据库事务下隔离级别实现——MVCC与锁【7】、SQL 事务隔离实用指南

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询