Docker 环境下 Redis Lua 脚本部署与执行

文章目录

最近在做Redis分布式锁、接口限流的时候,用到了Lua脚本,毕竟Redis单线程执行Lua,能保证操作的原子性,避免并发问题,但在实际落地时却发现一个问题:项目部署在 Docker 环境下时,Redis 的 Lua 脚本到底该怎么放、怎么执行才最稳妥。

先提前说个核心结论 :Redis的Lua脚本不需要编译、不需要安装,核心就是"让Redis能找到脚本文件",再用简单的命令执行就行。Docker部署Redis Lua,优先用「目录挂载」,测试用「直接复制文件」

一、Redis Lua脚本执行的本质

很多人会误以为Lua脚本需要像部署应用一样,安装依赖、启动服务------其实完全不用!

Redis本身就内置了Lua解释器 ,只要Redis能读取到Lua脚本文件,或者你直接把脚本内容传给Redis,它就能直接执行。咱们在Docker里要做的,本质就是「让Redis容器能访问到Lua脚本」,剩下的就是Redis的原生命令操作。

另外补充两个关键知识点:

  • Redis执行Lua脚本有两种方式:直接传脚本内容(EVAL命令)、缓存脚本后用SHA值调用(SCRIPT LOAD + EVALSHA,性能更高,生产推荐);
  • Docker里的Redis容器,默认没有宿主机的文件权限,所以不能直接读取宿主机的脚本,要么挂载目录,要么把脚本复制进容器。

二、生产环境首选:目录挂载(最规范,方便维护)

这种方式是生产环境最推荐的,核心逻辑是「把宿主机的一个文件夹,挂载到Redis容器里 」,这样我们在宿主机修改、新增Lua脚本,容器里就能实时同步,不用每次都复制文件,后期维护特别方便。

步骤1:启动Redis容器,挂载目录

先停止并删除旧的Redis容器(如果有的话),然后执行以下命令,启动一个新的Redis容器,并挂载宿主机目录:

bash 复制代码
# 先创建宿主机存放Lua脚本的目录(自定义路径,我这里用/home/redis/lua)
mkdir -p /home/redis/lua
chmod 777 /home/redis/lua  # 给权限,避免容器访问不到

# 启动Redis容器,挂载目录
docker run -d \
  --name wzxgRedis  # 容器名字,自定义
  -p 6379:6379    # 端口映射,宿主机6379对应容器6379
  -v /home/redis/lua:/lua  # 挂载:宿主机路径:/容器内路径
  redis:latest redis-server  # 启动Redis服务

注:-v /home/redis/lua:/lua 这一句,就是把宿主机的/home/redis/lua文件夹,和容器里的/lua文件夹关联起来,相当于"共享文件夹",宿主机放脚本,容器里就能直接看到。

步骤2:在宿主机编写Lua脚本

在宿主机的挂载目录里写脚本,容器里会自动同步,不用再手动复制。比如写一个简单的"自增计数"脚本。

bash 复制代码
# 进入宿主机的Lua脚本目录
cd /home/redis/lua

# 创建并编辑脚本(也可以用记事本编辑后上传)
vim wzxg.lua

脚本内容

lua 复制代码
-- KEYS[1]:传入的第一个key(后续执行时指定)
-- redis.call():调用Redis原生命令
local key = KEYS[1]
-- 对key进行自增,返回自增后的值
local count = redis.call("INCR", key)
return count

保存退出vim(按ESC,输入:wq,回车即可)。这时候,容器里的/lua目录下,wzxg.lua脚本,因为我们挂载了目录。

步骤3:进入容器,执行Lua脚本

脚本准备好了,接下来进入Redis容器,用Redis命令执行脚本,两种方式任选,优先推荐方式2(性能更高)。

bash 复制代码
# 进入Redis容器的redis-cli客户端
docker exec -it wzxgRedis redis-cli
方式1:直接加载文件执行(测试用,简单)

直接读取容器内的脚本文件,一次性执行,适合测试脚本是否能正常运行:

redis 复制代码
EVAL "$(cat /lua/wzxg.lua)" 1 mycounter

