Redis缓冲区设置

对于Redis服务器的输出(也就是命令的返回值)来说,其大小通常是不可控制的。有可能一个简单的命令,能够产生体积庞大的返回数据。另外也有可能因为执行了太多命令,导致产生返回数据的速率超过了往客户端发送的速率,这是也会导致服务器堆积大量消息,从而导致输出缓冲区越来越大,占用过多内存,甚至导致系统崩溃。

所幸,Redis设置了一些保护机制来避免这种情况的出现,不同类型的客户端有不同的限制参数。限制方式有如下两种:

(1)、大小限制,当某一个客户端的缓冲区超过某一个大小值时,直接关闭这个客户端的连接;

(2)、持续性限制,当某一个客户端的缓冲区持续一段时间占用过大空间时,会直接关闭客户端连接。

 

我们来看看配置文件关于客户端输出缓冲区的配置:

client-output-buffer-limit normal 0 0 0

client-output-buffer-limit slave 256mb 64mb 60

client-output-buffer-limit pubsub 8mb 2mb 60

 

不同客户端有不同策略,策略如下:

Ø  对于普通客户端来说,限制为0,也就是不限制。因为普通客户端通常采用阻塞式的消息应答模式,何谓阻塞式呢?如:发送请求,等待返回,再发送请求,再等待返回。这种模式下,通常不会导致Redis服务器输出缓冲区的堆积膨胀;

Ø  对于Pub/Sub客户端(也就是发布/订阅模式),大小限制是8M,当输出缓冲区超过8M时,会关闭连接。持续性限制是,当客户端缓冲区大小持续60秒超过2M,则关闭客户端连接;

Ø  对于slave客户端来说,大小限制是256M,持续性限制是当客户端缓冲区大小持续60秒超过64M,则关闭客户端连接。

上述三种规则都是可以修改的。可以通过CONFIG SET 命令设置或者直接修改redis.conf文件。

 

 

 

 

 

 

 

jedis 的 Unexpected end of stream 解决方案

  • redis 服务端版本号:2.8.X
  • Jedis 客户端版本号:2.8.1
  • 单线程、无并发操作

Jedis单链接、JedisPool、ShardedJedisPool,无论使用哪一种方式对 redis 服务进行操作,均出现了 Unexpected end of stream 的问题。

通过查看源码发现,报错具体位置是:RedisInputStream 类的 ensureFill() 方法

private void ensureFill() throws JedisConnectionException {
    if (count >= limit) {
      try {
        limit = in.read(buf);
        count = 0;
        if (limit == -1) {
          throw new JedisConnectionException("Unexpected end of stream.");
        }
      } catch (IOException e) {
        throw new JedisConnectionException(e);
      }
    }
  }

 

解决方案:

第一步,检查 redis config 中的 client-output-buffer-limit 配置

client-output-buffer-limit normal 0 0 0
client-output-buffer-limit slave 0 0 0
client-output-buffer-limit pubsub 0 0 0
  • 1
  • 2
  • 3

请根据实际情况合理设置Redis输出缓冲区限制,确定不是因为缓冲区太小,导致链接关闭,进而引起 Unexpected end of stream ,可以临时都设置成 “0(关闭缓冲区限制)” 来验证此种场景

第二步,检查 redis config 中的 timeout 配置

当此时间设置过短时,同一个 jedis 链接,两次访问 redis 服务的时间间隔 > ${timeout} , 服务端会单方关闭这个jedis链接,第二次使用这个jedis对象 操作 redis 时,会发生 Unexpected end of stream , 可以将其设置成“0(无过期)”来验证此种场景

第三步,优化客户端代码,增加重试机制

经过前两步的调试,根据业务实际情况,调整配置,但这样并不能完全杜绝 Unexpected end of stream 的发生,比如 “网络抖动”之类的场景下,依然会发生此问题(实际生产环境中,此种情况发生概率极低), 
但是为了防止此异常引起 jvm宕机,建议在代码层面上增加加重试机制。

话外篇:

使用 redis 做存储层时,有可能出现数数据不一致的情况( redis 无事务)

转载于:https://www.cnblogs.com/Dhouse/p/8295995.html

