lua脚本为什么能保证原子性
Redis 处理客户端请求是基于单线程模型的( Redis 6.0 开始引入了多线程处理网络 IO,但命令执行仍然是单线程的)。这意味着,在任意时刻 Redis 只会执行一个命令或脚本。这种单线程特性确保了当 Redis 在执行一个 Lua 脚本时,不会有其他命令或脚本同时执行。
2. Lua 脚本被视为一个整体命令
当使用 EVAL
或 EVALSHA
命令执行 Lua 脚本时,Redis 将整个 Lua 脚本视为一个不可分割的命令。这意味着从开始执行 Lua 脚本直到脚本执行完毕这段时间内,Redis 不会处理任何其他命令。所有在 Lua 脚本中调用的 Redis 命令都会按照它们出现的顺序依次执行,且不会被其他客户端的命令中断。
3. 原子性和隔离性
由于上述原因,Lua 脚本在执行期间提供了类似于数据库事务中的原子性和隔离性:
- 原子性:要么整个脚本全部执行成功,要么完全不执行,不存在部分执行的情况。
- 隔离性:脚本执行过程中,其他客户端的操作不能影响到当前脚本的执行结果,反之亦然。
举例说明:
if redis.call('get', KEYS[1]) == ARGV[1] thenreturn redis.call('del', KEYS[1])
elsereturn 0
end为了确保只有锁的持有者才能删除锁(即比较传入的 requestId 和存储在 Redis 中的值是否匹配),我们需要连续执行两个操作:GET 和 DEL。如果这两个操作不是原子性的,那么在这两者之间可能会有其他客户端修改了数据,导致竞态条件的发生。但是,通过将这两个操作封装在一个 Lua 脚本中,Redis 确保这两个操作作为一个不可分割的整体来执行,从而避免了竞态条件的发生。
总结
Lua 脚本之所以能够在 Redis 中保证原子性,主要是因为 Redis 的单线程模型以及它对待 Lua 脚本的方式——即将 Lua 脚本作为单一、不可分割的命令来执行。这使得 Lua 脚本不仅可以在分布式环境中安全地执行复杂的逻辑,而且还可以保证这些逻辑以原子方式执行,不受并发操作的影响。