redis与mysql的数据一致性问题(并发更新)
并发更新引发的问题
案例场景: 考虑一个在线购物系统,其中商品库存信息存储在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不匹配,则表示有其他事务已经修改了数据,更新将不会执行。