我成功地使用Redis编写了一个文本搜索和其他条件的交集。为了实现这一点,我使用了Lua脚本。问题在于,我不仅从该脚本中读取值,还要写入值。从Redis 3.2开始,可以通过调用redis.replicate_commands()
来实现,但在3.2之前不行。
以下是我存储值的方式。
名称
> HSET product:name 'Cool product' 1
> HSET product:name 'Nice product' 2
价格
> ZADD product:price 49.90 1
> ZADD product:price 54.90 2
例如,为了获取所有匹配'ice'
的产品,我需要调用以下代码:
> HSCAN product:name 0 MATCH *ice*
然而,由于HSCAN
使用游标,我需要多次调用它来获取所有结果。这就是我使用Lua脚本的地方:
local cursor = 0
local fields = {}
local ids = {}
local key = 'product:name'
local value = '*' .. ARGV[1] .. '*'
repeat
local result = redis.call('HSCAN', key, cursor, 'MATCH', value)
cursor = tonumber(result[1])
fields = result[2]
for i, id in ipairs(fields) do
if i % 2 == 0 then
ids[#ids + 1] = id
end
end
until cursor == 0
return ids
由于不可能在另一个调用中使用脚本的结果,例如 SADD key EVAL(SHA) ...
。而且,在脚本中也不能使用全局变量。我已更改字段循环内部的部分,以访问脚本外部的ID列表:
if i % 2 == 0 then
ids[#ids + 1] = id
redis.call('SADD', KEYS[1], id)
end
我必须在第一行添加redis.replicate_commands()
。通过这个改变,当我调用脚本时可以从传递的键中获取所有ID(参见KEYS[1]
)。
最后,要获取价格在40到50之间、名称包含“ice”的100个产品 ID 列表,我会执行以下操作:
> ZUNIONSTORE tmp:price 1 product:price WEIGHTS 1
> ZREMRANGEBYSCORE tmp:price 0 40
> ZREMRANGEBYSCORE tmp:price 50 +INF
> EVALSHA b81c2b... 1 tmp:name ice
> ZINTERSTORE tmp:result tmp:price tmp:name
> ZCOUNT tmp:result -INF +INF
> ZRANGE tmp:result 0 100
我使用ZCOUNT
命令提前知道有多少结果页,以count / 100
计算。
如我之前所说,这在Redis 3.2上运行得很好。但是当我尝试在只支持Redis 2.8的AWS上运行代码时,它就无法工作了。我不确定如何在没有使用脚本或从脚本中编写的情况下迭代HSCAN
游标。有没有办法使其在Redis 2.8上运行?
一些注意事项:
- 我知道可以在Redis外部进行部分处理(例如迭代游标或交集匹配),但这会影响应用程序的整体性能。
- 我不想部署自己的Redis实例以使用版本3.2。
- 以上标准(价格范围和名称)仅是为了保持简单起见的示例。我有其他字段和类型的匹配,不仅仅是这些。
- 我不确定我存储数据的方式是否是最佳方式。我愿意听取建议。
redis.call('SADD', KEYS[1], id)
。 - Gustavo Straube