一、redis资源未释放的起因:

N年前,在修改一个古老程序时,不小心把redis释放的这块给干掉了,

if (jedis != null) {
if (!isInProcess) {
jedis.del(currentPageRunControlRedisKey);
}
JedisUtil.getInstance().closeJedis(jedis);
}

程序调用了一会之后,就获取不到redis连接了,异常如下:

redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
at redis.clients.util.Pool.getResource(Pool.java:42)

对比代码,定位到问题之后,修复上线。

二、事后分析出错的原因:

1、对于redis的认知不足.

2、java从集成redis角度上,我的认知:

2.1、选择spring-data-redis集成,目前我们的osp框架支持我们用这种方式,我目前的项目在用(po服务化).

集成后用RedisTemplate即可来操作。

好处:spring来帮你管理redis的连接获取和释放,我们只需要关注自己的业务就好.

坏处:限制于spring框架,其他暂未感觉...

2.2、用jedis来操作吧,

自己写好一个类,来获取JedisPool,对于redis的操作,记住操作完成后,释放连接回连接池,否则就会发生,我这次发生的这种问题。

好处:操作上更加灵活,不限于spring。

坏处:容易出错。

三、改进:

对于资源未释放,想到了在io操作,db操作等情况,最好封装统一的方法,保证最后资源一定是释放的。

redis方面,我参考spring的redisTemplate,

封装一个redis工具类,对每种类型的redis操作,封装一个方法,操作完后将资源释放回连接池,可避免再忘记释放redis。

代码示例:         

 import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; /**
* 描 述:JedisUtil
* 作 者:潇邦
*/
public class JedisUtil { private static final Log logger = LogFactory.getLog(JedisUtil.class); //Redis服务器IP
private static String IP = "127.0.0.1"; //Redis的端口号
private static int PORT = 6379; //可用连接实例的最大数目,默认值为8;
//如果赋值为-1,则表示不限制;如果pool已经分配了maxActive个jedis实例,则此时pool的状态为exhausted(耗尽)。
private static int MAX_ACTIVE = 64; //控制一个pool最多有多少个状态为idle(空闲的)的jedis实例,默认值也是8。
private static int MAX_IDLE = 20; //等待可用连接的最大时间,单位毫秒,默认值为-1,表示永不超时。如果超过等待时间,则直接抛出JedisConnectionException;
private static int MAX_WAIT = 3000; private static int TIMEOUT = 3000; //在borrow一个jedis实例时,是否提前进行validate操作;如果为true,则得到的jedis实例均是可用的;
private static boolean TEST_ON_BORROW = true; //在return给pool时,是否提前进行validate操作;
private static boolean TEST_ON_RETURN = true; private static Map<String, JedisPool> maps = new ConcurrentHashMap<String, JedisPool>(); private JedisUtil() {
} /**
* 类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例 没有绑定关系,而且只有被调用到时才会装载,从而实现了延迟加载。
*/
private static class RedisUtilHolder {
private static JedisUtil instance = new JedisUtil();
} /**
* 当getInstance方法第一次被调用的时候,它第一次读取 RedisUtilHolder.instance,导致RedisUtilHolder类得到初始化;而这个类在装载并被初始化的时候,会初始化它的静
* 态域,从而创建RedisUtil的实例,由于是静态的域,因此只会在虚拟机装载类的时候初始化一次,并由虚拟机来保证它的线程安全性。 这个模式的优势在于,getInstance方法并没有被同步,
* 并且只是执行一个域的访问,因此延迟初始化并没有增加任何访问成本。
*/
public static JedisUtil getInstance() {
return RedisUtilHolder.instance;
} /**
* 获取连接池.
*/
private JedisPool getPool(String ip, int port) {
String key = ip + ":" + port;
JedisPool pool = null;
if (!maps.containsKey(key)) {//根据ip和端口判断连接池是否存在.
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(MAX_ACTIVE);
config.setMaxIdle(MAX_IDLE);
config.setMaxWaitMillis(MAX_WAIT);
config.setTestOnBorrow(TEST_ON_BORROW);
config.setTestOnReturn(TEST_ON_RETURN);
try {
pool = new JedisPool(config, ip, port, TIMEOUT);
maps.put(key, pool);
} catch (Exception e) {
logger.error("初始化Redis连接池异常:", e);
}
} else {
pool = maps.get(key);
}
return pool;
} /**
* 获取Jedis实例
*/
public Jedis getJedis() {
Jedis jedis = null;
try {
jedis = getPool(IP, PORT).getResource();
} catch (Exception e) {
logger.error("获取Jedis实例异常:", e);
// 销毁对象
getPool(IP, PORT).returnBrokenResource(jedis);
}
return jedis;
} /**
* 释放jedis资源到连接池
*/
public void returnResource(final Jedis jedis) {
if (jedis != null) {
getPool(IP, PORT).returnResource(jedis);
}
} /**
* 获取数据
*/
public Object get(String key) {
Object value = null;
Jedis jedis = null;
try {
jedis = getJedis();
value = jedis.get(key);
} catch (Exception e) {
logger.warn("获取数据异常:", e);
} finally {
//返还到连接池
returnResource(jedis);
}
return value;
} public static void main(String[] args) {
Object val = JedisUtil.getInstance().get("redisKey");
System.out.println(val);
} }

四、redis的概念:

redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。

五、数据库连接池的概念:

数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个;释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏。这项技术能明显提高对数据库操作的性能。

六、参考的资料:

http://www.cnblogs.com/linjiqin/archive/2013/06/14/3135248.html

http://www.sjsjw.com/kf_www/article/70_11941_26800.asp