<think>好的,我现在需要回答用户关于Redis回收输出缓冲区机制和方法的问题。首先,我需要回顾用户提供的引用内容,看看有没有相关信息。用户引用的内容主要涉及Redis的持久化(RDB、AOF)、单线程模型、过期键删除策略以及并发处理等,但并没有直接提到输出缓冲区的回收机制。不过,根据我对Redis的了解,输出缓冲区管理是客户端连接处理的一部分,可能与内存管理和淘汰策略有关。 Redis作为单线程模型,处理客户端请求时,每个客户端连接都有自己的输入和输出缓冲区。输出缓冲区用于暂存命令执行后的返回数据,等待发送给客户端。如果客户端无法及时接收数据(比如网络慢或消费能力不足),缓冲区可能会积压,导致内存占用过高,甚至影响Redis性能。因此,Redis需要有机制来限制和回收输出缓冲区。 根据官方文档,Redis对客户端输出缓冲区有三种限制策略: 1. 普通客户端:包括除订阅和复制外的常规客户端,可以设置硬性限制(hard limit)和软性限制(soft limit)。超过硬限制或持续超过软限制的客户端会被断开连接。 2. 发布订阅客户端:由于发布订阅可能产生大量数据,限制较宽松,但仍有上限。 3. 复制客户端(slave):主节点在向从节点同步数据时,缓冲区更大,但同样有保护机制。 当客户端输出缓冲区超过设定阈值时,Redis会断开该连接以释放内存。此外,内存淘汰策略如maxmemory可能也会间接影响缓冲区管理,但主要还是通过客户端限制策略来回收。 需要验证这些信息是否正确,比如确认客户端缓冲区分类和限制参数,以及处理机制是否符合Redis的设计。用户可能还关心如何配置这些参数或监控缓冲区使用情况,但当前问题主要询问方法和原理,应集中在机制上。确保回答结构清晰,分点说明,并引用提供的参考资料中的相关内容,比如单线程模型对I/O的处理[^2],以及内存管理相关的持久化策略[^1][^4]可能作为背景补充。</think>### Redis输出缓冲区回收机制解析 Redis通过**客户端分类限制**和**动态回收策略**管理输出缓冲区,防止内存过度消耗。核心机制分为以下三个层次: --- #### 一、客户端分类管理策略 1. **普通客户端** - 包含常规非订阅/复制的客户端连接 - 通过`client-output-buffer-limit normal`配置项设置: - **硬性限制(hard limit)**:缓冲区超过该值立即关闭连接 - **软性限制(soft limit) + 时间窗口**:持续超出软限制超过指定时间后关闭连接 - 示例配置:`client-output-buffer-limit normal 256mb 64mb 60` (硬限制256MB,软限制64MB持续60秒) 2. **发布订阅客户端** - 针对使用`SUBSCRIBE/PSUBSCRIBE`的客户端 - 配置项:`client-output-buffer-limit pubsub` - 通常设置更大的阈值(如`32mb 8mb 60`),因订阅模式可能产生突发数据流[^2] 3. **复制客户端(Slave节点)** - 主节点向从节点同步数据时使用的缓冲区 - 配置项:`client-output-buffer-limit slave` - 典型配置:`1gb 256mb 300`(高吞吐场景需调整) --- #### 二、回收触发机制 | 触发条件 | 处理动作 | |-------------------------|--------------------------------------------------------------------------| | 瞬时超过硬限制 | 立即关闭连接并释放内存 | | 持续超过软限制+时间窗口 | 断开连接防止长期资源占用 | | 内存达到maxmemory时 | 结合LRU等淘汰策略,优先释放输出缓冲区(需配合`noeviction`外的淘汰策略) | --- #### 三、底层实现原理 1. **环形缓冲区结构** Redis使用连续内存块实现输出缓冲区,通过指针记录写入/读取位置。当写入速度超过发送速度时,缓冲区扩展直至触发限制。 2. **异步回收机制** 在事件循环(Event Loop)中,每次处理写事件后检查缓冲区状态: ```c // Redis源码片段(networking.c) void handleClientsWithPendingWrites(void) { while ((ln = listNext(&li))) { client *c = listNodeValue(ln); if (writeToClient(c->fd,c,0) == C_ERR) continue; if (clientHasPendingReplies(c)) { // 超过限制时异步关闭连接 if (checkClientOutputBufferLimits(c)) { freeClientAsync(c); } } } } ``` 3. **内存安全保护** 单线程模型下,缓冲区操作无需锁,但需确保及时释放: - 每次响应发送成功后收缩缓冲区 - 使用`zmalloc_usable_size()`精确跟踪实际内存消耗[^1] --- #### 四、监控与优化建议 1. **关键监控指标** - `redis-cli client list`查看`omem`字段(输出缓冲区内存) - `info memory`中的`output_memory`统计总量 2. **典型配置调整** ```bash # redis.conf client-output-buffer-limit normal 512mb 128mb 120 # 高吞吐场景 client-output-buffer-limit slave 2gb 512mb 0 # 主从大流量场景 ``` 3. **异常处理模式** - 频繁触发限制时需排查: - 客户端消费能力(如网络延迟) - 是否返回过大结果集(需分页优化) - 是否滥用`MONITOR`等高风险命令 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值