`
carlosfu
  • 浏览: 582275 次
  • 性别: Icon_minigender_1
  • 来自: 北京
博客专栏
Ba8b5055-9c58-3ab0-8a1c-e710f0495d2c
BigMemory实战与理...
浏览量:31215
53b2087e-c637-34d2-b61d-257846f73ade
RedisCluster开...
浏览量:151022
C9f66038-7478-3388-8086-d20c1f535495
缓存的使用与设计
浏览量:125211
社区版块
存档分类
最新评论

Redis客户端常见异常分析

阅读更多

本文部分内容来自《Redis开发与运维》一书,转载请声明。原文如下:

https://cachecloud.github.io/2016/11/17/Redis%E5%AE%A2%E6%88%B7%E7%AB%AF%E5%B8%B8%E8%A7%81%E5%BC%82%E5%B8%B8%E5%88%86%E6%9E%90/

 

更多Redis的开发、运维、架构以及新动态,欢迎关注微信公众号:

    

    

 

 

在Redis客户端的使用过程中,无论是客户端使用不当或者Redis服务端出现问题,客户端会反应出一些异常,下面分析一下Jedis使用过程中常见的异常情况:

一.无法从连接池获取到连接

JedisPool中的Jedis对象个数是有限的,默认是8个。这里假设使用的默认配置,如果有8个Jedis对象被占用,并且没有归还,如果调用者还要从JedisPool中借用Jedis,就需要进行等待(例如设置了maxWaitMillis>0),如果在maxWaitMillis时间内仍然无法获取到Jedis对象就会抛出如下异常。

redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
	…
Caused by: java.util.NoSuchElementException: Timeout waiting for idle object
	at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:449)

还有一种情况,就是设置了blockWhenExhausted=false,那么调用者发现池子中没有资源时,会立即抛出异常不进行等待,下面的异常就是blockWhenExhausted=false时的效果。

redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
	…
Caused by: java.util.NoSuchElementException: Pool exhausted
	at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:464)

 

对于这个问题,需要重点讨论的是为什么连接池没有资源了,造成没有资源的可能的原因非常多

  • 1.客户端:高并发下连接池设置过小,出现供不应求,所以会出现上面的错误,但是正常情况下只要比默认的最大连接数(8个)多一些即可,因为正常情况下JedisPool以及Jedis的处理效率足够高。

  • 2.客户端:没有正确使用连接池,比如没有进行释放,例如下面代码所示:
    定义JedisPool,使用默认的连接池配置。

GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
JedisPool jedisPool = new JedisPool(poolConfig, "127.0.0.1", 6379);
//向JedisPool借用8次连接,但是没有执行归还操作。
for (int i = 0; i < 8; i++) {
    Jedis jedis = null;
    try {
	jedis = jedisPool.getResource();
	jedis.ping();
    } catch (Exception e) {
	e.printStackTrace();
    }
}

当调用者再向连接池借用Jedis时(如下操作),就会抛出异常:

jedisPool.getResource().ping();
  • 3.客户端:存在慢查询操作,这些慢查询持有的Jedis对象归还速度会比较慢,造成池子满了。
  • 4.服务端:客户端是正常的,但是Redis服务端由于一些原因造成了客户端命令执行过程的阻塞,也会使得客户端抛出这种异常。
    可以看到造成这个异常的原因是多个方面的,不要被异常的表象所迷惑,而且并不存在通用的解决方案,开发和运维只能不断加强对于Redis的理解,顺藤摸瓜逐渐找到问题所在。

二、 客户端读写超时

Jedis在调用Redis时,如果出现了读写超时后,会出现下面的异常:

redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: Read timed out
造成该异常的原因也有以下几种: 

 

  • 读写超时设置的过短。
  • 命令本身就比较慢。
  • 客户端与服务端网络不正常。
  • Redis自身发生阻塞。

三 客户端连接超时

Jedis在调用Redis时,如果出现了读写超时后,会出现下面的异常:

redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: connect timed out
造成该异常的原因也有以下几种: 

 

  • 连接超时设置的过短。
  • Redis发生阻塞,造成tcp-backlog已满,造成新的连接失败。
  • 客户端与服务端网络不正常。

四、客户端缓冲区异常

Jedis在调用Redis时,如果出现客户端数据流异常,会出现下面的异常。

 

redis.clients.jedis.exceptions.JedisConnectionException: Unexpected end of stream.
造成这个异常原因可能有如下几种: 

 

  • 1.输出缓冲区满。例如将普通客户端的输出缓冲区设置为1M 1M 60:

 

config set client-output-buffer-limit "normal 1048576 1048576 60 slave 268435456 67108864 60 pubsub 33554432 8388608 60"
 

 

如果使用get命令获取一个bigkey(例如3M),就会出现这个异常。

  • 2.长时间闲置连接被服务端主动断开,可以查询timeout配置的设置以及自身连接池配置是否需要做空闲检测。
  • 3.不正常并发读写:Jedis对象同时被多个线程并发操作,可能会出现上述异常。

五、Lua脚本正在执行

如果Redis当前正在执行Lua脚本,并且超过了lua-time-limit,此时Jedis调用Redis时,会收到下面的异常。对于如何处理这类问题(Lua lua-time-limit配置之前章节已经介绍了)

redis.clients.jedis.exceptions.JedisDataException: BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE

六、Redis正在加载持久化文件

Jedis调用Redis时,如果Redis正在加载持久化文件,那么会收到下面的异常。

redis.clients.jedis.exceptions.JedisDataException: LOADING Redis is loading the dataset in memory

七、Redis使用的内存超过maxmemory配置

Jedis调用Redis执行写操作时,如果Redis的使用内存大于maxmemory的设置,会收到下面的异常,此时应该调整maxmemory并找到造成内存增长的原因(maxmemory之前章节已经介绍了)

redis.clients.jedis.exceptions.JedisDataException: OOM command not allowed when used memory > 'maxmemory'.

八、客户端连接数过大

如果客户端连接数超过了maxclients,新申请的连接就会出现如下异常:

redis.clients.jedis.exceptions.JedisDataException: ERR max number of clients reached

此时新的客户端连接执行任何命令,返回结果都是如下: 

127.0.0.1:6379> get hello
(error) ERR max number of clients reached
 
  •  1.客户端:如果maxclients参数不是很小的话,应用方的客户端连接数基本不会超过maxclients,通常来看是由于应用方对于Redis客户端使用不当造成的。此时如果应用方是分布式结构的话,可以通过下线部分应用节点(例如占用连接较多的节点),使得Redis的连接数先降下来。从而让绝大部分节点可以正常运行,此时在再通过查找程序bug或者调整maxclients进行问题的修复。这个问题可能会比较棘手,因为此时无法执行Redis命令,一般来说可以从两个方面进行着手。
  • 2.服务端:如果此时客户端无法处理,而当前Redis为高可用模式(例如Redis Sentinel和Redis Cluster),可以考虑将当前Redis做故障转移。

此问题不存在确定的解决方式,但是无论从哪个方面进行处理,故障的快速恢复极为重要,当然更为重要的是找到问题的所在,否则一段时间后客户端连接数依然会超过maxclients。

九、JedisCluster异常将在集群章节介绍。

附赠GenericObjectPoolConfig的重要属性

序号 参数名 含义 默认值
1 maxActive 连接池中最大连接数 8
2 maxIdle 连接池中最大空闲的连接数 8
3 minIdle 连接池中最少空闲的连接数 0
4 maxWaitMillis 当连接池资源用尽后,调用者的最大等待时间(单位为毫秒),一般不建议使用默认值 -1:表示永远不超时,一直等。
5 jmxEnabled 是否开启jmx监控,如果应用开启了jmx端口并且jmxEnabled设置为true,就可以通过jconsole或者jvisualvm看到关于连接池的相关统计,有助于了解连接池的使用情况,并且可以针对其做监控统计 true
6 minEvictableIdleTimeMillis 连接的最小空闲时间,达到此值后空闲连接将被移除 30分钟
7 numTestsPerEvictionRun 做空闲连接检测时,每次的采样数 3
8 testOnBorrow 向连接池借用连接时是否做连接有效性检测(ping),无效连接会被移除,每次借用多执行一次ping命令 false
9 testOnReturn 向连接池归还连接时是否做连接有效性检测(ping),无效连接会被移除,每次归还多执行一次ping命令 false
10 testWhileIdle 向连接池借用连接时是否做连接空闲检测,空闲超时的连接会被移除 false
11 timeBetweenEvictionRunsMillis 空闲连接的检测周期(单位为毫秒) -1:表示不做检测
12 blockWhenExhausted 当连接池用尽后,调用者是否要等待,这个参数是和maxWaitMillis对应的,只有当此参数为true时,maxWaitMillis才会生效 true

本文部分内容来自《Redis开发与运维》一书,转载请声明。

分享到:
评论

相关推荐

    Redis常见客户端异常.docx

    本文将详细介绍 Redis 客户端常见的异常类型、原因和解决方法。 一、无法从连接池获取连接异常 在使用 JedisPool 连接 Redis 时,可能会出现无法从连接池获取连接的异常。这是因为 JedisPool 中的 Jedis 对象个数...

    Redis客户端Java服务接口封装

    本文将围绕“Redis客户端Java服务接口封装”这一主题进行深入探讨,结合给定的`RedisClientTemplate.java`文件,我们将讨论如何在Java环境中高效地与Redis进行交互。 首先,`RedisClientTemplate`这个名字暗示它是...

    redis客户端桌面连接工具

    ### 常见的Redis客户端桌面连接工具 1. **Redis Desktop Manager**:一款非常流行的开源Redis客户端工具,支持多种数据类型显示,并具备良好的跨平台兼容性。 2. **RedisInsight**:由Redis Labs推出的官方客户端...

    poseidon2011#CookBook-1#10-Redis面试热点问题1

    2.Redis的内存优化 3.Redis的Linux系统优化 4.Redis客户端常见异常分析 6.Redis无限全量复制问题分析与优化

    c#版本的redis客户端(目前基于使用RESP2协议对接)

    例如,StackExchange.Redis是一个常见的C# Redis客户端,它完全支持RESP2协议,提供了丰富的API来操作Redis。 数据库相关知识点: 1. 键值对存储:Redis的核心特性之一是其键值对存储模型,键可以是任何字符串,值...

    Redis客户端连接工具

    本文将详细介绍两个常见的Redis客户端连接工具:Redis Desktop Manager和RedisClient。 1. Redis Desktop Manager Redis Desktop Manager是一款跨平台的图形用户界面(GUI)工具,适用于Windows、macOS和Linux操作...

    windows 部署redis 服务端和客户端实战资源

    2. **Redis客户端**: - `RedisDesktopManager`(如`Redis+Desktop+Manager++v0.7.6可视化查看工具.exe`)是一个流行的图形用户界面(GUI)工具,用于连接并管理Redis服务器,进行键值操作、数据浏览和集群管理。 ...

    java客户端测试redis集群

    2. **Java客户端**:Java中常见的Redis客户端有Jedis、Lettuce和Redisson等。它们都提供了与Redis服务器通信的API,包括连接管理、命令执行、事务处理等功能。例如,Jedis是较早的Redis客户端,功能完备,而Lettuce...

    A C++ Redis client.zip

    标题 "A C++ Redis client.zip" 提供了一个关键信息,即这个压缩包包含了一个用C++编写的Redis客户端。Redis是一种高性能的键值存储系统,常用于数据缓存、消息队列以及数据库等多个场景。C++是通用的、面向对象的...

    RedisTest.rar

    StackExchange.Redis是.NET平台上广泛使用的Redis客户端,提供了丰富的功能和良好的性能。 首先,让我们深入了解一下Redis的基础知识。Redis作为一个内存数据存储系统,支持多种数据结构,如字符串、哈希、列表、...

    redis-client-windows

    "redis-client-windows" 是一个专为Windows操作系统设计的Redis客户端工具,它提供了便捷的方式在本地Windows环境中与Redis服务器进行交互,包括复制、查询和修改Redis中的数据。 在Windows上使用Redis通常涉及以下...

    C# winform中使用Redis

    StackExchange.Redis是.NET社区广泛使用的Redis客户端,提供了丰富的API接口来与Redis服务器进行交互。你可以通过NuGet包管理器在项目中添加这个库。 1. **配置Redis连接** 在C#代码中,首先创建一个`...

    tomcat-redis依赖jar包

    Jedis是较早且广泛应用的Redis客户端,支持多种Redis命令,而Lettuce则提供了更现代的Netty基础的异步API,适合高并发场景。 2. **集成步骤** - 添加依赖:在Maven或Gradle项目中,你需要在pom.xml或build.gradle...

    c++ 操作redis数据库

    2. C++库选择:熟悉cpp-redis、hiredis等C++ Redis客户端库,了解其API用法。 3. 网络编程:理解TCP/IP协议基础,能够建立和管理网络连接。 4. 数据序列化:了解如何将C++的数据结构转换为Redis能识别的命令格式,...

    scredis:基于Akka IO的非阻塞超快Scala Redis客户端

    2. **完整的Redis命令支持**:`scredis`实现了Redis的所有标准命令,可以方便地执行常见的数据操作。 3. **连接池**:支持连接池管理,有效利用资源,减少创建和销毁连接的开销。 4. **命令流水线**:通过批量发送...

    redis utils 工具类

    RedisUtils通常依赖于Jedis,这是一个Java语言编写的Redis客户端。通过Jedis,我们可以实现连接Redis服务器、执行各种命令(如GET、SET、HGETALL等)以及断开连接等功能。在工具类中,我们通常会定义静态方法,如`...

    c#Redis引用dll+简单教程

    本教程将围绕"C# Redis引用dll+简单教程"这一主题,详细介绍如何在C#项目中引入Redis客户端库以及进行基本操作。 首先,为了在C#项目中使用Redis,我们需要一个支持.NET的Redis客户端库。常见的有StackExchange....

    redis-api.tar.gz_REDIS C#_c redis_cut_redis

    这意味着当网络出现问题时,客户端不会立即抛出异常并阻塞整个应用程序,而是可以设置超时和重试策略。StackExchange.Redis 提供了异步操作支持,如 `Task` 基于的 API,这使得应用程序可以在等待 Redis 回应的同时...

    Qt 应用Redis 实现消息队列

    在Qt应用中集成Redis,我们通常会使用第三方库如`QRedis`,这是一个基于Qt的Redis客户端库,它提供了一系列方便的API来操作Redis。首先,你需要将`QRedis`库添加到你的Qt项目中,可以通过配置项目文件.pro或使用...

    redis倒计时商品订单状态

    3. **Java Redis客户端**:在Java项目中,我们通常使用Jedis或Lettuce等库来操作Redis。这些客户端库提供了丰富的API,便于与Redis进行交互。 4. **订单状态管理**:订单可以有多种状态,如"新建"、"已支付"、"已...

Global site tag (gtag.js) - Google Analytics