http://www.cnblogs.com/tankaixiong/p/3660075.html

http://blog.csdn.net/truong/article/details/46711045

http://my.oschina.net/ydsakyclguozi/blog/465859

http://blog.csdn.net/aacm1992/article/details/21977237

http://www.cnblogs.com/jifeng/p/4676863.html

redis资源未释放引发的问题的更多相关文章

  1. 解决切换场景时NGUI图集资源未释放的问题

    使用unity3d编辑器,在切换场景的时候.NGUI的图集没有释放造成内存不足游戏闪退的问题. 默认情况下,unity3d切换场景之后会释放不用的内存,即内部会调用Resources.UnloadUn ...

  2. 使用HttpURLConnection时遇到的资源未释放的问题

    http://blog.sina.com.cn/s/blog_56beadc60100j9zu.html 今天自己写了一个压力测试的小程序,同时启100个线程,每个线程都串行地访问应用服务器上的一个j ...

  3. 图层损坏 E/ArcGIS﹕ The map or layer has been destroyed or recycled. 资源未释放

    看到论坛上有个网友和我一样的问题: The map or layer has been destroyed or recyled t Hello, I have a problem when the ...

  4. 一次 Redis 事务使用不当引发的生产事故

    这是悟空的第 170 篇原创文章 官网:http://www.passjava.cn 你好,我是悟空. 本文主要内容如下: 一.前言 最近项目的生产环境遇到一个奇怪的问题: 现象:每天早上客服人员在后 ...

  5. mysql优化, 删除数据后物理空间未释放(转载)

    mysql优化, 删除数据后物理空间未释放(转载) OPTIMIZE TABLE 当您的库中删除了大量的数据后,您可能会发现数据文件尺寸并没有减小.这是因为删除操作后在数据文件中留下碎片所致.OPTI ...

  6. 关于mysql 删除数据后物理空间未释放(转载)

    转自 关于mysql 删除数据后物理空间未释放(转载) - NETDATA - 博客园http://www.cnblogs.com/shawnloong/archive/2013/02/07/2908 ...

  7. jsch连接sftp后连接未释放掉问题排查

    项目中通过jsch中的sftp实现上传下载文件.在压测过程中,由于调用到sftp,下载文件不存在时,系统不断抛出异常,内存飙升,逐渐把swap区也占满,通过top监控未发现占用内存的进程,通过查找ss ...

  8. java输入输出流操作同一资源实现覆盖引发冲突的解析

    一.问题发生的场景 题目:把s.txt中大写转成小写,小写转成大写,空格转成下划线,在输入到文件中覆盖之前的 前面的没有问题,问题出现在后面的覆盖文件上,输入流.输出流要操作同一个文件的问题 二.小白 ...

  9. linux删除文件未释放空间问题处理

    linux删除文件未释放空间问题处理 或者 /根分区满了 (我的根分区是/dev/sda1,/dev/sda1满了) http://blog.csdn.net/donghustone/article/ ...

随机推荐

  1. iOS中 断点下载详解 韩俊强的博客

    布局如下: 基本拖拉属性: #import "ViewController.h" #import "AFNetworking.h" @interface Vie ...

  2. Linux信号实践(3) --信号内核表示

    信号在内核中的表示 执行信号的处理动作称为信号递达(Delivery),信号从产生到递达之间的状态,称为信号未决(Pending).进程可以选择阻塞(Block)某个信号.被阻塞的信号产生时将保持在未 ...

  3. GDAL中MEM格式的简单使用示例

    GDAL库中提供了一种内存文件格式--MEM.如何使用MEM文件格式,主要有两种,一种是通过别的文件使用CreateCopy方法来创建一个MEM:另外一种是图像数据都已经存储在内存中了,然后使用内存数 ...

  4. 挖掘频繁项集之FP-Growth算法

    http://blog.csdn.net/pipisorry/article/details/48918007 FP-Growth频繁项集挖掘算法(Frequent-Pattern Growth, 频 ...

  5. C++闭包: Lambda Functions in C++11

    表达式无疑是C++11最激动人心的特性之一!它会使你编写的代码变得更优雅.更快速! 它实现了C++11对于支持闭包的支持.首先我们先看一下什么叫做闭包 维基百科上,对于闭包的解释是: In progr ...

  6. java,http的post和get

    使用Java发送GET.POST请求 --节选自<疯狂Java讲义>    URL的openConnection()方法将返回一个URLConnection对象,该对象表示应用程序和 UR ...

  7. VS2010中NET4项目中使用LOG4NET办法

    两年以前项目中是用的log4net.dll是net2.0环境下面的,最近在官网http://logging.apache.org/log4net/download_log4net.cgi找到了net4 ...

  8. SpriteBuilder给节点添加effect在32设备上发生crash

    环境为 Xcode 6.4 , cocos2D 3.0.4 , SpriteBuilder 1.4.9 在给某一节点添加Effect后,运行在真机iphone4s上发生崩溃,显示为: 可以看到整个堆栈 ...

  9. shell脚本处理长参数的模板

    shell脚本处理长参数的模板 一个shell模板,处理命令行参数,支持长短参数: #!/bin/bash # # FILE: kvm-clone-v2.sh # # DESCRIPTION: Clo ...

  10. 【算法导论】八皇后问题的算法实现(C、MATLAB、Python版)

    八皇后问题是一道经典的回溯问题.问题描述如下:皇后可以在横.竖.斜线上不限步数地吃掉其他棋子.如何将8个皇后放在棋盘上(有8*8个方格),使它们谁也不能被吃掉?         看到这个问题,最容易想 ...