全部学科
NodeJS全栈
nodejs
Python全栈
python
小程序首页
📅 2026-05-18 10 分钟 ✍️ juanwangdev

事务隔离级别

事务隔离级别控制并发事务之间的可见性,解决数据一致性问题。

并发问题

问题说明示例
脏读读取到未提交的数据A修改后未提交,B读到修改值,A回滚,B读到脏数据
不可重复读同事务内两次读取结果不同A读取数据,B修改并提交,A再次读取结果不同
幻读同事务内两次查询记录数不同A查询age>20,B插入age=25的记录,A再次查询多出记录

五种隔离级别

隔离级别脏读不可重复读幻读说明
DEFAULT---使用数据库默认隔离级别
READ_UNCOMMITTED读未提交,最低隔离
READ_COMMITTED读已提交
REPEATABLE_READ可重复读
SERIALIZABLE串行化,最高隔离

DEFAULT - 使用数据库默认

Java
@Transactional(isolation = Isolation.DEFAULT)
public void saveOrder(Order order) {
    // MySQL默认: REPEATABLE_READ
    // PostgreSQL默认: READ_COMMITTED
    // Oracle默认: READ_COMMITTED
}

READ_UNCOMMITTED - 读未提交

Java
// 允许读取未提交数据,存在脏读风险
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public List<Account> findAll() {
    return accountDao.findAll();
}

实际开发几乎不使用此隔离级别。

READ_COMMITTED - 读已提交

Java
// 只读取已提交数据,防止脏读
@Transactional(isolation = Isolation.READ_COMMITTED)
public Account getAccount(Long id) {
    Account account = accountDao.findById(id);
    // 此时其他事务已提交的修改可见
    return account;
}

PostgreSQL、Oracle、SQL Server默认级别。

REPEATABLE_READ - 可重复读

Java
// 保证同事务内读取一致,防止脏读和不可重复读
@Transactional(isolation = Isolation.REPEATABLE_READ)
public void processAccount(Long id) {
    Account a1 = accountDao.findById(id);  // 余额100
    // 其他事务修改并提交,余额变为80
    Account a2 = accountDao.findById(id);   // 仍然读取100
    // MySQL通过MVCC实现
}

MySQL默认隔离级别,通过MVCC防止不可重复读,通过间隙锁防止幻读。

SERIALIZABLE - 串行化

Java
// 最高隔离级别,完全串行执行
@Transactional(isolation = Isolation.SERIALIZABLE)
public void transfer(Long fromId, Long toId, BigDecimal amount) {
    Account from = accountDao.findById(fromId);
    Account to = accountDao.findById(toId);
    // 执行期间完全阻塞其他事务
    accountDao.transfer(from, to, amount);
}

性能最差,仅在极端一致性要求场景使用。

Spring配置方式

注解配置

Java
@Service
public class OrderService {

    @Transactional(isolation = Isolation.READ_COMMITTED)
    public void createOrder(Order order) { }

    @Transactional(isolation = Isolation.REPEATABLE_READ)
    public Order getOrder(Long id) { return null; }
}

XML配置

XML
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="get*" isolation="READ_COMMITTED" read-only="true"/>
        <tx:method name="save*" isolation="REPEATABLE_READ"/>
        <tx:method name="*" isolation="DEFAULT"/>
    </tx:attributes>
</tx:advice>

全局配置

YAML
spring:
  datasource:
    hikari:
      transaction-isolation: TRANSACTION_READ_COMMITTED

脏读问题演示

Java
// 隔离级别: READ_UNCOMMITTED
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public void demo() {
    // 线程A: 修改数据未提交
    account.setBalance(200);
    accountDao.update(account);

    // 线程B: 可能读到未提交的200
    Account a = accountDao.findById(1L);  // 余额200(脏读风险)

    // 线程A: 回滚
    // 线程B读取的200变成无效数据
}

不可重复读问题演示

Java
// 隔离级别: READ_COMMITTED
@Transactional(isolation = Isolation.READ_COMMITTED)
public void demo() {
    // 第一次读取
    Account a1 = accountDao.findById(1L);  // 余额100

    // 其他事务修改并提交,余额变为80

    // 第二次读取(结果不同)
    Account a2 = accountDao.findById(1L);  // 余额80(不可重复读)
}

幻读问题演示

Java
// 隔离级别: REPEATABLE_READ
@Transactional(isolation = Isolation.REPEATABLE_READ)
public void demo() {
    // 第一次查询
    List<Account> list1 = accountDao.findByAgeGreaterThan(20);  // 5条记录

    // 其他事务插入age=25的记录并提交

    // 第二次查询(MySQL防止幻读)
    List<Account> list2 = accountDao.findByAgeGreaterThan(20);  // 仍然5条

    // 但如果是INSERT操作可能受影响
    accountDao.insert(new Account("test", 30));  // 可能主键冲突
}

隔离级别选择

业务场景推荐隔离级别原因
报表统计READ_COMMITTED平衡性能和一致性
金融交易REPEATABLE_READ防止余额读取不一致
高并发读取READ_COMMITTED减少锁等待
极端一致性SERIALIZABLE完全隔离

要点总结

  • DEFAULT使用数据库默认设置
  • READ_UNCOMMITTED存在脏读风险,几乎不用
  • READ_COMMITTED防止脏读,主流数据库默认
  • REPEATABLE_READ防止脏读和不可重复读,MySQL默认
  • SERIALIZABLE完全隔离,性能最差
  • 隔离级别越高,并发性能越低

📝 发现内容有误?点击此处直接编辑

← 上一篇 事务超时与回滚规则
下一篇 → 声明式事务与编程式事务
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

长按或扫描二维码,立即体验

扫码体验小程序
马上就来
使用微信扫描二维码
立即体验完整题库