以下是我存储值的方法.
名称
> 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键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]).
最后,为了获得列表100产品ID的价格在40到50之间,其中名称包含“ice”,我执行以下操作:
> 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调用预先知道我将拥有多少个结果页面,数量为/ 100.
正如我之前所说,这与Redis 3.2很好地配合.但是当我试图在AWS上运行代码时,它只支持Redis高达2.8,我无法再让它工作了.我不知道如何在不使用脚本或不从脚本编写的情况下使用HSCAN游标进行迭代.有一种方法可以在Redis 2.8上运行吗?
一些考虑:
>我知道我可以在Redis之外进行部分处理(比如迭代光标或交叉匹配),但它会影响应用程序的整体性能.
>我不想自己部署Redis实例来使用3.2版.
>上面的标准(价格范围和名称)只是一个简单的例子.我有其他领域和比赛类型,不仅仅是那些.
>我不确定我存储数据的方式是否是最佳方式.我愿意听取有关它的建议.
在我看来,很少有理由在Lua脚本中使用SCAN命令.该命令的主要目的是允许您以小批量执行操作,这样您就不会锁定处理巨大密钥空间(或散列密钥空间)的服务器.由于脚本是原子的,因此使用HSCAN无济于事 – 您仍然会锁定服务器,直到整个过程完成.
以下是我可以看到的选项:
如果您不能冒险使用冗长的命令锁定服务器:
>在客户端上使用HSCAN.这是最安全的选择,但也是最慢的.
如果您想在单个原子Lua命令中进行尽可能多的处理:
>使用Redis 3.2和脚本效果复制.>在脚本中进行扫描,但将值返回给客户端并从那里开始写入. (也就是Karthikeyan Gopall的回答.)>而不是HSCAN,在脚本中执行HKEYS并使用Lua的模式匹配过滤结果.由于HKEYS是确定性的,因此后续写入不会有问题.当然,缺点是您必须首先读入所有键,无论它们是否与您的模式匹配. (虽然HSCAN也是散列大小的O(N).)