Redis lua set使用场景举例
【摘要】 在 Redis 的 Lua 脚本中,SET 类型(即 Redis 的字符串类型)虽然看似简单,但结合 Lua 的逻辑控制能力后,可以高效实现许多复杂场景。以下是 SET 类型在 Lua 脚本中的典型使用场景及代码示例,涵盖原子操作、条件判断、分布式锁等核心需求。 1. 原子性计数器场景:实现一个原子性的计数器,支持递增、递减、重置等操作。优势:Lua 脚本保证整个计数过程的原子性,避免竞态条...
在 Redis 的 Lua 脚本中,SET
类型(即 Redis 的字符串类型)虽然看似简单,但结合 Lua 的逻辑控制能力后,可以高效实现许多复杂场景。以下是 SET
类型在 Lua 脚本中的典型使用场景及代码示例,涵盖原子操作、条件判断、分布式锁等核心需求。
1. 原子性计数器
场景:实现一个原子性的计数器,支持递增、递减、重置等操作。
优势:Lua 脚本保证整个计数过程的原子性,避免竞态条件。
示例:带阈值检查的计数器
-- KEYS[1]: 计数器键名(如 "page:views")
-- ARGV[1]: 递增步长(如 1)
-- ARGV[2]: 最大阈值(如 1000)
local key = KEYS[1]
local increment = tonumber(ARGV[1])
local max_limit = tonumber(ARGV[2])
local current = tonumber(redis.call('GET', key) or '0')
local new_value = current + increment
if new_value > max_limit then
return 0 -- 超过阈值,返回失败
else
redis.call('SET', key, new_value)
return new_value -- 返回新值
end
调用方式:
127.0.0.1:6379> EVAL "..." 1 page:views 1 1000
2. 分布式锁(安全获取与释放)
场景:在分布式系统中实现互斥锁,确保同一时间只有一个客户端能操作共享资源。
关键点:
- 使用
SET NX PX
原子性获取锁。 - 通过 Lua 脚本确保“检查锁归属”和“释放锁”的原子性。
(1) 获取锁
-- KEYS[1]: 锁键名(如 "order:lock")
-- ARGV[1]: 锁的唯一标识(如客户端 UUID)
-- ARGV[2]: 锁的过期时间(毫秒)
local key = KEYS[1]
local value = ARGV[1]
local ttl = tonumber(ARGV[2])
if redis.call('SET', key, value, 'NX', 'PX', ttl) then
return 1 -- 获取锁成功
else
return 0 -- 锁已被占用
end
调用方式:
127.0.0.1:6379> EVAL "..." 1 order:lock "client-123" 30000
(2) 释放锁(安全删除)
-- KEYS[1]: 锁键名
-- ARGV[1]: 锁的唯一标识(必须匹配才能删除)
local key = KEYS[1]
local value = ARGV[1]
if redis.call('GET', key) == value then
return redis.call('DEL', key) -- 删除锁
else
return 0 -- 锁不属于当前客户端,不删除
end
调用方式:
127.0.0.1:6379> EVAL "..." 1 order:lock "client-123"
3. 缓存防击穿(热点数据保护)
场景:当缓存失效时,防止大量请求直接穿透到数据库。
策略:
- 检查缓存是否存在。
- 如果不存在,先获取分布式锁,再查询数据库并更新缓存。
- 其他请求在锁未释放时返回默认值或等待。
示例脚本
-- KEYS[1]: 数据键名(如 "product:1001")
-- ARGV[1]: 锁键名(如 "product:1001:lock")
-- ARGV[2]: 锁的过期时间(秒)
local data_key = KEYS[1]
local lock_key = ARGV[1]
local lock_ttl = tonumber(ARGV[2])
-- 1. 尝试从缓存获取数据
local data = redis.call('GET', data_key)
if data then
return data -- 缓存命中
end
-- 2. 获取分布式锁
if redis.call('SET', lock_key, '1', 'NX', 'EX', lock_ttl) then
-- 模拟查询数据库(实际应替换为真实逻辑)
local db_data = "db_result_for_" .. data_key
redis.call('SET', data_key, db_data, 'EX', 60) -- 写入缓存,有效期60秒
redis.call('DEL', lock_key) -- 释放锁
return db_data
else
-- 未获取锁,返回空或等待(根据业务需求)
return nil
end
调用方式:
127.0.0.1:6379> EVAL "..." 1 product:1001 product:1001:lock 5
4. 条件更新(CAS 操作)
场景:实现“比较并设置”(Compare-And-Swap)操作,确保数据一致性。
示例:只有当当前值等于预期值时,才更新为新值。
-- KEYS[1]: 目标键名
-- ARGV[1]: 预期值
-- ARGV[2]: 新值
local key = KEYS[1]
local expected = ARGV[1]
local new_value = ARGV[2]
if redis.call('GET', key) == expected then
redis.call('SET', key, new_value)
return 1 -- 更新成功
else
return 0 -- 条件不满足,未更新
end
调用方式:
127.0.0.1:6379> SET version "1.0"
OK
127.0.0.1:6379> EVAL "..." 1 version "1.0" "2.0" # 成功更新
(integer) 1
127.0.0.1:6379> EVAL "..." 1 version "1.0" "3.0" # 条件不满足
(integer) 0
5. 限流器(固定窗口算法)
场景:限制某个键在指定时间窗口内的操作次数。
逻辑:
- 每次操作时递增计数器。
- 如果计数器超过阈值,则拒绝操作。
- 计数器过期后自动重置。
-- KEYS[1]: 限流键名(如 "api:rate_limit:user123")
-- ARGV[1]: 阈值(如 100)
-- ARGV[2]: 时间窗口(秒)
local key = KEYS[1]
local limit = tonumber(ARGV[1])
local window = tonumber(ARGV[2])
local current = tonumber(redis.call('GET', key) or '0')
if current >= limit then
return 0 -- 超过限流阈值
end
redis.call('INCR', key)
if current == 0 then
redis.call('EXPIRE', key, window) -- 首次设置过期时间
end
return 1 -- 允许操作
调用方式:
127.0.0.1:6379> EVAL "..." 1 api:rate_limit:user123 100 60
6. 配置热更新
场景:动态更新系统配置,无需重启服务。
示例:修改全局开关或参数。
-- KEYS[1]: 配置键名(如 "system:config:feature_flag")
-- ARGV[1]: 新配置值(如 "enabled")
local key = KEYS[1]
local new_value = ARGV[1]
redis.call('SET', key, new_value)
return redis.call('GET', key) -- 返回新值
调用方式:
127.0.0.1:6379> EVAL "..." 1 system:config:feature_flag "enabled"
7. 总结:SET
类型在 Lua 中的核心优势
场景 | 优势 |
---|---|
原子计数器 | 避免竞态条件,无需外部同步机制。 |
分布式锁 | 通过 SET NX PX + Lua 脚本实现安全可靠的锁机制。 |
缓存防击穿 | 结合锁和缓存,防止数据库被突发流量打垮。 |
条件更新 | 实现类似数据库的 CAS 操作,保证数据一致性。 |
限流器 | 简单高效的固定窗口限流算法。 |
配置热更新 | 动态修改配置,实时生效。 |
最佳实践
- 优先使用
EVALSHA
:缓存脚本 SHA1,减少网络传输。 - 错误处理:用
redis.pcall
捕获异常,避免脚本中断。 - 键命名规范:使用
:
分隔命名空间(如service:config:key
)。 - 避免长脚本:Redis 是单线程的,长时间运行的脚本会阻塞其他命令。
通过 Lua 脚本与 SET
类型的结合,Redis 可以轻松应对各种高并发、一致性要求严格的场景。
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)