案例场景: 考虑一个在线购物系统,其中商品库存信息存储在MySQL数据库中,同时使用Redis缓存了商品库存以提高读取速度。多个用户同时购买同一商品,导致MySQL和Redis同时发生库存更新操作。
问题: 在这种情况下,可能会发生竞争条件,导致MySQL和Redis中的库存数量不一致。例如,两个用户同时查询库存,得到相同的库存数量,然后都尝试购买,最终导致超卖或者库存数量错误。
使用锁机制:
通过在关键操作中使用锁,可以确保在同一时刻只有一个线程可以执行该操作,避免了竞争条件。在Redis中,可以使用WATCH
和MULTI
命令实现乐观锁。
# Python代码示例 - 使用Redis的WATCH和MULTI命令实现乐观锁
import redis
def purchase_item(user_id, product_id):
redis_client = redis.StrictRedis(host='localhost', port=6379, db=0)
with redis_client.pipeline() as pipe:
while True:
try:
# 监视商品库存
pipe.watch(f'product:{product_id}:stock')
# 获取当前库存
current_stock = int(pipe.get(f'product:{product_id}:stock') or 0)
if current_stock > 0:
# 开始Redis事务
pipe.multi()
# 扣减库存
pipe.decr(f'product:{product_id}:stock')
# 执行Redis事务
pipe.execute()
# 购买成功
print(f"User {user_id} purchased product {product_id}. Remaining stock: {current_stock - 1}")
break
else:
# 库存不足,取消监视,退出循环
pipe.unwatch()
print(f"User {user_id} attempted to purchase product {product_id}, but it's out of stock.")
break
except redis.WatchError:
# 被监视的键被其他客户端修改,重新尝试
continue
使用乐观锁和版本号: 在MySQL中,可以使用乐观锁和版本号机制,通过在更新语句中增加版本号的判断,确保并发更新时只有一个事务可以成功执行。
-- MySQL更新语句示例
UPDATE products SET stock = stock - 1, version = version + 1 WHERE id = 123 AND version = 1;
这里,version字段的值会在每次更新时递增,如果在更新时发现version不匹配,则表示有其他事务已经修改了数据,更新将不会执行。