- 不可重复读取问题复现:
设置事务隔离级别为读取已提交
事务A | 事务B |
---|---|
开启事务,设置事务隔离级别为读取已提交 | |
查到5条记录 | |
开启事务,插入一条记录id=6 | |
事务提交 | |
继续查询,查到6条记录 |
这就出现了一个问题,同一个事务中两次读取的事务条数不一致
更改事务隔离级别解决这个问题
- 设置事务隔离级别为可重复读取
事务A | 事务B |
---|---|
开启事务,设置事务隔离级别为可以重复读取 | |
查到5条记录 | |
开启事务,插入一条记录id=6 | |
事务提交 | |
继续查询,依然查到5条记录 | |
插入一条记录id=6 | |
事务提交 | |
报错id唯一性校验没有通过 |
-
可重复读取的问题解决了但是又出现新的问题:幻读
-
mysql的事务隔离级别就是可以重复读取,为了性能做取舍,因为解决幻读需要牺牲数据库性能
-
解决幻读问题:mysql事务隔离级别——序列化
-
代码演示:
@Test void test() throws InterruptedException { // try (SqlSession session = sqlSessionFactory.openSession(TransactionIsolationLevel.READ_COMMITTED)) { try (SqlSession session = sqlSessionFactory.openSession(TransactionIsolationLevel.REPEATABLE_READ)) { // 开启事务 List<User> list = session.selectList("getAllUsers"); assertEquals(5, list.size()); // 等待子线程插入数据 startThread(); // 等待子线程插入数据 Thread.sleep(1000); // 再次读取依然是刚才的数据,说明数据可以重复读取 List<User> list2 = session.selectList("getAllUsers"); assertEquals(5, list2.size()); /** * * 可重复读的问题解决了,但是又出现了幻读问题 * * sqlSessionFactory.openSession(TransactionIsolationLevel.REPEATABLE_READ) * 出现幻读问题 * 子线程插入数据成功 * 主线程事务回滚 */ try { insertUser(session, "user66666"); } catch (Exception e) { e.printStackTrace(); } } try (SqlSession session = sqlSessionFactory.openSession(TransactionIsolationLevel.REPEATABLE_READ)) { User user = session.selectOne("getUsersById", 6); assertEquals("user6", user.getName()); } }
-
通过代码调试可以对事务隔离级别有深刻的理解:
gituhub代码下载