面试题答案
一键面试常见业务场景
- 银行转账:在进行资金从一个账户转移到另一个账户的操作时,需要确保两个账户余额的修改是原子性的,并且在操作过程中两个账户的余额不会被其他进程修改。
- 库存管理:当商品库存数量减少时,需要保证库存数量在读取和更新之间不会被其他业务逻辑修改,以防止超卖情况。
- 分布式系统中的资源竞争:例如多个节点同时竞争有限的资源,需要保证资源分配过程中的数据一致性。
保障数据一致性原理
Redis 的 WATCH
命令用于监控一个或多个键。在执行 WATCH
后,直到 EXEC
执行前,如果被监控的键被其他客户端修改,那么当前客户端的事务将被打断,EXEC
返回 nil
,事务不会执行。这样可以确保在 WATCH
开始到 EXEC
之间,被监控的数据没有发生变化,从而保障了数据的一致性。
代码示例(Python)
import redis
# 连接到 Redis 服务器
r = redis.Redis(host='localhost', port=6379, db=0)
# 使用 WATCH、MULTI、EXEC 组合
def transfer(sender, receiver, amount):
pipe = r.pipeline()
while True:
try:
# 监控发送者和接收者账户余额
pipe.watch(sender, receiver)
sender_balance = pipe.get(sender)
receiver_balance = pipe.get(receiver)
if sender_balance is None or receiver_balance is None:
raise ValueError("账户不存在")
sender_balance = int(sender_balance)
receiver_balance = int(receiver_balance)
if sender_balance < amount:
raise ValueError("余额不足")
# 开始事务
pipe.multi()
pipe.decrby(sender, amount)
pipe.incrby(receiver, amount)
# 执行事务
pipe.execute()
return True
except redis.WatchError:
# 事务执行失败,重试
continue
finally:
pipe.unwatch()
# 示例调用
if transfer('account1', 'account2', 100):
print("转账成功")
else:
print("转账失败")
在上述代码中,transfer
函数实现了从 sender
账户向 receiver
账户转账 amount
金额的功能。通过 WATCH
监控两个账户余额,确保在读取余额和修改余额之间,余额不会被其他客户端修改。如果在 WATCH
到 EXEC
之间余额被修改,EXEC
会失败,代码会捕获 WatchError
并重新尝试整个操作。