MST

星途 面试题库

面试题:Redis EVAL命令参数验证策略在复杂场景下的应用

假设在一个电商系统中,使用Redis EVAL命令执行Lua脚本实现商品库存的扣减和订单记录,该Lua脚本接收商品ID、购买数量、用户ID等参数。请详细描述如何设计参数验证策略,以确保在高并发场景下,参数的准确性和业务逻辑的正确性,同时要考虑防止恶意请求导致的数据错误。
28.0万 热度难度
数据库Redis

知识考点

AI 面试

面试题答案

一键面试
  1. 参数类型验证
    • 商品ID:应验证为有效的标识符类型,通常为数字或字符串。如果是数字,需确保其为正整数(因为负的商品ID在电商系统中无意义)。在Lua脚本中可以使用type函数来判断参数类型,例如:
    local productId = ARGV[1]
    if type(productId) ~= 'number' or productId <= 0 then
        return {false, "Invalid product ID"}
    end
    
    • 购买数量:必须是正整数,以确保有实际的商品购买行为。同样在Lua脚本中验证:
    local quantity = ARGV[2]
    if type(quantity) ~= 'number' or quantity <= 0 then
        return {false, "Invalid quantity"}
    end
    
    • 用户ID:类似于商品ID,验证为有效的标识符类型,通常为数字或字符串。
    local userId = ARGV[3]
    if type(userId) ~= 'number' and type(userId) ~= 'string' then
        return {false, "Invalid user ID"}
    end
    
  2. 库存可用性验证
    • 在扣减库存前,先从Redis中获取当前商品的库存数量。例如:
    local stockKey = "product:".. productId.. ":stock"
    local currentStock = tonumber(redis.call('GET', stockKey))
    if currentStock < quantity then
        return {false, "Insufficient stock"}
    end
    
    • 为防止在获取库存和扣减库存之间其他请求修改库存,可使用Redis的事务机制或Lua脚本的原子性。因为整个Lua脚本在Redis中是原子执行的,所以在脚本执行期间库存不会被其他并发请求修改。
  3. 防止恶意请求
    • 频率限制
      • 可以使用Redis的计数器来记录每个用户对某个商品的购买请求频率。例如,以用户ID和商品ID组合作为键,每次请求时增加计数器的值。
      • 在Lua脚本开始时,检查计数器的值是否超过设定的阈值。如果超过,拒绝请求并返回错误信息。例如,假设每分钟最多允许同一用户对同一商品发起10次购买请求:
      local requestKey = "user:".. userId.. ":product:".. productId.. ":request_count"
      local currentCount = tonumber(redis.call('GET', requestKey))
      if currentCount == nil then
          currentCount = 0
      end
      if currentCount >= 10 then
          return {false, "Too many requests in a short time"}
      end
      redis.call('INCR', requestKey)
      redis.call('EXPIRE', requestKey, 60) -- 设置60秒过期,保证每分钟计数
      
    • 黑名单机制
      • 维护一个恶意用户ID的黑名单,存储在Redis的Set中。
      • 在Lua脚本开始时,检查用户ID是否在黑名单中。如果在,直接拒绝请求。例如:
      local blacklistKey = "blacklist:users"
      local isBlacklisted = redis.call('SISMEMBER', blacklistKey, userId)
      if isBlacklisted == 1 then
          return {false, "User is blacklisted"}
      end
      
  4. 业务逻辑一致性验证
    • 确保订单记录和库存扣减操作的一致性。在Lua脚本中,先执行库存扣减操作,成功后再记录订单。例如:
    local stockKey = "product:".. productId.. ":stock"
    local orderKey = "user:".. userId.. ":orders"
    local orderRecord = "product:".. productId.. ":quantity:".. quantity
    redis.call('DECRBY', stockKey, quantity)
    redis.call('RPUSH', orderKey, orderRecord)
    return {true, "Order created successfully"}
    
    • 如果在记录订单过程中出现错误,应考虑回滚库存扣减操作。但由于Lua脚本的原子性,一旦脚本开始执行,中途不会被打断,所以在设计脚本时要尽量保证订单记录逻辑的正确性。如果确实需要回滚机制,可以在Lua脚本中实现简单的错误处理逻辑,例如在记录订单失败时,增加库存数量:
    local stockKey = "product:".. productId.. ":stock"
    local orderKey = "user:".. userId.. ":orders"
    local orderRecord = "product:".. productId.. ":quantity:".. quantity
    local stockResult = redis.call('DECRBY', stockKey, quantity)
    local orderResult = redis.call('RPUSH', orderKey, orderRecord)
    if orderResult ~= 1 then
        redis.call('INCRBY', stockKey, quantity)
        return {false, "Order record failed, stock rolled back"}
    end
    return {true, "Order created successfully"}