概要:
事务的四个特性:原子性、一致性、隔离性、持久性
事务不隔离带来的问题:更新丢失、脏读、不可重复读、虚读(幻读)。其中更新丢失就是并发写,这是一定不允许的,因此一定要解决更新丢失问题。
事务隔离的级别:读未提交(1000)、读已提交(1100)、可重复读(1110)、串行化(1111)。
第一类更新丢失 | 脏读 | 不可重复读 | 幻读 | 第二类更新丢失 | |
---|---|---|---|---|---|
RU(读未提交) | 避免 | ||||
RC(读提交) | 避免 | 避免 | |||
RR(可重复读) | 避免 | 避免 | 避免 | 避免 | |
S(串行化) | 避免 | 避免 | 避免 | 避免 | 避免 |
1、事务四个特性
如果一个数据库声称支持事务的操作,那么该数据库必须要具备以下四个特性:
(1)原子性(Atomicity)
原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚,这和前面两篇博客介绍事务的功能是一样的概念,因此事务的操作如果成功就必须要完全应用到数据库,如果操作失败则不能对数据库有任何影响。
(2)一致性(Consistency)
一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。
拿转账来说,假设用户A和用户B两者的钱加起来一共是5000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是5000,这就是事务的一致性。
(3)隔离性(Isolation)
隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。
即要达到这么一种效果:对于任意两个并发的事务T1和T2,在事务T1看来,T2要么在T1开始之前就已经结束,要么在T1结束之后才开始,这样每个事务都感觉不到有其他事务在并发地执行。
关于事务的隔离性数据库提供了多种隔离级别,稍后会介绍到。
(4)持久性(Durability)
持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。
例如我们在使用JDBC操作数据库时,在提交事务方法后,提示用户事务操作完成,当我们程序执行完成直到看到提示后,就可以认定事务以及正确提交,即使这时候数据库出现了问题,也必须要将我们的事务完全执行完成,否则就会造成我们看到提示事务处理完毕,但是数据库因为故障而没有执行事务的重大错误。
2、事务隔离
以上介绍完事务的四大特性(简称ACID),现在重点来说明下事务的隔离性,当多个线程都开启事务操作数据库中的数据时,数据库系统要能进行隔离操作,以保证各个线程获取数据的准确性。
2.1、不事务隔离带来的问题
在介绍数据库提供的各种隔离级别之前,我们先看看如果不考虑事务的隔离性,会发生的几种问题:
更新丢失:
第一类(回滚覆盖):撤消一个事务时,该事务内的写操作要回滚,把其它已提交的事务写入的数据覆盖了
第二类(提交覆盖):它和不可重复读本质上是同一类并发问题,通常将它看成不可重复读的特例。两个或多个事务查询相同的记录,然后各自基于查询的结果更新记录时会造成此问题,每个事务不知道其它事务的存在,最后一个事务对记录所做的更改将覆盖其它事务对该记录所做的更改
脏读:
A事务读取B事务尚未提交的数据,此时如果B事务发生错误并执行回滚操作,那么A事务读取到的数据就是脏数据。
不可重复读:
事务A执行读操作,前后两次读取同一条数据的间隔中,事务B执行更改操作,将事务A读的那条数据修改了,此时事务A第二次读取到的数据和第一次读的数据不一样了,系统重复读取多次数据不一样,成为不可重复读。
虚读(幻读):
幻读类似于不可重复读,幻读指的是事务A在事务中用相同条件执行两次查询,在两次查询的间隔期间,事务B进行了插入或删除操作,导致A第二次查询的记录条数和第一次不一样。
脏读和不可重复读的区别:脏读是某一事务读取了另一个事务未提交的脏数据,而不可重复读则是读取了前一事务提交的数据。
不可重复读和幻读的异同:都是读取了另一条已经提交的事务(这点就脏读不同),所不同的是不可重复读查询的都是同一个数据项,而幻读针对的是一批数据整体(比如数据的个数)。
2.2、事务隔离的级别
为此我们需要通过提供不同类型的“锁”机制针对数据库事务进行不同程度的并发访问控制,由此产生了不同的事务隔离级别:隔离级别(低->高)。SQL、SQL2标准定义了四种隔离级别:
读未提交(Read Uncommitted)
含义解释:只限制同一数据写事务禁止其他写事务。能够避免”第一类更新丢失”。(一事务写时禁止其他事务写)
名称解释:可读取未提交数据
所需的锁:写共享锁
补充:
下面是一个第一类更新丢失的例子
读未提交隔离级别能避免第一类丢失更新的原因是:
t7时刻undo log记录的是已经提交的1100,所以回滚也不会更新丢失,本质靠的是记录排他锁跟undo log实现的
读提交(Read Committed)
含义解释:只限制同一数据写事务禁止其它读写事务。解决”脏读”,以及”更新丢失”。(一事务写时禁止其他事务读写)
名称解释:必须提交以后的数据才能被读取
所需的锁:写排他锁
可重复读(Repeatable Read)
含义解释:限制同一数据写事务禁止其他读写事务,读事务禁止其它写事务(允许读)。解决”不可重复读”,以及”第二类更新丢失”和”脏读”。(一事务写时禁止其他事务读写、一事务读时禁止其他事务写)
注意没有解决幻读,解决幻读的方法是增加范围锁(range lock)或者表锁。
名称解释:能够重复读取
所需的锁:写排他锁、读共享锁
串行化(Serializable)
含义解释:限制所有读写事务都必须串行化实行。它要求事务序列化执行,事务只能一个接着一个地执行,但不能并发执行。如果仅仅通过“行级锁”是无法实现事务序列化的,必须通过其他机制保证新插入的数据不会被刚执行查询操作的事务访问到。(一事务写时禁止其他事务读写、一事务读时禁止其他事务读写)
所须的锁:范围锁或表锁
下表是各隔离级别对各种异常的控制能力。
第一类更新丢失 | 脏读 | 不可重复读 | 幻读 | 第二类更新丢失 | |
---|---|---|---|---|---|
RU(读未提交) | 避免 | ||||
RC(读提交) | 避免 | 避免 | |||
RR(可重复读) | 避免 | 避免 | 避免 | 避免 | |
S(串行化) | 避免 | 避免 | 避免 | 避免 | 避免 |
3、常见数据库的事务隔离
数据库 | 默认级 |
---|---|
MySQL | 可重复读(Repeatable Read) |
Oracle | 读提交(Read Committed) |
SQLServer | 读提交(Read Committed) |
DB2 | 读提交(Read Committed) |
PostgreSQL | 读提交(Read Committed) |
参考资料:
1.https://blog.csdn.net/qq_41378597/article/details/88377509
2.为什么说只要数据库支持事务,第一类丢失更新就不会发生?
3.共享锁和排它锁
4.https://www.cnblogs.com/yanghanwen/p/12181078.html