16-沙盒与随机数
6.4.2 沙盒与随机数
Redis脚本禁止使用Lua标准库中与文件或系统调用相关的函数,在脚本中只允许对Redis的数据进行处理。并且Redis还通过禁用脚本的全局变量的方式保证每个脚本都是相对隔离的,不会互相干扰。
使用沙盒不仅是为了保证服务器的安全性,而且还确保了脚本的执行结果只和脚本本身和执行时传递的参数有关,不依赖外界条件(如系统时间、系统中某个文件的内容、其他脚本执行结果等)。这是因为在执行复制和AOF持久化(复制和持久化会在第7章介绍)操作时记录的是脚本的内容而不是脚本调用的命令,所以必须保证在脚本内容和参数一样的前提下脚本的执行结果必须是一样的。
除了使用沙盒外,为了确保执行的结果可以重现,Redis 还对随机数和会产生随机结果的命令进行了特殊的处理。
对于随机数而言,Redis 替换了 math.random 和 math.randomseed 函数使得每次执行脚本时生成的随机数列都相同,如果希望获得不同的随机数序列,最简单的方法是由程序生成随机数并通过参数传递给脚本,或者采用更灵活的方法,即在程序中生成随机数传给脚本作为随机数种子(通过 math.randomseed(tonumber(ARGV[ 种子参数索引 ])) ),这样在脚本中再调用 math.random 产生的随机数就不同了(由随机数种子决定)。
对于会产生随机结果的命令如 SMEMBERS (因为集合类型是无序的)或 HKEYS (因为散列类型的字段也是无序的)等Redis会对结果按照字典顺序排序。内部是通过调用Lua标准库的 table.sort 函数实现的,代码与下面这段很相似:
function __redis__compare_helper(a,b)
if a == false then a = '' end
if b == false then b = '' end
return a < b
end
table.sort(result_array, __redis__compare_helper)
对于会产生随机结果但无法排序的命令(比如只会产生一个元素),Redis会在这类命令执行后将该脚本状态标记为 lua_random_dirty ,此后只允许调用只读命令,不允许修改数据库的值,否则会返回错误:“Write commands not allowed after non deterministic commands.”属于此类的Redis命令有 SPOP , SRANDMEMBER , RANDOMKEY 和 TIME 。