在分布式系统中,冻结库存 和 释放库存 是保证库存一致性的重要操作。冻结库存用于在用户下单时预留库存,防止超卖;释放库存用于在订单取消或支付失败时释放已冻结的库存。以下是实现冻结库存和释放库存的详细方案。
在库存表中,通常需要设计以下字段:
总库存(total_stock):商品的总库存数量。
可用库存(available_stock):当前可售的库存数量。
冻结库存(frozen_stock):已冻结的库存数量。
冻结库存:
用户下单时,从可用库存中扣除指定数量,并增加到冻结库存中。
冻结库存的目的是预留库存,防止其他用户购买同一商品。
释放库存:
订单取消或支付失败时,从冻结库存中扣除指定数量,并恢复到可用库存中。
释放库存的目的是将未使用的库存重新变为可售状态。
在数据库中,可以通过以下 SQL 语句实现冻结库存:
sql
复制
UPDATE inventory SET available_stock = available_stock - :quantity, frozen_stock = frozen_stock + :quantity WHERE item_id = :item_id AND available_stock >= :quantity;
:quantity
:需要冻结的库存数量。
item_id
:商品 ID。
available_stock >= :quantity
:确保可用库存足够。
在 Java 中,可以通过以下代码实现冻结库存:
java
复制
public boolean freezeStock(String itemId, int quantity) { String sql = "UPDATE inventory SET available_stock = available_stock - ?, frozen_stock = frozen_stock + ? WHERE item_id = ? AND available_stock >= ?"; try (Connection conn = dataSource.getConnection(); PreparedStatement pstmt = conn.prepareStatement(sql)) { pstmt.setInt(1, quantity); pstmt.setInt(2, quantity); pstmt.setString(3, itemId); pstmt.setInt(4, quantity); int rows = pstmt.executeUpdate(); return rows > 0; // 返回是否冻结成功 } catch (SQLException e) { throw new RuntimeException("冻结库存失败", e); } }
在数据库中,可以通过以下 SQL 语句实现释放库存:
sql
复制
UPDATE inventory SET frozen_stock = frozen_stock - :quantity, available_stock = available_stock + :quantity WHERE item_id = :item_id AND frozen_stock >= :quantity;
:quantity
:需要释放的库存数量。
item_id
:商品 ID。
frozen_stock >= :quantity
:确保冻结库存足够。
在 Java 中,可以通过以下代码实现释放库存:
java
复制
public boolean releaseStock(String itemId, int quantity) { String sql = "UPDATE inventory SET frozen_stock = frozen_stock - ?, available_stock = available_stock + ? WHERE item_id = ? AND frozen_stock >= ?"; try (Connection conn = dataSource.getConnection(); PreparedStatement pstmt = conn.prepareStatement(sql)) { pstmt.setInt(1, quantity); pstmt.setInt(2, quantity); pstmt.setString(3, itemId); pstmt.setInt(4, quantity); int rows = pstmt.executeUpdate(); return rows > 0; // 返回是否释放成功 } catch (SQLException e) { throw new RuntimeException("释放库存失败", e); } }
用户下单,选择商品和数量。
调用 freezeStock
方法冻结库存。
如果冻结成功,创建订单;否则,提示库存不足。
用户支付成功。
调用 deductStock
方法扣减库存:
sql
复制
UPDATE inventory SET frozen_stock = frozen_stock - :quantity WHERE item_id = :item_id AND frozen_stock >= :quantity;
更新订单状态为“已支付”。
用户支付失败或取消订单。
调用 releaseStock
方法释放库存。
更新订单状态为“已取消”。
在高并发场景下,多个用户可能同时冻结同一商品的库存,导致超卖。
解决方案:
使用数据库的行级锁(如 SELECT ... FOR UPDATE
)或乐观锁(如版本号)保证并发安全。
冻结库存、创建订单和支付操作需要在同一个事务中执行,保证数据一致性。
解决方案:
使用分布式事务(如 TCC、本地消息表)或数据库事务。
在冻结库存时,需要检查可用库存是否足够。
解决方案:
在 SQL 中添加 available_stock >= :quantity
条件。
冻结库存和释放库存操作需要保证幂等性,避免重复操作。
解决方案:
在业务逻辑中增加幂等性检查(如订单状态检查)。
java
复制
public boolean freezeStock(String itemId, int quantity) { String sql = "UPDATE inventory SET available_stock = available_stock - ?, frozen_stock = frozen_stock + ? WHERE item_id = ? AND available_stock >= ?"; try (Connection conn = dataSource.getConnection(); PreparedStatement pstmt = conn.prepareStatement(sql)) { pstmt.setInt(1, quantity); pstmt.setInt(2, quantity); pstmt.setString(3, itemId); pstmt.setInt(4, quantity); int rows = pstmt.executeUpdate(); return rows > 0; // 返回是否冻结成功 } catch (SQLException e) { throw new RuntimeException("冻结库存失败", e); } }
java
复制
public boolean releaseStock(String itemId, int quantity) { String sql = "UPDATE inventory SET frozen_stock = frozen_stock - ?, available_stock = available_stock + ? WHERE item_id = ? AND frozen_stock >= ?"; try (Connection conn = dataSource.getConnection(); PreparedStatement pstmt = conn.prepareStatement(sql)) { pstmt.setInt(1, quantity); pstmt.setInt(2, quantity); pstmt.setString(3, itemId); pstmt.setInt(4, quantity); int rows = pstmt.executeUpdate(); return rows > 0; // 返回是否释放成功 } catch (SQLException e) { throw new RuntimeException("释放库存失败", e); } }
冻结库存 和 释放库存 是保证库存一致性的关键操作。
冻结库存用于预留库存,防止超卖;释放库存用于将未使用的库存恢复为可售状态。
在实现时需要注意并发问题、事务管理和幂等性。
通过合理的 SQL 和业务逻辑设计,可以高效地实现库存管理。