用Java折腾Redis配置那些事儿,边写代码边调参数体验
- 问答
- 2025-12-30 22:07:45
- 2
这事儿得从我接手那个老项目说起,项目里用Redis缓存一些用户信息,本来跑得好好的,突然有一天,运营说部分用户登录后老是转圈圈,体验很差,我一查日志,好家伙,Redis连接超时的错误一堆,看来是时候好好折腾一下Redis的配置了。
我用的Java客户端是Jedis,当时图省事,配置就是最基础的那种,代码里直接new JedisPool(config, "127.0.0.1", 6379)就完事儿了,现在问题来了,肯定不能这么糙了。
第一回合:和连接超时时间(connectionTimeout)的较量
最开始报的错误就是连接超时,我寻思着,是不是网络有点波动,默认的超时时间太短了?Jedis的默认connectionTimeout是2000毫秒,也就是2秒,我试着把它调大到5000毫秒。
JedisPoolConfig poolConfig = new JedisPoolConfig(); // ... 其他池子配置先不管 JedisPool jedisPool = new JedisPool(poolConfig, "127.0.0.1", 6379, 5000); // 最后一个参数就是connectionTimeout,单位毫秒
改完部署上去,观察了一会儿,连接超时的错误确实少了一些,但没完全消失,而且还出现了新的情况:有时候请求的响应变得特别慢,我意识到,光调大这个参数是治标不治本,如果网络真的不稳定,等5秒才报错,虽然连接可能成功了,但用户的请求已经被阻塞了5秒,体验更差,这就像等一个迟到的人,等2分钟你可能就走了,等10分钟你可能会非常烦躁,这个值不能太小(频繁超时),也不能太大(阻塞太久),后来我把它设成了3000毫秒,算是取个折中,并且开始怀疑是不是连接池本身有问题。

第二回合:深挖连接池参数(JedisPoolConfig)
之前的配置根本没好好设置连接池,用的都是默认值,这可能是问题的根源,我打开JedisPoolConfig的文档,开始一个个参数琢磨。
-
maxTotal(最大连接数):默认是8,这太少了!想象一下,我们系统并发一高,8个连接瞬间被用完,第9个请求就得等着,等久了不就超时了吗?我看了下服务器资源,果断先调到50,这个值不能瞎设,得根据业务量和服务器配置来。
-
maxIdle和minIdle(最大和最小空闲连接数):默认分别是8和0,maxIdle我让它和maxTotal保持一致,也是50,避免连接频繁创建和关闭,minIdle我设成了5,意思是让池子里始终维持至少5个空闲连接,来了请求就能直接用,不用临时建立连接,速度更快。

-
maxWaitMillis(获取连接时的最大等待时间):默认是-1,意思是无限等待,这太危险了!万一连接池耗尽,所有线程都会卡死在这,我把它设成了2000毫秒,如果2秒内还拿不到连接,就抛个异常,然后我们代码里可以捕获这个异常,给用户返回一个“系统繁忙”之类的友好提示,总比一直傻等着强。
-
testOnBorrow(借出连接时是否校验):这个参数我纠结了很久,如果设为true,每次从池子里拿连接都会执行一次
ping命令,确保连接是好的,这能保证拿到的连接绝对有效,但每次多一次网络开销,性能有损耗,如果设为false,性能好了,但有可能拿到一个已经断开的连接(比如被Redis服务器主动踢掉了),到时候操作就会报错,我采取了折中方案,用了testWhileIdle。 -
testWhileIdle(定期校验空闲连接):这个我设成了true,然后配合另外三个参数:
timeBetweenEvictionRunsMillis:我设成30000毫秒(30秒),意思是每30秒运行一次空闲连接校验任务。minEvictableIdleTimeMillis:设成60000毫秒(1分钟),如果一个连接空闲超过1分钟,就把它踢掉。numTestsPerEvictionRun:每次校验任务最多检查5个连接。
这样配置的好处是,既不用每次借连接都检查,又能定期清理掉无效的空闲连接,在性能和可靠性之间找了个平衡。

折腾完这一套连接池配置,代码看起来像样多了:
JedisPoolConfig poolConfig = new JedisPoolConfig(); poolConfig.setMaxTotal(50); poolConfig.setMaxIdle(50); poolConfig.setMinIdle(5); poolConfig.setMaxWaitMillis(2000); poolConfig.setTestOnBorrow(false); poolConfig.setTestWhileIdle(true); poolConfig.setTimeBetweenEvictionRunsMillis(30000); poolConfig.setMinEvictableIdleTimeMillis(60000); poolConfig.setNumTestsPerEvictionRun(5); JedisPool jedisPool = new JedisPool(poolConfig, "127.0.0.1", 6379, 3000);
部署之后,观察了几天,之前那种连接超时和响应慢的问题基本再没出现过。
第三回合:读写超时(soTimeout)的小插曲
安稳日子没过多久,又出了个新问题,有用户反馈,上传一个大头像之后,登录就报错,查日志发现是socket read timeout,原来,Redis的soTimeout默认也是2000毫秒,用户上传大头像,用户信息比较大,序列化后写入Redis的时间超过了2秒,导致读超时。
这个参数在Jedis里设置有点隐蔽,它在Connection类里,在创建JedisPool时,需要用一个带更多参数的构造方法:
// 注意最后一个参数 soTimeout,单位也是毫秒 JedisPool jedisPool = new JedisPool(poolConfig, "127.0.0.1", 6379, 3000, null, 0, 5000);
这里我把soTimeout设成了5000毫秒,给大value的读写留足了时间,我也反思了业务,把那些特别大的对象(比如头像的Base64编码)就不要往Redis里塞了,还是应该存到文件服务器上,Redis里只存个URL。
经过这几轮的折腾,我算是明白了,Redis配置这事儿,真没有一劳永逸的“黄金参数”,都是摸着石头过河,根据自己业务的实际情况——比如网络环境、数据大小、并发量——边写代码边调整,观察效果,再调整,最重要的不是记住某个参数值,而是理解每个参数背后代表的含义,出了问题能有个清晰的排查思路。
本文由颜泰平于2025-12-30发表在笙亿网络策划,如有疑问,请联系我们。
本文链接:http://www.haoid.cn/wenda/71514.html
