以下是 Redis 中 脚本(Scripting) 的详细讲解,适用于需要在 Redis 中执行复杂操作、批量处理、原子操作等场景,常用的脚本语言为 Lua。


📚 目录

  1. 脚本简介
  2. 核心命令说明
  3. Lua 脚本的执行与原子性
  4. 典型使用场景
  5. 脚本的优化与安全性
  6. 参考资料

🧾 脚本简介

Redis 脚本主要是通过 Lua 语言编写,并通过 Redis 提供的命令执行。这种方式能确保脚本内的多个命令在执行时具有原子性,避免了多次往返通信的延迟,并提高了效率。

Lua 脚本的特点:

  • 原子性执行:Redis 脚本中的所有操作是原子执行的,其他客户端的请求会在脚本执行完之前被阻塞。
  • 减少网络延迟:多个命令可以在一个脚本内执行,避免了频繁的网络往返请求。
  • 安全性:Lua 脚本只能访问 Redis 提供的基本命令,不能访问外部资源(如文件系统、网络等)。

🔧 核心命令说明

命令描述
EVAL script numkeys key [key ...] arg [arg ...]执行 Lua 脚本
EVALSHA sha1 numkeys key [key ...] arg [arg ...]使用已加载的脚本 SHA1 执行 Lua 脚本
SCRIPT LOAD script将 Lua 脚本加载到 Redis 中,返回 SHA1 值
SCRIPT EXISTS sha1 [sha1 ...]检查脚本是否已加载
SCRIPT FLUSH清空 Redis 中所有加载的脚本

EVAL 命令示例

EVAL "return redis.call('GET', KEYS[1])" 1 mykey

  • EVAL 命令会执行 Lua 脚本,KEYS 是传入的键(最多支持 32 个键),ARGV 是传入的参数。
  • 1 代表有 1 个键 mykey

⚡ Lua 脚本的执行与原子性

Redis 在执行 Lua 脚本时会 阻塞其他客户端请求,直到脚本执行完毕。这确保了脚本中的所有命令按顺序执行,且具有原子性。

执行过程:

  1. Redis 接收到 EVALEVALSHA 命令。
  2. 脚本的所有命令在 Redis 内部执行,期间不会受到其他命令的干扰。
  3. 脚本执行完成后,返回执行结果。

示例:简单计数器

EVAL "local current = redis.call('GET', KEYS[1]) if current then return current else return 0 end" 1 counter

  • 这个脚本获取键 counter 的值,如果存在则返回值,如果不存在则返回 0。

🧪 典型使用场景

✅ 分布式锁

EVAL "if redis.call('setnx', KEYS[1], ARGV[1]) == 1 then return 1 else return 0 end" 1 lock_key lock_value

  • 使用 SETNX 获取锁,若返回 1 表示成功获取锁,若返回 0 表示锁已被其他客户端持有。

✅ 原子性计数器

EVAL "local value = redis.call('INCR', KEYS[1]) return value" 1 counter

  • 原子性地递增 counter 键的值,确保操作不会被其他客户端打断。

✅ 批量操作

EVAL "for i=1, #KEYS do redis.call('SET', KEYS[i], ARGV[i]) end return 'OK'" 3 key1 key2 key3 value1 value2 value3

  • 批量设置多个键的值,避免多次 SET 命令带来的延迟。

🔒 脚本的优化与安全性

脚本的优化

  • 减少循环:尽量减少 Lua 脚本中的循环次数,因为每次循环都会阻塞其他客户端的请求。
  • 避免长时间执行:脚本应尽量简短,以提高 Redis 的响应能力。
  • 利用 Redis 提供的原子性操作:利用 Redis 内置命令如 SETNXINCRDECRGETSET 等,避免手动实现复杂逻辑。

安全性

  • 资源隔离:Redis 脚本的执行环境是 Lua,无法访问外部网络或文件系统,因此可以避免外部攻击。
  • 防止恶意脚本:通过 EVALSHA 机制确保脚本的安全性,只有经过验证并安全的脚本会被执行。

📎 参考资料


如果你希望我帮助设计一个复杂的 Lua 脚本,比如处理 批量操作、分布式锁、原子性计数器、数据库一致性保障、或实时数据处理,可以随时告诉我,我将为你提供详细的脚本示例和实现方案!