事务隔离级别实战
事务隔离级别
在数据库系统中,事务隔离级别用于定义事务之间的隔离程度,主要是为了解决并发事务访问数据时可能出现的问题,如脏读、不可重复读和幻读。常见的事务隔离级别有以下四种,不同的数据库对这些隔离级别的支持和实现细节可能会有所不同。
读未提交(Read Uncommitted)
- 特点:这是最低的隔离级别,允许一个事务读取另一个事务尚未提交的数据。
- 问题:会导致脏读问题,即一个事务读取到了另一个事务未提交的数据,如果该事务回滚,那么读取到的数据就是无效的。
- 示例:假设事务 A 更新了某条记录,但还未提交,此时事务 B 读取了这条被更新的数据。如果事务 A 后来回滚了,那么事务 B 读取到的数据就是无效的。
- 适用场景:很少使用,因为脏读会带来很多问题,但在对数据一致性要求不高的场景下,如某些统计分析系统,可能会使用这种隔离级别来提高性能。
读已提交(Read Committed)
- 特点:一个事务只能读取另一个事务已经提交的数据。
- 问题:解决了脏读问题,但可能会出现不可重复读问题。即一个事务在多次读取同一数据时,由于其他事务对该数据进行了更新并提交,导致每次读取的结果不一致。
- 示例:事务 A 在第一次读取某条记录时,得到了一个值。然后事务 B 更新了这条记录并提交。当事务 A 再次读取这条记录时,得到了不同的值。
- 适用场景:大多数数据库系统默认的隔离级别,适用于大多数对数据一致性有一定要求的场景,如银行系统的转账操作。
可重复读(Repeatable Read)
- 特点:保证在一个事务中多次读取同一数据时,其结果是一致的。在事务执行期间,其他事务不能对该事务正在读取的数据进行更新操作。
- 问题:解决了不可重复读问题,但可能会出现幻读问题。幻读是指一个事务在执行查询操作时,由于其他事务插入或删除了符合查询条件的记录,导致该事务在后续的查询中发现结果集发生了变化。
- 示例:事务 A 在第一次查询时,得到了符合某个条件的一组记录。然后事务 B 插入了一条符合该条件的记录并提交。当事务 A 再次执行相同的查询时,发现结果集中多了一条记录。
- 适用场景:适用于对数据一致性要求较高的场景,如财务系统的报表生成。MySQL 的 InnoDB 存储引擎默认使用可重复读隔离级别。
串行化(Serializable)
- 特点:这是最高的隔离级别,它强制事务串行执行,即一个事务执行完后,另一个事务才能开始执行。
- 问题:避免了脏读、不可重复读和幻读问题,但会导致并发性能大幅下降,因为事务之间不能并发执行,只能依次执行。
- 示例:如果有多个事务需要访问同一数据,它们会按照顺序依次执行,不会出现并发冲突。
- 适用场景:适用于对数据一致性要求极高,且对并发性能要求不高的场景,如某些金融交易系统。
查看mysql8默认事务隔离级别
mysql默认事务隔离级别是可重复读
SHOW VARIABLES LIKE 'transaction_isolation';
场景实战
避免一级缓存的干扰,由于mybatis存在一级缓存,所以使用以下方式的时候每次方法调用返回的对象是同一个
userService.getBaseMapper().selectById(50)private User getById(Object id) {LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();wrapper.eq(User::getId, id);return userService.getOne(wrapper);
}
<select id="getByUserId" resultType="cn.hollycloud.server.entity.User" parameterType="long">select * from user where id = #{id}
</select>
我们可以通过给xml加flushCache="true"的方式避免一级缓存
<select id="getByUserId" resultType="cn.hollycloud.server.entity.User" parameterType="long" flushCache="true">select * from user where id = #{id}
</select>
测试1,开启断点到testTransaction方法第一行,这时事务已经开始,我们再把数据库user id是1的积分从10改成20,再去读用户,这时读到的积分是多少?是20
测试2,先读一次user,打上断点,这时改下用户积分,从10改到20,再读一次用户,这时数据库的用户积分是20,但是我获取到的用户积分还是10,这就是可重复读,前后两次读到的数据是一致的
测试3,设置事务的隔离级别为Isolation.READ_COMMITTED,重复以上试验,会发现第一次返回10,第二次返回20