拆解一下这个命令:

  • EVAL:Redis执行Lua脚本的核心命令;

  • "$(cat /lua/test.lua)":读取容器内/lua目录下的test.lua脚本内容;

  • 1:表示后续传入的KEYS数量(这里我们只传一个key,就是mycounter);

  • mycounter:传入的KEYS[1],也就是脚本里的key变量。

执行后,会返回1(第一次自增),再执行一次返回2,说明脚本执行成功!

方式2:缓存脚本,用SHA值调用(生产推荐)

如果脚本需要频繁执行,每次都传脚本内容会浪费带宽,这时候可以把脚本缓存到Redis里,Redis会返回一个唯一的SHA值,后续直接用SHA值调用即可,性能更高。

redis 复制代码
# 1. 缓存脚本,返回SHA值
SCRIPT LOAD "$(cat /lua/wzxg.lua)"

SHA 值是 Redis 对 Lua 脚本内容进行哈希计算后,生成的一串唯一字符串(比如 54cfc1234567890abcdef...),相当于这个脚本的 "专属标识"------ 只要脚本内容不变,SHA 值就不变;脚本一改,SHA 值就会变。

redis 复制代码
# 2. 用SHA值执行脚本,参数和EVAL一样
EVALSHA 54cfc1234567890abcdef 1 mycounter

效果和方式1一样,但后续每次执行,只需要传SHA值,不用再传整个脚本,适合生产环境频繁调用的场景。

三、测试用简易方案:直接复制脚本进容器

如果只是临时测试脚本,不想挂载目录,也可以直接把宿主机的脚本复制进Redis容器,步骤更简单,适合快速测试。

bash 复制代码
# 1. 宿主机创建脚本(比如还是wzxg.lua,路径随意,比如~/wzxg.lua)
# 2. 复制脚本到Redis容器的/lua目录(没有这个目录会自动创建)
docker cp ~/wzxg.lua wzxgRedis:/lua/wzxg.lua

# 3. 进入redis-cli执行(和上面步骤3一样)
docker exec -it wzxgRedis redis-cli
EVAL "$(cat /lua/wzxg.lua)" 1 mycounter

注意:这种方式,宿主机修改脚本后,不会同步到容器里,需要重新执行docker cp命令复制,所以只适合测试,不适合生产环境。

四、踩过的坑

  1. 挂载目录时,一定要给宿主机目录权限(chmod 777),否则容器里会提示"权限不足",读不到脚本;

  2. 执行脚本时,KEYS的数量一定要写对(EVAL后面的数字),比如传2个key,就写2,否则会报错;

  3. 生产环境一律用「SCRIPT LOAD + EVALSHA」,避免每次传脚本浪费带宽;

  4. Lua脚本里只能用Redis支持的命令(redis.call()),不能写复杂的循环和逻辑,因为Redis是单线程,脚本执行时间过长会阻塞Redis;

  5. 如果脚本里有中文注释,保存时一定要用UTF-8编码,否则会出现乱码,导致脚本执行失败。

相关推荐
疯狂成瘾者2 小时前
Redis 实用学习清单
redis·学习
七夜zippoe2 小时前
消息队列选型:Kafka vs RabbitMQ vs Redis 深度对比
redis·python·kafka·消息队列·rabbitmq
iMingzhen2 小时前
不想引入 Redis,我用一张 SQLite 表实现了消息队列
数据库·redis·ai·sqlite
Curvatureflight2 小时前
Redis实战:缓存设计与高频场景全解析
数据库·redis·缓存
万里不留行2 小时前
解决ubuntu docker拉取环境失败问题
linux·ubuntu·docker
无名-CODING2 小时前
从零开始!Vue3+SpringBoot前后端分离项目Docker部署实战(下):Vue前端Nginx反代与致命坑点盘点
前端·spring boot·docker
我真会写代码2 小时前
从入门到精通:Redis实战指南,解锁高性能缓存核心能力
数据库·redis·缓存
DJ斯特拉2 小时前
Docker基本使用
运维·docker·容器
无名-CODING2 小时前
从零开始!Vue3+SpringBoot前后端分离项目Docker部署实战(中):Spring Boot后端与Docker Compose串联
spring boot·后端·docker