面试题答案
一键面试- 参数类型验证:
- 商品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
- 商品ID:应验证为有效的标识符类型,通常为数字或字符串。如果是数字,需确保其为正整数(因为负的商品ID在电商系统中无意义)。在Lua脚本中可以使用
- 库存可用性验证:
- 在扣减库存前,先从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中是原子执行的,所以在脚本执行期间库存不会被其他并发请求修改。
- 防止恶意请求:
- 频率限制:
- 可以使用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
- 频率限制:
- 业务逻辑一致性验证:
- 确保订单记录和库存扣减操作的一致性。在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"}