在 OpenResty 中操作 Redis 合并两个有序集合(ZSET)A 和 B,可以通过 Lua 脚本实现高效且原子性的操作。以下是完整的示例代码和逻辑说明:
方法一:使用 ZUNIONSTORE
(推荐)
若只需临时合并并获取结果(不存储),可跳过第3步。
local redis = require "resty.redis"
local red = redis:new()
-- 连接 Redis
local ok, err = red:connect("127.0.0.1", 6379)
if not ok then
ngx.say("Redis连接失败: ", err)
return
end
-- 定义源 ZSET 和目标 ZSET 的 key
local zset_a = "A"
local zset_b = "B"
local zset_merged = "MERGED_RESULT"
-- 1. 使用 ZUNIONSTORE 合并(默认 SUM 聚合分数)
local res, err = red:zunionstore(zset_merged, 2, zset_a, zset_b)
if not res then
ngx.say("合并失败: ", err)
else
ngx.say("合并成功,结果条目数: ", res)
end
-- 2. (可选)设置过期时间
red:expire(zset_merged, 3600) -- 1小时后自动删除
-- 3. (可选)读取合并后的数据
local members, err = red:zrange(zset_merged, 0, -1, "WITHSCORES")
if members then
for i = 1, #members, 2 do
ngx.say(members[i], ": ", members[i+1])
end
end
-- 释放连接
red:close()
方法二:手动遍历合并(灵活控制逻辑)
如果需要自定义分数处理逻辑(如取最大值、平均值等):
local function merge_zsets(red, src1, src2, dest)
-- 获取所有成员及分数
local members1 = red:zrange(src1, 0, -1, "WITHSCORES")
local members2 = red:zrange(src2, 0, -1, "WITHSCORES")
-- [[
自定义分数处理逻辑,例如:
- SUM(默认):score_new = score1 + score2
- MAX:score_new = math.max(score1 or -inf, score2 or -inf)
此处以 SUM为例
--]]
-- 合并到新 ZSET (支持增量添加)
for i = 1,#members1 ,2 do
red:zadd(dest ,members[i+1] ,members[i])
end
for i= 1,#members2 ,2 do
local member=members[ i]
local score=tonumber(members[ i+ ] )or 0
--如果已存在则累加分数 (SUM)
local existing_score=tonumber(red.zscore(dest ,member ))or 0
red.zadd(dest ,existing_score+score ,member )
end
end
--调用示例
merge_zsets(red,"A","B","MERGED_RESULT")
关键注意事项
项目 | 说明 |
---|---|
原子性 | Lua脚本在Redis中单线程执行,无需担心并发问题 |
性能优化 | ZUNIONSTORE 时间复杂度O(N)+O(M),优于手动遍历 |
内存消耗 | 大ZSET建议分批次处理或使用ZSCAN 迭代 |
集群环境 | KEYS需在同一哈希槽,否则用{hashtag} 确保一致性,如:zunionstore {user}:merged ... |
常见问题解决
- 错误处理:检查Redis连接池是否耗尽(
max_connections
)。 - 调试技巧:通过
redis.log(ngx.NOTICE,"debug msg")
输出日志到Nginx error log。
根据需求选择合适的方法即可高效实现ZSET合并。
内容由零声教学AI助手提供,问题来源于学员提问