数据库并发事务问题全解析

在数据库系统中,当多个事务同时访问相同数据时,可能引发并发事务问题。以下是主要的并发问题及其原理:


1. 脏读(Dirty Read)

  • 问题:事务A读取了事务B未提交的数据,事务B随后回滚
  • 后果:事务A读到了无效的"脏数据"
  • 示例
    -- 事务B(未提交)
    UPDATE users SET balance = 500 WHERE id = 1;  -- 未提交
    
    -- 事务A(读取未提交数据)
    SELECT balance FROM users WHERE id = 1;  -- 读到500(脏数据)
    
    -- 事务B回滚
    ROLLBACK;  -- balance实际恢复为1000
    

2. 不可重复读(Non-repeatable Read)

  • 问题:事务A多次读取同一数据,期间事务B修改并提交了该数据
  • 后果:事务A两次读取结果不一致
  • 示例
    -- 事务A第一次读取
    SELECT points FROM users WHERE id = 1;  -- 返回100
    
    -- 事务B更新并提交
    UPDATE users SET points = 200 WHERE id = 1;
    COMMIT;
    
    -- 事务A第二次读取
    SELECT points FROM users WHERE id = 1;  -- 返回200(结果改变)
    

3. 幻读(Phantom Read)

  • 问题:事务A按条件查询数据,期间事务B新增/删除了符合条件的数据并提交
  • 后果:事务A两次查询的结果集数量/内容不一致
  • 示例
    -- 事务A第一次查询
    SELECT COUNT(*) FROM orders WHERE user_id = 1;  -- 返回3条
    
    -- 事务B插入新订单并提交
    INSERT INTO orders(user_id) VALUES (1);
    COMMIT;
    
    -- 事务A第二次查询
    SELECT COUNT(*) FROM orders WHERE user_id = 1;  -- 返回4条(出现"幻影行")
    

4. 更新丢失(Lost Update)

  • 问题:两个事务同时读取并修改同一数据,后提交的事务覆盖了前者的修改
  • 类型
    • 第一类丢失更新:事务A回滚导致事务B的更新被覆盖
    • 第二类丢失更新:事务A提交后,事务B的提交覆盖了A的更新
  • 示例
    -- 初始值:balance=1000
    -- 事务A:增加100
    SELECT balance INTO @bal FROM accounts WHERE id=1;  -- 读到1000
    SET @new = @bal + 100;
    UPDATE accounts SET balance = @new WHERE id=1;  -- 写为1100
    
    -- 事务B:增加200(同时执行)
    SELECT balance INTO @bal FROM accounts WHERE id=1;  -- 也读到1000
    SET @new = @bal + 200;
    UPDATE accounts SET balance = @new WHERE id=1;  -- 覆盖为1200(丢失了A的+100)
    

问题对比表

问题类型关键特征数据变化方式隔离级别影响
脏读读取未提交数据任何修改READ UNCOMMITTED 会发生
不可重复读同记录多次读取结果不同数据修改READ COMMITTED 会发生
幻读结果集数量/内容变化数据增/删REPEATABLE READ 可能发生
更新丢失后提交覆盖先提交的修改并发修改冲突所有级别都可能发生(需锁解决)

解决方案

  1. 隔离级别控制

    SET TRANSACTION ISOLATION LEVEL [级别];
    -- 级别:READ UNCOMMITTED / READ COMMITTED / REPEATABLE READ / SERIALIZABLE
    
  2. 锁机制

    • 共享锁(S锁):SELECT ... LOCK IN SHARE MODE
    • 排他锁(X锁):SELECT ... FOR UPDATE
  3. 乐观锁(应用层解决):

    UPDATE products 
    SET stock = stock - 10, version = version + 1 
    WHERE id = 100 AND version = 5; -- 检查版本号
    
  4. MVCC(多版本并发控制)

    • InnoDB通过Undo Log实现快照读
    • 保证在REPEATABLE READ级别下不会出现不可重复读

各隔离级别解决能力

隔离级别脏读不可重复读幻读更新丢失
READ UNCOMMITTED
READ COMMITTED
REPEATABLE READ❌*
SERIALIZABLE

*注:MySQL的InnoDB在REPEATABLE READ下通过Next-Key Locking解决了幻读问题

选择隔离级别需权衡数据一致性和并发性能,通常:

  • 金融系统:SERIALIZABLE
  • 通用系统:REPEATABLE READ(MySQL默认)
  • 高并发读:READ COMMITTED
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值