Redis客户端常见异常分析

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介: 本文部分内容来自《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%B...

本文部分内容来自《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服务端出现问题,客户端会反应出一些异常,下面分析一下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实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
8天前
|
JSON NoSQL Java
Redis入门到通关之Java客户端SpringDataRedis(RedisTemplate)
Redis入门到通关之Java客户端SpringDataRedis(RedisTemplate)
215 0
|
8天前
|
编解码 NoSQL 数据可视化
一个现代化轻量级的跨平台Redis桌面客户端
一个现代化轻量级的跨平台Redis桌面客户端
283 0
|
8天前
|
缓存 监控 NoSQL
【Redis性能瓶颈揭秘】「调优系列」深入分析热Key的排查策略和解决方案
【Redis性能瓶颈揭秘】「调优系列」深入分析热Key的排查策略和解决方案
215383 12
|
8天前
|
NoSQL Java Redis
使用Redis实例搭建网上商城的商品相关性分析程序
本教程将指导您如何快速创建实例并搭建网上商城的商品相关性分析程序。(ApsaraDB for Redis)是兼容开源Redis协议标准的数据库服务,基于双机热备架构及集群架构,可满足高吞吐、低延迟及弹性变配等业务需求。
17523 0
|
8天前
|
存储 消息中间件 缓存
Redis的高性能使得它非常适合用于实时分析场景
【5月更文挑战第15天】Redis在Python Web开发中扮演关键角色,常用于缓存系统,提高数据读取速度;会话管理,存储用户信息;分布式锁,确保数据一致性;排行榜和计数,利用有序集合和哈希结构;消息队列,基于列表结构实现异步处理;实时分析,高效处理实时数据。其丰富的数据结构和高性能使其在多种场景下应用广泛。
283 3
|
8天前
|
JSON NoSQL Java
深入浅出Redis(十三):SpringBoot整合Redis客户端
深入浅出Redis(十三):SpringBoot整合Redis客户端
|
8天前
|
NoSQL 网络协议 Java
Redis客户端Lettuce深度分析介绍(上)
Spring Boot自2.0版本开始默认使用Lettuce作为Redis的客户端(注1)。Lettuce客户端基于Netty的NIO框架实现,对于大多数的Redis操作,只需要维持单一的连接即可高效支持业务端的并发请求 —— 这点与Jedis的连接池模式有很大不同。同时,Lettuce支持的特性更加全面,且其性能表现并不逊于,甚至优于Jedis。本文通过分析Lettuce的特性和内部实现(基于6.0版本),及其与Jedis的对照比较,对这两种客户端,以及Redis服务端进行深度探讨。
101071 5
|
8天前
|
存储 NoSQL 前端开发
【Redis深度专题】「核心技术提升」探究Redis服务启动的过程机制的技术原理和流程分析的指南(集群指令分析—实战篇)
【Redis深度专题】「核心技术提升」探究Redis服务启动的过程机制的技术原理和流程分析的指南(集群指令分析—实战篇)
13 0
|
8天前
|
存储 NoSQL 算法
【Redis深度专题】「核心技术提升」探究Redis服务启动的过程机制的技术原理和流程分析的指南(集群指令分析—上篇)(二)
【Redis深度专题】「核心技术提升」探究Redis服务启动的过程机制的技术原理和流程分析的指南(集群指令分析—上篇)
34 0
|
8天前
|
存储 监控 NoSQL
【Redis深度专题】「核心技术提升」探究Redis服务启动的过程机制的技术原理和流程分析的指南(集群指令分析—上篇)(一)
【Redis深度专题】「核心技术提升」探究Redis服务启动的过程机制的技术原理和流程分析的指南(集群指令分析—上篇)
30 0