redis script 不具备 all or nothing
特性的,可能是 crud 程序猿会遇到,这可能是思维惯性导致的。
redis.call('SET', 'key1', 'value1');
local a = b;
redis.call('SET', 'key2', 'value2');
有以上的脚本,redis 在执行 local a = b;
这一行时,就会报错如下的错误:
(error) ERR Error running script (call to f_71007e955106f406b23cfaba7647eec1081fda7d):
@enable_strict_lua:15: user_script:1: Script attempted to access nonexistent global variable 'b'
然后后续的代码便不再执行,对于长期习惯于丢异常就会回滚修改的 crud 程序员来说,key1
和 key2
的值肯定没有设置成功。然而事实是,上诉代码是一半成功(成功设置 key1
),一半根本没有执行(没有执行到 key2
的位置)。
简而言之,redis script 的原子性特性只是指 redis 只使用一个 lua 解释器执行 script,且是单线程执行 script。但是 script 执行中途报错,是不会将修改回滚的,回滚特性应该属于事务,而 redis 其实是没有严格的事务特性的,redis script 是没有 all or nothing
的特性。但是单条 redis 命令是原子性的,且是 all or nothing
的。
这里贴出 redis 官方的对 script 原子性的描述。
Redis uses the same Lua interpreter to run all the commands. Also Redis guarantees that a script is executed in an atomic way: no other script or Redis command will be executed while a script is being executed. This semantic is similar to the one of MULTI / EXEC. From the point of view of all the other clients the effects of a script are either still not visible or already completed.
However this also means that executing slow scripts is not a good idea. It is not hard to create fast scripts, as the script overhead is very low, but if you are going to use slow scripts you should be aware that while the script is running no other client can execute commands.
详细参考 https://redis.io/commands/eval#atomicity-of-scripts
这个 stackoverflow 上也指出 redis script 的原子性更像是隔离性。error-in-redis-lua-script-after-successful-write-command。
What they mean by “atomic” is actually closer to isolation than atomicity: Lua scripts are never concurrent with other Lua scripts or commands.