【问题标题】:Redis Lua Script math.randomRedis Lua 脚本 math.random
【发布时间】:2016-07-09 06:08:30
【问题描述】:

我刚刚在 Redis 中遇到了 Lua 环境的一个有趣行为:

我有一个 Lua 脚本,它执行一些简单的集合操作并在脚本末尾生成一个唯一的时间戳,如 id - 将 Redis 用作时间戳预言机 - 如下所示:

...
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,无论脚本执行的频率如何。

对我来说这没什么大不了的(因为我可以在脚本返回后附加随机数字),但我没想到会出现这种行为,因此需要相当长的时间才能找到我的错误。

我希望这些信息可以节省一些时间。

【问题讨论】:

  • 在脚本顶部尝试math.randomseed(os.time())
  • @warspyking - 不起作用,因为 Redis Lua 沙箱没有 os 库,而且在任何情况下,Redis 的 math.random 被设计为在使用时产生相同的值基于脚本的复制(v3.2 之前唯一可用的模式)
  • @Itamar 嗯,它真的可以是任何不断变化的东西。不一定是os.time

标签: random lua redis


【解决方案1】:

如果您正在调用 lua 脚本,最好的办法是将时间作为脚本参数传递。这可以让您完全避免 redis.call("TIME"),然后您可以将种子设置为当前时间。

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))

这也避免了任何未来的复制问题,因为所有实例都将接收相同的参数并生成相同的输出。

【讨论】:

  • 虽然OP实际上并没有提出问题,但我认为这是对“我应该如何在redis lua脚本中生成伪随机数?”这个问题的最佳答案。
【解决方案2】:

我认为这种行为的原因是 Redis 试图阻止人们在脚本内生成随机密钥,因为在复制时这些脚本被传送到副本(而不是数据本身)。因此生成随机密钥可能会导致副本不一致。

这就是为什么在调用 redis.call('TIME') 之后,脚本中不允许写入 Redis。

我的猜测是,出于同样的原因,Redis 中的 Lua 环境总是从 math.random() 返回相同的数字。

【讨论】:

猜你喜欢
  • 2015-10-27
  • 2016-05-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-27
  • 2020-11-08
  • 2014-04-15
  • 1970-01-01
相关资源
最近更新 更多