MST

星途 面试题库

面试题:如何利用Redis实现分布式事务中的原子性操作

假设在一个分布式电商系统的订单处理场景中,要保证库存扣减与订单生成这两个操作的原子性,基于Redis,你会如何设计并实现相关的分布式事务机制,详细说明设计思路、使用的Redis命令及原理。
36.7万 热度难度
数据库Redis

知识考点

AI 面试

面试题答案

一键面试

设计思路

  1. 使用Redis的单线程特性:利用Redis单线程执行命令的特性,保证一系列操作在Redis层面的原子性。
  2. Lua脚本:将库存扣减和订单生成相关操作封装在Lua脚本中,确保在执行脚本时这一系列操作不会被其他命令打断。
  3. Watch机制:在执行Lua脚本前,通过Watch命令监控库存相关的键,防止在脚本执行过程中库存被其他客户端修改。

使用的Redis命令

  1. Watch:用于监视一个或多个键,当事务执行时,如果被监视的键被其他客户端修改,事务将被打断。
  2. Multi:标记一个事务块的开始。
  3. Exec:执行所有事务块内的命令。
  4. Lua脚本相关命令(EVAL或EVALSHA):用于执行Lua脚本。

原理

  1. Watch原理:Watch命令会在Redis服务端为每个客户端维护一个被监视键的列表。当被监视的键被修改时,Redis会标记这个客户端的事务需要被取消。在执行Exec命令时,Redis会检查事务对应的客户端所监视的键是否被修改,如果有修改则不执行事务。
  2. Lua脚本原理:Redis执行Lua脚本时,会将Lua脚本作为一个整体执行,不会被其他命令打断。在Lua脚本中可以通过Redis的API操作Redis数据,这样就可以将库存扣减和订单生成操作作为一个原子操作进行处理。

以下是一个简单的Lua脚本示例(假设库存键为stock_key,订单生成相关操作通过向一个订单队列order_queue中添加数据模拟):

-- 获取库存键的值
local stock = redis.call('GET', KEYS[1])
-- 判断库存是否足够
if tonumber(stock) >= tonumber(ARGV[1]) then
    -- 扣减库存
    redis.call('DECRBY', KEYS[1], ARGV[1])
    -- 生成订单,这里简单模拟为向订单队列添加数据
    redis.call('RPUSH', KEYS[2], ARGV[2])
    return 1
else
    return 0
end

在客户端代码中,先使用Watch命令监视库存键,然后调用EVAL(或EVALSHA)命令执行Lua脚本,例如在Python中使用redis - py库:

import redis

r = redis.Redis(host='localhost', port=6379, db = 0)

# 监视库存键
r.watch('stock_key')
try:
    stock = r.get('stock_key')
    if stock is not None and int(stock) >= 1:
        script = """
        local stock = redis.call('GET', KEYS[1])
        if tonumber(stock) >= tonumber(ARGV[1]) then
            redis.call('DECRBY', KEYS[1], ARGV[1])
            redis.call('RPUSH', KEYS[2], ARGV[2])
            return 1
        else
            return 0
        end
        """
        result = r.eval(script, 2,'stock_key', 'order_queue', 1, 'order_info')
        print(result)
    else:
        print('库存不足')
finally:
    r.unwatch()