一、redis pipeline
redis pipeline 是一个客户端提供的机制,而不是服务端提供的;

二、redis事务
事务:用户定义一系列数据库操作,这些操作视为一个完整的逻辑处理工作单元,要么全部执行,
MULTI开启事务begin / start transactionEXEC提交事务commitDISCARD取消事务rollbackWATCH检测 key 的变动,若在事务执行中, key 变动则取消事务;在事务开启前调用,乐观锁实现( cas ) ;若被取消则事务返回 nil ;
lua 脚本
lua脚本实现原子性;
redis 中加载了一个 lua 虚拟机;用来执行 redis lua 脚本;redis lua 脚本的执行是原子性的;当某 个脚本正在执行的时候,不会有其他命令或者脚本被执行;
一、从文件加载 Lua 脚本(高效批量加载)
命令
cat script.lua | redis-cli script load --pipe作用
将本地 Lua 脚本文件通过标准输入(
cat)传递给redis-cli,利用--pipe选项批量加载脚本到 Redis 缓存,避免逐行传输的网络开销。实例
假设脚本文件
test.lua内容为:-- 功能:获取键对应的值并返回 local value = redis.call('GET', KEYS[1]) return value执行加载:
cat test.lua | redis-cli -h 127.0.0.1 -p 6379 script load --pipe输出
SHA1哈希值(如): "a1b2c3d4e5f67890abcdef1234567890abcdef12"注意
--pipe用于批量执行命令,此处仅加载单个脚本,实际生产中可批量加载多个脚本。- 加载后脚本存储在 Redis 服务器的脚本缓存中,后续可通过
EVALSHA命令调用,减少网络传输量。二、加载 Lua 脚本字符串(手动生成 SHA1)
命令
redis-cli script load "脚本内容"作用
直接传入 Lua 脚本字符串,Redis 会执行语法检查并生成 SHA1 哈希值,用于后续通过
EVALSHA调用,避免重复传输完整脚本。实例
# 加载一个简单脚本(返回第一个传入的键名) > script load 'local val = KEYS[1]; return val'输出
"b8059ba43af6ffe8bed3db65bac35d452f8115d8" # SHA1 哈希值三、检查脚本是否存在于缓存(避免重复加载)
命令
redis-cli script exists SHA1哈希值 [SHA1哈希值 ...]作用
检查指定的 SHA1 哈希值对应的脚本是否已加载到 Redis 缓存中,返回 1(存在)或 0(不存在)。
实例
# 检查之前生成的 SHA1 是否存在 > script exists "b8059ba43af6ffe8bed3db65bac35d452f8115d8"输出
1) (integer) 1 # 表示存在应用场景
- 在调用
EVALSHA前先执行script exists,若脚本不存在则先用script load加载,避免EVALSHA报错(NOSCRIPT错误)。四、清除所有脚本缓存(释放内存)
命令
redis-cli script flush作用
删除 Redis 服务器中缓存的所有 Lua 脚本,释放相关内存。
实例
> script flush输出
OK # 表示清除成功注意
- 清除后,所有通过
script load或EVAL加载的脚本都会被删除,后续调用EVALSHA会报错,需重新加载。- 生产环境中慎用,避免影响正在运行的依赖脚本的业务。
五、终止长时间运行的脚本(处理阻塞)
命令
redis-cli script kill作用
杀死当前正在执行的、运行时间过长的 Lua 脚本(仅能终止 非阻塞写操作 的脚本,即未执行过
WRITE命令的脚本)。实例
# 假设存在一个死循环脚本,尝试终止 > script kill输出
(error) NOTBUSY No scripts in execution right now. # 无正在执行的脚本时提示注意
- 若脚本已执行过写操作(如
SET、DEL),script kill无法终止,只能通过重启 Redis 实例强制停止(可能导致数据不一致,需谨慎)。- Redis 的单线程模型决定了脚本会阻塞其他命令,应避免编写耗时过长的脚本(建议控制在毫秒级)。
六、核心命令对比表
命令 功能描述 典型场景 输出示例 script load加载脚本,生成 SHA1,存入缓存 预加载脚本,后续通过 EVALSHA调用以减少网络传输SHA1 哈希值 script exists检查脚本是否在缓存中 调用 EVALSHA前判断是否需要重新加载脚本1(存在)或0(不存在)script flush清除所有脚本缓存 重置环境、释放内存或脚本更新后强制重新加载 OKscript kill终止正在执行的脚本(仅非阻塞写脚本) 处理脚本死循环或长时间阻塞 OK(成功终止)或错误提示七、最佳实践
预加载脚本:
在服务启动时通过script load加载所有需要的脚本,后续用EVALSHA调用(比EVAL更高效,无需传输完整脚本)。# 调用示例:EVALSHA SHA1 键数量 键1 键2 ... 参数1 参数2 ... redis-cli evalsha b8059ba43af6ffe8bed3db65bac35d452f8115d8 1 mykey "arg1"处理
NOSCRIPT错误:
若EVALSHA因脚本未加载报错,捕获错误后重新调用script load加载脚本,再重试EVALSHA。避免长耗时脚本:
确保脚本逻辑简洁,必要时拆分复杂操作,防止阻塞 Redis 事件循环。脚本调试:
先用redis-cli eval直接执行脚本调试(如eval "脚本内容" 0),确认无误后再加载到缓存。
三、ACID特性分析
A :原子性;事务是一个不可分割的工作单位,事务中的操作要么全部成功,要么全部失败; redis不支持回滚;即使事务队列中的某个命令在执行期间出现了错误,整个事务也会继续执行下去,直到将事务队列中的所有命令都执行完毕为止。C :一致性;事务的前后,所有的数据都保持一个一致的状态,不能违反数据的一致性检测;这里的一致性是指预期的一致性而不是异常后的一致性;所以 redis 也不满足;这个争议很大: redis 能确保事务执行前后的数据的完整约束;但是并不满足业务功能上的一致性;比如转账功能,一个扣钱一个加钱;可能出现扣钱执行错误,加钱执行正确,那么最终还是会加钱成功;系统凭空多了钱;I :隔离性;各个事务之间互相影响的程度; redis 是单线程执行,天然具备隔离性;D :持久性; redis 只有在 aof 持久化策略的时候,并且需要在 redis.conf 中appendfsync=always 才具备持久性;实际项目中几乎不会使用 aof 持久化策略;面试时候回答: lua 脚本满足原子性和隔离性;一致性和持久性不满足;
四、redis发布订阅
为了支持消息的多播机制,redis 引入了发布订阅模块;
# 订阅频道
subscribe 频道
# 订阅模式频道
psubscribe 频道
# 取消订阅频道
unsubscribe 频道
# 取消订阅模式频道
punsubscribe 频道
# 发布具体频道或模式频道的内容
publish 频道 内容
# 客户端收到具体频道内容
message 具体频道 内容
# 客户端收到模式频道内容
pmessage 模式频道 具体频道 内容 五、redis异步连接
redis协议图
异步连接
同步连接方案采用阻塞 io 来实现;优点是代码书写是同步的,业务逻辑没有割裂;缺点是阻塞当 前线程,直至 redis 返回结果;通常用多个线程来实现线程池来解决效率问题;
实现方案
详情代码见:https://github.com/jerry123456123456/redis.git 中的redis-master
0voice · GitHub
