欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 财经 > 产业 > redis协议与异步方式

redis协议与异步方式

2025/5/12 13:25:17 来源:https://blog.csdn.net/m0_74186706/article/details/147874524  浏览:    关键词:redis协议与异步方式

一、redis pipeline

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

        注意:pipeline 不具备事务性;
        目的:节约网络传输时间;
        通过一次发送多次请求命令,从而减少网络传输的时间。

 

二、redis事务

        事务:用户定义一系列数据库操作,这些操作视为一个完整的逻辑处理工作单元,要么全部执行,

        要么全部不执行,是不可分割的工作单元。
        MULTI 开启事务,事务执行过程中,单个命令是入队列操作,直到调用 EXEC 才会一起执行;
        乐观锁实现,所以失败需要重试,增加业务逻辑的复杂度;
MULTI
开启事务
begin / start transaction
EXEC
提交事务
commit
DISCARD
取消事务
rollback
WATCH
检测 key 的变动,若在事务执行中, key 变动则取消事务;在事务开启前调用,乐观锁实现
cas ;
若被取消则事务返回 nil ;

lua 脚本 

        lua脚本实现原子性;

        redis 中加载了一个 lua 虚拟机;用来执行 redis lua 脚本;redis lua 脚本的执行是原子性的;当某 个脚本正在执行的时候,不会有其他命令或者脚本被执行;

        lua 脚本当中的命令会直接修改数据状态;
        lua 脚本 mysql 存储区别: MySQL 存储过程不具备事务性,所以也不具备原子性;
        注意:如果项目中使用了 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.  # 无正在执行的脚本时提示
注意
  • 若脚本已执行过写操作(如 SETDEL),script kill 无法终止,只能通过重启 Redis 实例强制停止(可能导致数据不一致,需谨慎)。
  • Redis 的单线程模型决定了脚本会阻塞其他命令,应避免编写耗时过长的脚本(建议控制在毫秒级)。

六、核心命令对比表

命令功能描述典型场景输出示例
script load加载脚本,生成 SHA1,存入缓存预加载脚本,后续通过 EVALSHA 调用以减少网络传输SHA1 哈希值
script exists检查脚本是否在缓存中调用 EVALSHA 前判断是否需要重新加载脚本1(存在)或 0(不存在)
script flush清除所有脚本缓存重置环境、释放内存或脚本更新后强制重新加载OK
script kill终止正在执行的脚本(仅非阻塞写脚本)处理脚本死循环或长时间阻塞OK(成功终止)或错误提示

七、最佳实践

  1. 预加载脚本
    在服务启动时通过 script load 加载所有需要的脚本,后续用 EVALSHA 调用(比 EVAL 更高效,无需传输完整脚本)。

    # 调用示例:EVALSHA SHA1 键数量 键1 键2 ... 参数1 参数2 ...
    redis-cli evalsha b8059ba43af6ffe8bed3db65bac35d452f8115d8 1 mykey "arg1"
    
  2. 处理 NOSCRIPT 错误
    若 EVALSHA 因脚本未加载报错,捕获错误后重新调用 script load 加载脚本,再重试 EVALSHA

  3. 避免长耗时脚本
    确保脚本逻辑简洁,必要时拆分复杂操作,防止阻塞 Redis 事件循环。

  4. 脚本调试
    先用 redis-cli eval 直接执行脚本调试(如 eval "脚本内容" 0),确认无误后再加载到缓存。

 

三、ACID特性分析        

        A :原子性;事务是一个不可分割的工作单位,事务中的操作要么全部成功,要么全部失败; redis
不支持回滚;即使事务队列中的某个命令在执行期间出现了错误,整个事务也会继续执行下去,直
到将事务队列中的所有命令都执行完毕为止。
        C :一致性;事务的前后,所有的数据都保持一个一致的状态,不能违反数据的一致性检测;这里
的一致性是指预期的一致性而不是异常后的一致性;所以 redis 也不满足;这个争议很大: redis
确保事务执行前后的数据的完整约束;但是并不满足业务功能上的一致性;比如转账功能,一个扣
钱一个加钱;可能出现扣钱执行错误,加钱执行正确,那么最终还是会加钱成功;系统凭空多了钱;

        I :隔离性;各个事务之间互相影响的程度; redis 是单线程执行,天然具备隔离性;
        D :持久性; redis 只有在 aof 持久化策略的时候,并且需要在 redis.conf
appendfsync=always 才具备持久性;实际项目中几乎不会使用 aof 持久化策略;
面试时候回答: lua 脚本满足原子性和隔离性;一致性和持久性不满足;

 

四、redis发布订阅

        为了支持消息的多播机制,redis 引入了发布订阅模块;

        消息不一定可达;分布式消息队列; stream 的方式确保一定可达;
# 订阅频道
subscribe 频道
# 订阅模式频道
psubscribe 频道
# 取消订阅频道
unsubscribe 频道
# 取消订阅模式频道
punsubscribe 频道
# 发布具体频道或模式频道的内容
publish 频道 内容
# 客户端收到具体频道内容
message 具体频道 内容
# 客户端收到模式频道内容
pmessage 模式频道 具体频道 内容
应用
发布订阅功能一般要区别命令连接重新开启一个连接;因为命令连接严格遵循请求回应模式;而
pubsub 能收到 redis 主动推送的内容;所以实际项目中如果支持 pubsub 的话,需要 另开一条连接
用于处理发布订阅;
缺点
发布订阅的生产者传递过来一个消息, redis 会直接找到相应的消费者并传递过去;假如没有消费
者,消息直接丢弃;假如开始有 2 个消费者,一个消费者突然挂掉了,另外一个消费者依然能收到
消息,但是如果刚挂掉的消费者重新连上后,在断开连接期间的消息对于该消费者来说彻底丢失
了;
另外, redis 停机重启, pubsub 的消息是不会持久化的,所有的消息被直接丢弃;

五、redis异步连接

redis协议图

 异步连接

        同步连接方案采用阻塞 io 来实现;优点是代码书写是同步的,业务逻辑没有割裂;缺点是阻塞当 前线程,直至 redis 返回结果;通常用多个线程来实现线程池来解决效率问题;

        异步连接方案采用非阻塞 io 来实现;优点是没有阻塞当前线程, redis 没有返回,依然可以往 redis 发送命令;缺点是代码书写是异步的(回调函数),业务逻辑割裂,可以通过协程解决
openresty skynet );配合 redis6.0 以后的 io 多线程(前提是有大量并发请求),异步连接
池,能更好解决应用层的数据访问性能;

实现方案

        详情代码见:https://github.com/jerry123456123456/redis.git 中的redis-master

0voice · GitHub

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词