一文搞懂事务四大特性、隔离级别、传播机制
事务四大特性、隔离级别、传播机制
事务四大特性(ACID)
如果一个数据库声称支持事务的操作,那么该数据库必须要具备以下四个特性:
原子性(Atomic)
原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚,因此事务的操作如果成功就必须要完全应用到数据库,如果操作失败则不能对数据库有任何影响。
一致性(consistency)
一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。
拿转账来说,假设用户A和用户B两者的钱加起来一共是5000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是5000,这就是事务的一致性。
隔离性(Isolation)
隔离性是当多个用户并发访问数据库时,一个事务不会被另一个事务影响,最理想的就是等待一个事务执行完成后再执行另一个事务,但处于性能上的考虑,一般都需要事务并发执行,就要求事务执行过程中不受到并行执行的事务的影响,例如不能读取到另一个未提交(提交就是指事务执行完成)事务写入的值。
即要达到这么一种效果:对于任意两个并发的事务T1和T2,在事务T1看来,T2要么在T1开始之前就已经结束,要么在T1结束之后才开始,这样每个事务都感觉不到有其他事务在并发地执行。
持久性(Durability)
持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。
例如:我们在使用JDBC操作数据库时,在提交事务方法后,提示用户事务操作完成,当我们程序执行完成直到看到提示后,就可以认定事务以及正确提交,即使这时候数据库出现了问题,也必须要将我们的事务完全执行完成,否则就会造成我们看到提示事务处理完毕,但是数据库因为故障而没有执行事务的重大错误。
事务并发产生问题(脏读,幻读,不可重复读)
脏读
脏读,指的是读到了其他事务未提交的数据。
事务A读取到了事务B未提交的数据,假设事务B回滚,则事务A读取到数据实际是不存在的。
幻读
假设这里的隔离级别是可重复读(RR),因为RU、RC会存在脏读,不可重复读的问题,为了先排除其他干扰,故以RR情况研究。
业务逻辑:查询是否存在id为1的数据,没有则插入。
1、事务B第一次查询id=1,发现id为1的数据不存在。
2、此时事务A插入id为1的数据并提交事务。
3、事务B,再去查询 id为1 的数据,发现还是没有记录(RR隔离级别所以读到依然没有数据)
4、于是事务B插入一条 id=1 的数据。
5、事务B提交事务,发现报错。查的时候明明没有这条记录,但插入的时候 却告诉我 主键冲突,这就好像幻觉一样。
不可重复读
不可重复读,指的是在一个事务内,最开始读到的数据和事务结束前的任意时刻读到的同一批数据出现不一致的情况。
事务B在一次事务中相同查询条件多次查询数据结果不一致。
事务隔离级别
读未提交(Read uncommitted)
在读取数据时不会加任何锁,也不会进行检测,就是一个事务可以读取另一个未提交事务的数据。
读已提交(Read committed)
只读取提交的数据等待其他事物释放排他锁,读数据的共享锁在读操作完成后会立即释放。这个隔离级别是sqlserver默认的隔离级别。
可重复读(Repeatable read)
重复读,就是在开始读取数据(事务开启)时,不再允许修改操作(解决了不可重复读的问题),但是解决不了新增数据的问题(幻读)。
像已提交读一样,但共享锁会保持到事物结束才会释放。
序列化(Serializable)
类似于可重复读,但锁不仅会锁定所查询的数据,也会锁定所查询的范围,这样就阻止了新数据插入所查询的范围。
隔离级别 | 脏读 | 幻读 | 不可重复读 | 丢失更新 | 并发模型 | 更新冲突检测 |
---|---|---|---|---|---|---|
读未提交 Read uncommitted | 是 | 是 | 是 | 是 | 悲观 | 否 |
读已提交 Read committed | 否 | 是 | 是 | 是 | 悲观 | 否 |
可重复读 Repeatable read | 否 | 是 | 否 | 否 | 悲观 | 否 |
串行化 Serializable | 否 | 否 | 否 | 否 | 悲观 | 否 |
sqlserver,oracle 默认事务隔离级别是读已提交,mysql 默认事务隔离级别是 可重复读
事务传播机制
required
如果当前存在事务,就加入该事务。如果当前没有事务,就创建一个新事务。(默认设置)。
只创建一个事务。
requires_new
不管是否存在事务,都创建一个新的、自己的事务。老事务 先挂起,再创建 新事务, 新事务 执行完并提交,接着,继续执行 老事务,最后提交。
先执行的方法后提交事务,后执行的方法先提交事务。老事务 的回滚,不会影响 新事务 的提交。
nested
如果当前存在事务,则 嵌套事务与外面事务使用同一事务,不同的是 嵌套内部开启一个新的保存点( savepoint
),执行有两种情况:
(1)当嵌套内部 发生回滚,不影响外面事务的正常提交。
(2)当外面事务 发生回滚,则嵌套内的事务要回滚。
如果当前没有事务,则执行与 required
类似的操作。
supports
支持当前事务。如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。
supports 不会创建事务。
not_supported
不支持事务。如果当前存在事务,就把当前事务 挂起。如果当前没有事务,就以非事务执行。
mandatory
强制、必须使用事务。如果当前已经存在事务,就加入该事务,如果当前不存在事务,就 抛出异常。
mandatory 不会创建事务。mandatory 执行的前提是已经存在事务。
never
禁止事务 。如果当前存在事务,则 抛出异常,如果当前没有事务,以非事务方式执行。
必须在一个没有事务中执行,否则报错。