电商系统中通过分布式锁,可以确保库存扣减操作的原子性,避免超卖问题。


1. 问题背景

假设有一个电商平台,某件商品的库存为 10。现在有多个用户同时尝试购买这件商品,如果不加控制,可能会出现以下问题:

  • 用户 A 和 用户 B 同时查询库存,都看到库存为 10。
  • 用户 A 下单并扣减库存,库存变为 9。
  • 用户 B 也下单并扣减库存,库存再次变为 8。

虽然两个用户都成功下单,但实际上库存只应该减少 2(从 10 到 8),而不是 1(从 10 到 9,再到 8)。如果并发量更大,可能会导致库存被扣减多次,最终出现超卖问题(即实际库存不足,但系统显示库存还有剩余)。


2. 分布式锁的作用

分布式锁的作用是确保在多个用户同时尝试购买同一件商品时,库存扣减操作是原子的,即同一时刻只有一个用户能够执行扣减操作。具体流程如下:

  1. 用户 A 尝试获取分布式锁,锁住商品库存。
  2. 用户 A 查询库存,发现库存为 10,扣减库存到 9,然后释放锁。
  3. 用户 B 尝试获取分布式锁,此时锁已被 用户 A 持有,用户 B 需要等待。
  4. 用户 A 释放锁后,用户 B 获取锁,查询库存,发现库存为 9,扣减库存到 8,然后释放锁。

通过这种方式,分布式锁确保了库存扣减操作的原子性,避免了超卖问题。


3. 代码示例

以下是一个基于 Redis 分布式锁的伪代码示例:

import redis
import time

# 连接 Redis
redis_client = redis.StrictRedis(host='localhost', port=6379, db=0)

def purchase_item(user_id, item_id):
    lock_key = f"lock:{item_id}"  # 锁的键
    stock_key = f"stock:{item_id}"  # 库存的键

    # 尝试获取锁
    lock_acquired = redis_client.set(lock_key, user_id, nx=True, ex=10)  # nx=True 表示键不存在时才设置,ex=10 表示锁的过期时间为 10 秒
    if not lock_acquired:
        print(f"用户 {user_id} 获取锁失败,等待重试...")
        time.sleep(1)
        return purchase_item(user_id, item_id)  # 重试

    try:
        # 查询库存
        stock = int(redis_client.get(stock_key))
        if stock <= 0:
            print(f"用户 {user_id} 购买失败,库存不足")
            return False

        # 扣减库存
        redis_client.decr(stock_key)
        print(f"用户 {user_id} 购买成功,库存剩余: {stock - 1}")
        return True
    finally:
        # 释放锁
        redis_client.delete(lock_key)

4. 关键点解释

  1. 锁的作用:

    • 锁确保同一时刻只有一个用户能够执行库存扣减操作,避免了并发冲突。
    • 锁的键通常是商品的唯一标识(如 item_id),锁的值可以是用户标识(如 user_id)。
  2. 原子性:

    • 在获取锁后,查询库存和扣减库存的操作是原子的,不会被其他用户打断。
  3. 锁的过期时间:

    • 为了避免死锁(如用户获取锁后崩溃,导致锁无法释放),锁需要设置一个合理的过期时间(如 10 秒)。
  4. 重试机制:

    • 如果获取锁失败,用户需要等待并重试,直到成功获取锁或超时。

5. 总结

在电商系统中,分布式锁的作用是确保库存扣减操作的原子性,避免多个用户同时扣减库存导致的超卖问题。通过锁机制,系统能够保证在高并发场景下,库存数据的一致性和正确性。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