doc: 阅读redis lua文档

This commit is contained in:
asahi
2025-09-25 11:29:35 +08:00
parent 10aca33a06
commit 3baa0caff2

View File

@@ -173,6 +173,7 @@
- [Script Command](#script-command) - [Script Command](#script-command)
- [Script Replication](#script-replication) - [Script Replication](#script-replication)
- [Replicating commands instead of Scripts](#replicating-commands-instead-of-scripts) - [Replicating commands instead of Scripts](#replicating-commands-instead-of-scripts)
- [Scripts with deterministic writes](#scripts-with-deterministic-writes)
# redis # redis
@@ -2835,4 +2836,100 @@ redis> EVALSHA ffffffffffffffffffffffffffffffffffffffff 0
- 当启用script effects replication时`non-determinstic function`校验将会被移除。故而,可以在脚本中自由的使用`TIME``SRANDMEMBER`这类非确定性的指令 - 当启用script effects replication时`non-determinstic function`校验将会被移除。故而,可以在脚本中自由的使用`TIME``SRANDMEMBER`这类非确定性的指令
- The Lua PRNG in this mode is seeded randomly on every call - The Lua PRNG in this mode is seeded randomly on every call
除非通过server配置或默认启用了effect replication否则若需令脚本同步按照effect replication的方式进行同步必须在脚本执行任何write command之前执行如下lua命令
```lua
redis.replicate_commands()
```
在调用`redis.replicate_commands`方法时:
- 如果effect replication被启用那么返回值为true
- 如果在脚本已经执行过write command之后再调用该方法那么该方法返回值为false并且会使用normal whole script replication
该方法在Redis 7.0中被标记为废弃如果仍然调用它那么其返回值一直为true
#### Scripts with deterministic writes
从redis 5.0开始script replication默认情况下为effect-based而不是verbatim而在redis 7.0verbatim script replication的方式被完全移除。故而如下内容只针对版本低于7.0并且没有使用effect-based的script replication。
在使用verbatim script replication的情况下主要注意`only change the database in a deterministic way`。在redis实例执行script时一直到redis 5.0,默认都是通过`sending the script itself`方式来传播脚本的执行到replicas和AOF中的。在replica上被传递的脚本会被重新执行脚本对database的修改必须可重现。
通常,发送脚本本身占用的带宽要比发送`脚本产生的命令`占用的带宽要小cpu消耗也更小。
但是,`sending the script itself`这种replication方式并非对所有的场景都是可行的。
`verbatim scripts replication`要求脚本拥有如下属性:
-`arguments相同input data set相同`的场景下脚本必须产生相同的redis write commands
- 脚本执行的操作不能依赖于任何`hidden(non-explicit) information``state that may change as the script execution proceeds or between different executions of the script`
- 脚本的执行也不能依赖于任何io设备的外部输入
例如,`使用系统时间、调用返回随机值的redis命令、使用redis的随机数生成器`都会导致`scripts that will not evaluate consistently`
为了保证脚本的确定性行为redis做了如下处理
- lua不会export任何`访问系统时间或外部状态的命令`
- 如果脚本在调用`random command`(例如`RANDOMKEY, SRANDMEMBER, TIME`)之后,又调用了`Redis command able to alter the data set`那么redis将会`block the script with error`
- 上述即代表`read-only scripts that don't modify dataset can call those commands`
- `random command`并不代表使用随机数的command而是代表`non-deterministic command`例如TIME
- 在redis 4.0中,`例如SMEMBERS这类以随机顺序返回元素的命令`,在`被lua脚本调用时`表现出的行为不同在将数据返回给lua脚本时会根据字典序进行排序。
- 故而在redis 4.0环境下lua脚本中调用`redis.call("SMEMBERS", KEYS[1])`总是会按相同的顺序来返回Set中的元素。
- 但是从redis 5.0开始,又不会执行该排序,因为可以`effect replication`被设置为默认
- lua的伪随机数生成function `math.random`已经被redis修改故而在每次执行时都会使用相同的seed。
- 故而每次script执行时调用`match.random`总是会生成相同序列的数字
由上述描述可知redis修改了lua的伪随机数生成器(只在verbatim replication下成立)故而每次运行lua脚本时随机数生成器返回的数值序列都相同。
但是,仍然可以通过一定的技巧来生成随机数,示例如下所示:
```ruby
require 'rubygems'
require 'redis'
r = Redis.new
RandomPushScript = <<EOF
local i = tonumber(ARGV[1])
local res
while (i > 0) do
res = redis.call('LPUSH',KEYS[1],math.random())
i = i-1
end
return res
EOF
r.del(:mylist)
puts r.eval(RandomPushScript,[:mylist],[10,rand(2**32)])
```
每次上述程序运行resulting list都会拥有相同的元素
```redis-cli
redis> LRANGE mylist 0 -1
1) "0.74509509873814"
2) "0.87390407681181"
3) "0.36876626981831"
4) "0.6921941534114"
5) "0.7857992587545"
6) "0.57730350670279"
7) "0.87046522734243"
8) "0.09637165539729"
9) "0.74990198051087"
10) "0.17082803611217"
```
为了让脚本确定并且让其产生不同的random elements可以像脚本中添加额外参数用于对lua的伪随机数生成器进行seed。脚本示例如下所示
```ruby
RandomPushScript = <<EOF
local i = tonumber(ARGV[1])
local res
math.randomseed(tonumber(ARGV[2]))
while (i > 0) do
res = redis.call('LPUSH',KEYS[1],math.random())
i = i-1
end
return res
EOF
r.del(:mylist)
puts r.eval(RandomPushScript,1,:mylist,10,rand(2**32))
```
上述示例中,通过`math.randomseed`方法来将ruby生成的随机数作为了lua随机数生成器的种子从而产生了不同的数值序列。
当然,上述脚本的内容仍然是确定的,当传递的`ARGV[2]`相同时lua随机数生成器生成的数值序列仍然是固定的。
该seed是由client生成的作为参数被传递给脚本并且会作为参数被传播给replicas和AOF。这样能够确保同步给AOF和replicas的changes相同。