并发与恢复

一、两个用户同时购买同一商品会出现什么问题

1. 超卖问题(Overselling)

  • 两个用户同时查询到商品状态为"未售出"
  • 两个用户同时提交购买请求
  • 系统可能为同一商品生成两个订单
  • 导致一个商品被卖给两个买家

2. 数据不一致

  • Item表中的status字段更新可能出现竞态条件
  • Orders表中可能插入重复记录
  • 违反"每个商品最多只能被交易一次"的业务规则

3. 丢失更新(Lost Update)

  • 一个用户的更新操作可能被另一个用户的更新覆盖
  • 导致数据状态不正确
  • 例如:两个订单都成功,但商品状态只更新一次

二、如何解决(加锁/事务)

1. 数据库事务(Transaction)

  • 使用BEGIN TRANSACTION开启事务
  • 将查询商品状态、插入订单、更新商品状态作为一个原子操作
  • 使用COMMIT提交或ROLLBACK回滚
  • 确保ACID特性(原子性、一致性、隔离性、持久性)

2. 悲观锁(Pessimistic Locking)

  • 使用SELECT FOR UPDATE对商品记录加排他锁
  • 代码示例:SELECT * FROM Item WHERE item_id = 'i001' FOR UPDATE;
  • 锁定期间其他事务无法修改该记录
  • 适用于并发冲突频繁的场景

3. 乐观锁(Optimistic Locking)

  • 添加版本号字段(version)或时间戳
  • 更新时检查版本号是否变化
  • 如果版本号变化,说明数据已被修改,操作失败需重试
  • 适用于读多写少的场景

4. 唯一约束

  • 在Orders表的item_id字段上设置UNIQUE约束
  • 数据库层面防止同一商品被重复购买
  • 尝试插入重复记录时会抛出异常

5. 应用层限流

  • 使用分布式锁(如Redis的SETNX)
  • 对同一商品的购买请求进行排队处理
  • 确保同一时间只有一个请求在处理特定商品

三、如果系统崩溃,如何恢复订单数据

1. 数据库日志恢复

  • 事务日志(Transaction Log/Redo Log):记录所有数据修改操作
  • 系统崩溃后,通过重做日志恢复已提交但未写入磁盘的事务
  • 通过撤销日志(Undo Log)回滚未提交的事务
  • 确保数据的一致性和完整性

2. 检查点机制(Checkpoint)

  • 定期将内存中的数据刷新到磁盘
  • 记录检查点位置,减少恢复时间
  • 恢复时只需处理检查点之后的日志

3. 数据备份策略

  • 全量备份:定期备份整个数据库
  • 增量备份:备份自上次备份以来的变化
  • 差异备份:备份自上次全量备份以来的变化
  • 制定备份计划:每日全量备份 + 每小时增量备份

4. 主从复制(Replication)

  • 设置主数据库和从数据库
  • 实时同步数据到从库
  • 主库崩溃时可快速切换到从库
  • 提供高可用性和数据冗余

5. 恢复流程

  • ① 使用最近的备份文件恢复数据库
  • ② 应用备份后的日志文件(Redo Log)
  • ③ 回滚未完成的事务(Undo Log)
  • ④ 验证数据一致性
  • ⑤ 恢复服务运行
返回首页