Redis协议与异步方式

)

01.慢查询

​ 慢查询日志就是Redis的"行车记录仪"。建议线上环境至少保留100条记录,阈值按业务调整。它的原理很简单:系统会记录每条命令的执行时间,一旦超过设定的阈值,就把这条命令的相关信息(eg:发生时间、耗时、具体操作内容)记下来。Redis 也支持类似的功能,方便排查那些"拖后腿"的慢操作。

  1. 发送命令
  2. 命令排队
  3. 命令执行
  4. 返回结果

​ Redis的慢查询只统计命令在Redis内部执行的时间(就是步骤3),不包含网络传输和排队等待的时间。所以即使慢查询日志干干净净,客户端照样可能超时。

复制代码
为了解决预设阀值怎么设置以及慢查询记录存放在哪的问题,可以使用一下配置进行解决。
  1. slowlog-log-slower-than

    • 设定"慢"的门槛,单位是微秒(1秒=100万微秒)
    • 默认10000微秒(10毫秒)------超过这个时间的命令才会被记录
    • 设成0:记录所有命令(慎用!)
    • 设成负数:直接关闭慢查询功能
  2. slowlog-max-len

    • 控制慢查询日志最多存多少条(本质是个内存列表
    • 新记录进来时,老记录自动弹出(先进先出)
    • 比如设成100,第101条进来时,最早那条就被踢掉

02.Redis pipeline

​ 已知传统模式是请求---效应,任意两个请求之间没有关联。Pipeline 是 Redis 的批处理技术,一次发送多个命令,Redis 按序执行并按序返回响应。减少网络往返时间(RTT),提升批量操作吞吐量。

​ Redis 客户端执行一条命令的四个阶段(发送 → 排队 → 执行 → 返回

​ Pipeline纯客户端行为,Redis 服务端无感知。类似 HTTP/1.1 的"请求管道化"(Pipelining),解决"请求-响应串行阻塞"问题。不保证原子性和事务语义,仅用于性能优化。


03.事务与Lua

3.1事务

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

​ Redis 命令遵循 请求-响应 模型------每个命令需等待服务器确认后才能发下一个。而事务通过 MULTI/EXEC 将多次网络往返压缩为两次交互 (入队 + 执行),大幅提升效率。 事务存在并发连接 时,需保证一组操作的逻辑完整性。eg:对 counter 执行 "读取 → 计算翻倍 → 写回",避免中间被其他客户端干扰。

​ 在下面的例子中,开启事务执行后未提交事务,后被另外一个连接对数据进行了修改,造成事务内部的数据key变动,事务被取消。

Redis 事务通过 队列 + 原子执行 实现:

  1. MULTI 开启事务 :后续命令(如多个 SET)不会立即执行,而是入队缓存
  2. EXEC 触发执行:一次性原子化执行队列中所有命令(单线程特性保证执行过程不被其他请求打断);
  3. DISCARD取消事务:清空已入队的命令,不执行任何操作。

WATCH机制

  • 通过 WATCH 监控 key ,若事务入队后、EXEC 前该 key 被其他客户端修改,则 EXEC 会自动丢弃整个队列,避免脏写。

下面每一条指令都是请求-响应的双向连接。一来一回的。


3.1ACID

原子性

  • 事务是一个不可分割的工作单位,事务中的操作要么全部成功,要么全部失败;redis不支持回滚;即使事务队列中的某个命令在执行期间出现了错误,整个事务也会继续执行下去,直到将事务队列中的所有命令都执行完毕为止。

一致性

  • 事务的前后,所有的数据都保持一个一致的状态,不能违反数据的一致性检测;这里的一致性是指预期的一致性而不是异常后的一致性;所以redis 也不满足;这个争议很大:redis 能确保事务执行前后的数据的完整约束;但是并不满足业务功能上的一致性;比如转账功能,一个扣钱一个加钱;可能出现扣钱执行错误,加钱执行正确,那么最终还是会加钱成功;系统凭空多了钱;
  • 完整约束的一致
  • 用户逻辑的一致
    • 是指我操作之间不能被其他操作影响

隔离性

  • 各个事务之间互相影响的程度;redis是单线程执行,天然具备隔离性;

持久性

  • redis 只有在laof持久化策略的时候,并且需要在redis.conf 中appendfsync=always才具备持久性;实际项目中几乎不会使用aof持久化策略;
3.2Lua脚本

​ Redis 内置 Lua 虚拟机,脚本执行具有原子性:一旦开始运行,整个脚本会独占 Redis 单线程,期间不会插入任何其他命令或脚本,所有操作连续完成。MySQL 不具备事务原子性。lua脚本满足原子性和隔离性;一致性和持久性不满足;

  • 脚本中的命令直接修改数据状态 ,无需额外事务(使用 Lua 后,无需 MULTI/EXEC 等事务命令)
  • 通过 EVALEVALSHA 触发执行,避免网络往返开销
bash 复制代码
# 从文件加载脚本(生成 SHA1 校验码)
cat test.lua | redis-cli --eval /dev/stdin

# 手动加载脚本字符串
> SCRIPT LOAD 'return KEYS[1]'
"b8059ba43af6ffe8bed3db65bac35d452f8115d8"

# 检查脚本是否已缓存
> SCRIPT EXISTS "b8059ba43af6ffe8bed3db65bac35d452f8115d8"
1) (integer) 1  # 1=存在,0=不存在

# 清理所有缓存脚本(重启 Redis 也会自动清空)
> SCRIPT FLUSH
OK

# 终止卡死的脚本(如死循环)
> SCRIPT KILL
(error) NOBUSY  # 无运行中脚本时返回此提示

​ 通过第一次发送的脚本生成的hash值,这个值就是未来执行lua脚本,通过hash值找到脚本索引找到脚本进行执行。

shell 复制代码
# 1. 开发测试:直接执行脚本
> EVAL "return {KEYS[1], ARGV[1]}" 1 user:1001 "profile"

# 2. 生产环境:先加载脚本获取 SHA1
> SCRIPT LOAD "return redis.call('GET', KEYS[1])"
"767e31a4d5a0f9a3d3d3d3d3d3d3d3d3d3d3d3d3"

04.发布订阅

​ Redis提供了基于"发布/订阅"模式的消息机制,此种模式下,消息发布者(可以是web服务器也可以是普通服务器?)和订阅者不进行直接通信,发布者客户端向指定的频道(channel)发布消息,订阅该频道的每个客户端都可以收到该消息。

shell 复制代码
#订阅频道
subscribe频道
#订阅模式频道
psubscrihe 频道人
#取消订阅频道
unsubscribe 频道
#取消订阅模式频道
punsubscribe 频道
#发布具体频道或模式频道的内容
publish频道 内容
#客户端收到具体频道内容
message 具体频道 内容
#客户端收到模式频道内容
pmessage模式频道具体频道内容

​ 发布订阅模式基于 TCP 长连接实现,应用此功能一般要区别命令连接重新开启一个连接;因为命令连接严格遵循请求回应模式;而pub/sub能收到redis主动推送的内容;所以实际项目中如果支持pub/sub的话,需要另开一条连接用于处理发布订阅。

​ 发布订阅的生产者传递过来一个消息,redis会直接找到相应的消费者并传递过去;假如没有消费者,消息直接丢弃;假如开始有2个消费者,一个消费者突然挂掉了,另外一个消费者依然能收到消息,但是如果刚挂掉的消费者重新连上后,在断开连接期间的消息对于该消费者来说彻底丢失了;

另外,redlis停机重启,pub/sub的消息是不会持久化的,所有的消息被直接丢弃;

适用场景:适用于容忍消息丢失的实时广播场景:

  • 实时通知(如在线状态变更)

  • 日志收集(非关键日志)

  • 配置热更新广播

  • 不适用于 需要可靠投递、消息追溯或强一致性的业务(如订单、支付、审计日志),此类场景应考虑 Redis Stream 或专业消息队列(如 Kafka、RabbitMQ)。


05.Reactor异步连接拆解
1. 与Redis建立连接
socket(fd,&addr,&len) 非阻塞I/O

​ 1.创建socket,设置文件描述符为非阻塞模式

​ 2.调用connect,当返回-1且errno为EINPROGRESS时表示连接正在进行中

​ 3.将文件描述符注册到epoll的写事件

​ 4.当连接建立成功后,epoll会触发写事件,此时注销写事件

2. 向Redis发送数据
使用Redis协议对命令进行编码
  1. 通过TCP发送数据

    • 调用int n = write(fd, buf, sz),若返回n < sz&&n != -1; n = -1且errno = EWOULDBLOCK,说明fd对应的发送缓冲区已满

    • 此时注册写事件,当写事件触发时调用write(fd, buf, sz),继续发送剩余数据

    • 数据全部发送完毕后,注销写事件并注册读事件

3. 读取Redis的返回
  1. 通过tcp接收数据分割数据包
  2. 使用redis协议解密
  • 当读事件触发时,int n = read(fd , buf , sz ),通过TCP接收数据
  • 根据Redis协议格式分割 数据包
  • 对协议数据进行解码,获取最终响应结果

c语言通过typedef,C++通过继承获取evenr_tctx两个对象

相关推荐
纪莫4 小时前
技术面:MySQL篇(InnoDB事务执行过程、事务隔离级别、事务并发异常)
数据库·java面试⑧股
Nerd Nirvana4 小时前
数据库模型全景:从原理到实践的系统性指南
数据库·oracle·电力行业
SelectDB4 小时前
从 Greenplum 到 Doris:集群缩减 2/3、年省数百万,度小满构建超大规模数据分析平台经验
数据库·数据分析·apache
fengbizhe4 小时前
bootstrapTable转DataTables,并给有着tfoot的DataTables加滚动条
javascript·bootstrap
alonewolf_994 小时前
MySQL索引优化实战二:分页、关联查询与Count优化深度解析
数据库·mysql
oMcLin5 小时前
如何在 Debian 10 上配置并优化 Redis 集群,确保低延迟高并发的实时数据缓存与查询
redis·缓存·debian
TDengine (老段)5 小时前
TDengine Python 连接器进阶指南
大数据·数据库·python·物联网·时序数据库·tdengine·涛思数据
赵渝强老师5 小时前
【赵渝强老师】OceanBase的配置文件与配置项
数据库·oceanbase
玖日大大5 小时前
OceanBase SeekDB:AI 原生数据库的技术革命与实践指南
数据库·人工智能·oceanbase