我刚刚在Redis中遇到了一个有趣的Lua环境行为:
我有一个Lua脚本执行一些简单的集合操作,并在脚本末尾生成一个唯一的时间戳,如id - 使用Redis作为时间戳oracle - 如下所示:
...
local time = redis.call('TIME')
local millis = (tonumber(time[1]) * 1000) + math.floor(tonumber(time[2]) / 1000)
local version = string.format("%.0f",mills) .. string.format("%05d", math.random(99999))
现在version
是这样的:145209287564117083
由一个时间戳和最后五个随机数字组成 - 至少这就是我的想法。
实际发生的是,最后的五个随机数字(由math.random(99999)
生成的根本不是随机的,但总是数字17083
,无论脚本执行的频率如何。< / p>
对我来说,这不是什么大问题(因为我可以在脚本返回后附加随机数字),但我没想到这种行为,因此需要相当长的时间才能找到我的错误。
我希望这些信息可以节省一些时间。
答案 0 :(得分:2)
我认为这种行为的原因是Redis试图阻止人们在脚本中生成随机密钥,因为在复制中这些脚本被发送到副本(而不是数据本身)。因此,生成随机密钥可能会导致副本不一致。
这就是为什么在调用redis.call('TIME')
之后,脚本中不允许写入Redis。
我的猜测是,由于同样的原因,Redis中的Lua环境始终从math.random()
返回相同的数字。
答案 1 :(得分:2)
如果您正在调用lua脚本,最好的办法是将时间作为脚本参数传递。这允许您完全避免redis.call(&#34; TIME&#34;),然后您可以使用当前时间设置种子。
local time = ARGV[1];
math.randomseed(time);
local millis = (tonumber(time[1]) * 1000) + math.floor(tonumber(time[2]) / 1000)
local version = string.format("%.0f",mills) .. string.format("%05d", math.random(99999))
这也避免了任何未来的复制问题,因为所有实例都会收到相同的参数并生成相同的输出。