事务
# 1 什么是事务?
事务逻辑上的一组操作,组成这组操作的各个逻辑单元,要么一起成功,要么一起失败。
# 2 事务有哪些特性?
事务的特性(ACID)
原子性(atomicity): 事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用;
假如你选择了将A-100以及B+100整体作为一个事务,那么他们就是一个整体,要么大家都成功,要么大家都失败。
一致性(consistency): 执行事务前后,数据保持一致,多个事务对同一个数据读取的结果是相同的;
A给B转钱,单从数据库的角度来看,从A上减掉100块钱,然后就提交,本身这个操作没有问题。但是从业务的角度来看,你必须得给B上加上100块钱,这样整体数据才算一致。
隔离性(isolation): 并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的;
持久性(durability): 事务一旦结束,数据就持久到数据库。
# 3 innodb怎么实现事务
原子性用回滚来保证,通过
undo log
记录了一个隐藏字段DATA_ROLL_PTR
指向undo log
中旧版本的数据来进行数据回滚,如果事务失败则rollback。一致性其实是通过另外三个特性来满足的。
隔离性通过设置四个隔离级别按照业务场景选择自己需要的隔离级别。
读未提交
其实就是没有做隔离。
串行化
性能最差,并发性最低,安全性最高,其实就是读的时候加共享锁(读锁),也就是其他事务可以并发读,但是不能写。写的时候加排它锁(写锁),其他事务不能并发写也不能并发读。
读已提交
和可重复读
采用MVCC 多版本并发控制
方式实现。
- 持久性通过
redo log
,在commit前先写入redo log
,确保commit成功,这时候就算断电也能在重启后完成数据的持久化。
# 4 并发事务带来的问题
# 4.1 脏读(Dirty read)
脏读发生在一个事务读取了被另一个事务改写但尚未提交的数据时。如果这些改变在稍后被回滚了,那么第一个事务读取的数据就会是无效的。
# 4.2 不可重复读(Nonrepeatable read)
不可重复读发生在一个事务执行相同的查询两次或两次以上,但每次查询结果都不相同时。这通常是由于另一个并发事务在两次查询之间更新了数据。
# 4.3 幻影读(Phantom reads)
幻影读和不可重复读相似。当一个事务(T1)读取几行记录后,另一个并发事务(T2)插入了一些记录时,幻影读就发生了。在后来的查询中,第一个事务(T1)就会发现一些原来没有的额外记录。
# 4.4 不可重复度和幻读区别
不可重复读的重点是修改,幻读的重点在于新增或者删除。
# 5 事务隔离级别
# 5.1 READ-UNCOMMITTED(读取未提交)
最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
# 5.2 READ-COMMITTED(读取已提交)
允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
# 5.3 REPEATABLE-READ(可重复读)
对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
# 5.4 SERIALIZABLE(串行化)
最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。
# 5.5 数据库默认隔离级别
MySQL InnoDB 存储引擎的默认支持的隔离级别是:REPEATABLE-READ(可重读)
Oracle 默认:READ-COMMITTED(读已提交)
# 6 Spring 事务中的隔离级别
TransactionDefinition 接口中定义了五个表示隔离级别的常量:
TransactionDefinition.ISOLATION_DEFAULT
TransactionDefinition.ISOLATION_READ_UNCOMMITTED
TransactionDefinition.ISOLATION_READ_COMMITTED
TransactionDefinition.ISOLATION_REPEATABLE_READ
TransactionDefinition.ISOLATION_SERIALIZABLE
# 6.1 ISOLATION_DEFAULT
使用后端数据库默认的隔离级别。
Mysql 默认采用的 REPEATABLE_READ隔离级别。Oracle 默认采用的 READ_COMMITTED隔离级别。
# 6.2 ISOLATION_READ_UNCOMMITTED
最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。
# 6.3 ISOLATION_READ_COMMITTED
允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。
# 6.4 ISOLATION_REPEATABLE_READ
对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
# 6.5 ISOLATION_SERIALIZABLE
最高的隔离级别,完全服从ACID的隔离级别。
所有事务依次逐个执行,事务之间不产生干扰,也就是说该级别可以防止脏读、不可重复读以及幻读。
但是这将严重影响程序的性能。通常情况下也不会用到该级别。
# 7 Spring事务的7种传播行为
# 7.1 保证同一个事务中
# 1 PROPAGATION_REQUIRED
如果存在一个事务则支持当前事务,如果不存在就新建一个(默认)
# 2 PROPAGATION_SUPPORTS
如果存在一个事务则支持当前事务,如果不存在,就不使用事务
# 3 PROPAGATION_MANDATORY
如果存在一个事务则支持当前事务,如果不存在,抛出异常
# 7.1 保证没有在同一个事务中
# PROPAGATION_REQUIRES_NEW
如果有事务存在,挂起当前事务,创建一个新的事务
# 5.PROPAGATION_NOT_SUPPORTED
以非事务方式运行,如果有事务存在,挂起当前事务
# 6.PROPAGATION_NEVER
以非事务方式运行,如果有事务存在,抛出异常
# 7.PROPAGATION_NESTED
如果当前事务存在,则嵌套事务执行
# 7.2 传播级别不生效原因?
必须用注入的实例(代理类实例,有代理事务逻辑处理),否则只是普通类实例会导致事务代码不生效。
可以类自己注入自己实例。