一、事务的 ACID 与隔离级别

事务的 ACID 老生常谈:

  • Atomicity 原子性:要么全成功,要么全回滚
  • Consistency 一致性:数据从一个合法状态变到另一个合法状态
  • Isolation 隔离性:并发事务互相不干扰
  • Durability 持久性:提交后不丢

ACID 里最复杂的是 I(隔离性),因为完全隔离(串行执行)性能差,所以 SQL 标准定义了 4 个隔离级别:

在这里插入图片描述

MySQL 默认是 RR,PostgreSQL 和 Oracle 默认是 RC。如果你从 PG 转 MySQL,要注意这个差异。

查看 / 修改:

SELECT @@transaction_isolation;          -- 看会话级别
SELECT @@global.transaction_isolation;   -- 看全局级别

SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;

二、MVCC完整实现流程

1. 写操作流程

在这里插入图片描述

2. 读操作流程

在这里插入图片描述

三、商品下单案例MVCC的使用

1.项目场景

  • 用户下单时需要检查库存
  • 多个用户同时下单
  • 需要保证库存准确性

2.数据表的设计

CREATE TABLE product (
    id INT PRIMARY KEY,
    name VARCHAR(50),
    stock INT,
    -- 隐藏字段(InnoDB自动添加)
    DB_TRX_ID BIGINT,
    DB_ROLL_PTR VARCHAR(20)
) ENGINE=InnoDB;

3.代码实现

1. Service层 - 下单逻辑

@Service
public class OrderService {
    
    @Autowired
    private ProductRepository productRepository;
    
    @Autowired
    private OrderRepository orderRepository;
    
    @Transactional
    public Order createOrder(Long productId, int quantity) {
        // 1. 查询商品(快照读 - MVCC)
        Product product = productRepository.findById(productId)
            .orElseThrow(() -> new RuntimeException("商品不存在"));
        
        // 2. 检查库存(当前读 - 加锁)
        if (product.getStock() < quantity) {
            throw new RuntimeException("库存不足");
        }
        
        // 3. 扣减库存(当前读 - 加锁)
        product.setStock(product.getStock() - quantity);
        productRepository.save(product);
        
        // 4. 创建订单
        Order order = new Order();
        order.setProductId(productId);
        order.setQuantity(quantity);
        order.setStatus(OrderStatus.CREATED);
        
        return orderRepository.save(order);
    }
}

2. Repository层 - 数据访问

@Repository
public class ProductRepository {
    
    @Autowired
    private JdbcTemplate jdbcTemplate;
    
    // 普通查询 - 使用MVCC(快照读)
    public Product findById(Long id) {
        String sql = "SELECT * FROM product WHERE id = ?";
        return jdbcTemplate.queryForObject(sql, new Object[]{id}, 
            (rs, rowNum) -> {
                Product product = new Product();
                product.setId(rs.getLong("id"));
                product.setName(rs.getString("name"));
                product.setStock(rs.getInt("stock"));
                return product;
            });
    }
    
    // 更新操作 - 当前读(加锁)
    public void updateStock(Long id, int newStock) {
        String sql = "UPDATE product SET stock = ? WHERE id = ?";
        jdbcTemplate.update(sql, newStock, id);
    }
}

3. 事务配置

@Configuration
@EnableTransactionManagement
public class TransactionConfig {
    
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}

四、MVCC在项目中的体现

场景1:库存查询(快照读)

// 用户A查询库存 - 读取历史版本
Product productA = productRepository.findById(1L);
// 返回 stock = 100

// 用户B同时查询库存 - 也读取历史版本
Product productB = productRepository.findById(1L); 
// 返回 stock = 100(同一事务内的快照)

场景2:库存更新(当前读)

// 用户A下单 - 当前读,加X锁
@Transactional
public void orderA() {
    Product product = productRepository.findById(1L); // 读取最新版本
    product.setStock(99);
    productRepository.save(product); // 加锁更新
}

// 用户B下单 - 等待锁释放
@Transactional
public void orderB() {
    // 等待用户A的事务完成
    Product product = productRepository.findById(1L);
    product.setStock(98);
    productRepository.save(product);
}

最终测试:并发测试代码

@SpringBootTest
public class ConcurrencyTest {
    
    @Autowired
    private OrderService orderService;
    
    @Test
    public void testConcurrency() throws InterruptedException {
        // 模拟100个用户同时下单
        ExecutorService executor = Executors.newFixedThreadPool(100);
        CountDownLatch latch = new CountDownLatch(100);
        
        for (int i = 0; i < 100; i++) {
            executor.submit(() -> {
                try {
                    orderService.createOrder(1L, 1);
                } catch (Exception e) {
                    System.out.println("下单失败: " + e.getMessage());
                } finally {
                    latch.countDown();
                }
            });
        }
        
        latch.await();
        executor.shutdown();
        
        // 验证最终库存
        Product product = productRepository.findById(1L);
        assertEquals(0, product.getStock());
    }
}
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