假设现在有A和B 两个事务 并发执行。
1)脏读:一个事务读取到另一事务未提交的更新新据
- A 将某条记录的 age 值 从 20修改为30
- B 读取了 A 更新后的值为 30
- A 回滚,age值回到20
- B 读取到的30 的值就是一个无效的值
2)不可重复读: 同一事务中,多次读取同一数据返回的结果有所不同(针对的update操作)
- A 读取了 age 值 为 20
- B 将 age 值修改为30
- A 再次读去age 的值为30
3)幻读:一个事务读取到另一事务已提交的insert数据(针对的insert操作)
- A 读取 学生表 中一部分数据
- B 向学生表中插入了 新的数据
- A 读取 学生表示 多出了一些行
数据库事务的隔离性: 数据库系统必须具有隔离并发运行各个事务的能力, 使它们不会相互影响, 避免各种并发问题.
一个事务与其他事务隔离的程度称为隔离级别. 数据库规定了多种事务隔离级别, 不同隔离级别对应不同的干扰程度, 隔离级别越高, 数据一致性就越好, 但并发性越弱
在代码中,我们可以通过
数据库提供了4种隔离级别:
脏读 | 不可重复读 | 幻读 | |
Read uncommitted (都未提交) | 有 | 有 | 有 |
Read committed(读已提交) | 无 | 有 | 有 |
Repeatable read (重复读) | 无 | 无 | 有 |
Serializable(序列化) | 无 | 无 | 无 |
- oracle 默认的事务隔离级别为: READ COMMITED ,Oracle 支持的 2 种事务隔离级别:READ COMMITED, SERIALIZABLE.
- Mysql 默认的事务隔离级别为: REPEATABLE READ,Mysql 支持 4 种事务隔离级别.
注 : 模拟并发情况。
1) 测试一下 mysql 的默认隔离级别:
public interface BookShopService {
void purchase(int userId,int isbn);
// 测试事务隔离级别
void transactionIsolationTest(int isbn);
}
复制代码
/**
* propagation :用来设置事务传播属性
* Propagation.REQUIRED 默认事务传播属性
*
* isolation :用来设置事务隔离级别
* Isolation.REPEATABLE_READ: mysql 默认事务隔离
* Isolation.READ_COMMITTED: oracle 默认事务隔离级别
*/
@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.REPEATABLE_READ)
@Override
public void transactionIsolationTest( int isbn) {
// 获取要买的图书
double bookPrice = bookShopMapper.getBookPriceByIsbn(isbn);
//此处应打上断点,待代码执行完上一句后,应手动将书的价格修改一下,看读到的数据是多少
System.out.println(bookPrice);
double bookPrice2 = bookShopMapper.getBookPriceByIsbn(isbn);
System.out.println(bookPrice2);
}
复制代码
测试代码特别简单,但因为我是手动模拟,得打断点、debug启动,
@Autowired
BookShopService bookShopService;
@Test
void transactionIsolationTest(){
bookShopService.transactionIsolationTest(1001);
}
复制代码
当执行完第一个 double bookPrice = bookShopMapper.getBookPriceByIsbn(isbn) 语句时,应该去mysql 修改一下书的价格,这样看一下结果。
这个时候再接着执行。看输出什么。
最后的结果仍然是50、50。因为mysql的默认事务隔级别是可重复读,意思在这同一个事务中,可以重复读。
注 :因为这是直接修改数据库,其操作行为并不可取,此处只是为了模拟。其结果有时也非一定准确。
原文链接:https://juejin.cn/post/6993524722503794725