资讯动态

Java连接Redis实战:Jedis连接池配置与排查指南

发布时间:2026/10/17 9:36:02 来源:尧图企业网站定制
聊到Java连接Redis很多人下意识就是Spring Data Redis加Lettuce一条龙。但我手头有几个项目从很早期就一直用Jedis甚至后来换到新框架的模块底层仍然指定了Jedis。原因不复杂Jedis足够简单直接命令映射几乎是照抄Redis官方文档出了问题也容易排查。这篇文章就把Java中用Jedis连接Redis这件事从头到尾捋一遍从连接模型、依赖引入、连接池参数到借还连接的标准写法再到线上常见的连接报错和性能调优。无论你是第一次接Redis还是被连接池耗尽、认证报错折磨过应该都能从这里找到直接能用的答案。1. Jedis连接Redis前先理解它的连接模型很多人写代码是直接抄配置、抄示例抄完能跑就不管了。但只要连接一多、并发一上来各种问题就跟着冒出来。所以我想先把连接模型讲清楚后面所有的坑都跟这个模型有关。1.1 先搞明白Jedis这层薄封装到底薄在哪Jedis是Java社区里历史非常悠久的Redis客户端核心思路特别朴素把Redis的RESP协议命令翻译成Java方法。你平时在命令行敲的SET foo bar在Jedis里就是jedis.set(foo, bar)敲的HSET user:1 name zhangsan就是jedis.hset(user:1, name, zhangsan)。几乎没有多余的抽象层也没有Spring Data Redis那套RedisTemplate、序列化器、回调机制。这层“薄”既是优点也是代价。优点是行为和Redis命令几乎一一对应排错很直观本地怎么用redis-cli敲代码里就怎么写线上出问题拿命令去复现也容易。代价就是它不帮你管理连接、不解决线程安全、不处理序列化这些都得自己在项目里安排。一个Jedis实例在底层持有的是实实在在的TCP连接以及基于这个连接的输入输出流。你调用jedis.get(key)时它会向Redis服务端写入一条GET命令然后同步阻塞等待响应返回。这不是异步消息而是像打电话一样说一句等一句。这套同步阻塞模型虽然不符合“高并发最优解”的刻板印象但它足够可控很多业务场景里反而是优势。1.2 线程不安全与连接池的必然性Jedis实例本身不是线程安全的这一点新手特别容易忽略。因为底层协议是严格的一问一答如果多个线程同时往一个Jedis实例的Socket里写命令再同时从里面读响应协议状态立刻就乱了你会收到一堆解析失败的异常。打个比方同一根电话线两个人同时对着话筒说话对端听到的只能是乱码。既然不能多线程共用一个Jedis实例那每次用的时候new一个行不行技术上可以比如new Jedis(host, port)但代价很大。每次新建连接都要走一遍TCP三次握手用完断开还有四次挥手。一次两次没关系在高并发下服务端会看到大量连接反复创建销毁客户端这边还会留下大量TIME_WAIT状态的端口端口资源被吃光之后本地Socket创建直接失败。我自己就见过一个没做池化的老服务高峰期端口耗尽新连接全部报Cannot assign requested address。所以Jedis官方给出的标准答案就是连接池。JedisPool本质上就是一个泛型对象池管理者若干可复用的Jedis实例。线程需要操作Redis时从池里借一个用完归还而不是销毁。就像酒店里洗完消毒好的毛巾客人用完还回去下个客人继续用。想明白这一点后面所有的使用规则就都顺理成章了。1.3 不同部署模式下的连接入口选择Redis的部署方式不同Jedis对应的入口类也不同但底层逻辑没变。单节点部署直接用JedisPool。哨兵模式用JedisSentinelPool构造时传入masterName和哨兵地址列表客户端会跟随主从切换自动找到当前master。集群模式用JedisCluster它内部按slot管理多个连接池对外只需要一个对象它可以被多线程共用不需要额外加池。老式的分片部署用ShardedJedisPool现在基本只在历史项目里见到了。我的建议是先把单机的JedisPool彻底搞懂因为切换哨兵、集群时连接池参数配置的思路、借还释放的原则、排错的方法都是一脉相承的。先学会走再考虑跑。2. 从依赖到初始化五分钟拿到可用的Jedis连接理论部分先聊到这下面上实际代码。我不会只给一段能用就完事的示例会把版本、环境和容易踩的坑一起说清楚。2.1 依赖版本别乱拉既要注意API差异也要防冲突Maven项目里引入Jedis很简单dependency groupIdredis.clients/groupId artifactIdjedis/artifactId version4.3.1/version /dependencyGradle的话就是implementation redis.clients:jedis:4.3.1。版本方面新项目直接选4.x以上的稳定版。3.x和4.x之间有不少API调整比如构造方法变了还新增了JedisPooled这一类简化入口。如果你网上搜到的是3.x时代的代码放到4.x里很可能会编译报错注意区分。还有一个比较容易踩的坑是依赖冲突。如果你的项目同时引入了Spring Data Redis而它默认依赖的是Lettuce那classpath下会同时存在两套Redis客户端。本身这不致命但会造成困惑你配了Spring Data Redis的连接参数底层跑的是Lettuce自己又在代码里new了一个JedisPool底层跑的是Jedis。两边各管各的连接排查问题时经常搞混。我的原则是选一条路走到底要么纯用Jedis要么用Spring Data Redis并在配置里显式指定client-type为jedis。2.2 非Spring项目的初始化代码不用Spring框架的话代码反而更直白。先创建一个JedisPoolConfig再基于它创建JedisPoolJedisPoolConfig poolConfig new JedisPoolConfig(); poolConfig.setMaxTotal(50); poolConfig.setMaxIdle(20); poolConfig.setMinIdle(5); poolConfig.setMaxWaitMillis(1000); JedisPool pool new JedisPool(poolConfig, 127.0.0.1, 6379);然后取连接、操作、归还try (Jedis jedis pool.getResource()) { jedis.set(hello, world); String value jedis.get(hello); System.out.println(value); }注意从pool.getResource()拿出来的这个Jedis对象调用close()方法时并不会真正断开连接而是把连接还回池里等待下一次复用。这一点非常关键后面我会专门展开。如果你用的是Jedis 4.x还有个更省事的JedisPooledtry (JedisPooled jedis new JedisPooled(127.0.0.1, 6379)) { jedis.set(hello, world); }它的内部仍然是连接池只是把借和还封装掉了。适合连接逻辑简单的项目但我个人还是推荐显式用JedisPool因为当你想在连接前后做点日志或监控时借还点更清晰。2.3 Spring环境里如何配置Jedis在Spring Boot项目里如果你只是想用Spring Data Redis并且让底层跑Jedis可以这样配置。不同版本配置前缀有差异Spring Boot 2.x常见的是spring.redis.*到Spring Boot 3.x改成了spring.data.redis.*spring.data.redis.host127.0.0.1 spring.data.redis.port6379 spring.data.redis.passwordyourpassword spring.data.redis.client-typejedis如果项目里同时引入了jedis和lettuce依赖指定client-typejedis后Spring Boot就会用Jedis作为默认客户端。但如果你更希望完全掌控连接我推荐直接声明一个JedisPool的BeanConfiguration public class JedisConfig { Bean public JedisPool jedisPool() { JedisPoolConfig config new JedisPoolConfig(); config.setMaxTotal(100); config.setMaxIdle(30); config.setMinIdle(10); config.setMaxWaitMillis(1000); config.setTestWhileIdle(true); config.setTimeBetweenEvictionRunsMillis(30000); return new JedisPool(config, 127.0.0.1, 6379, 2000, yourpassword); } }这样做的优势是所有连接参数、池配置都集中在一个地方业务代码里只要注入JedisPool就行不依赖Spring Data Redis的自动配置。排查问题时思路也很清晰这个Bean就是全部入口。2.4 认证与SSL四个参数解决8成问题如果Redis设置了requirepass构造连接池时要把密码传进去JedisPool pool new JedisPool(poolConfig, 127.0.0.1, 6379, 2000, password, 0);这里最后那个0是database参数表示默认连db0。Redis 6之后还支持ACL用户名如果你们启用了ACL高版本的Jedis也支持传入username不传默认是default用户。遇到某些内部权限收敛得比较严的环境连上了但很多key查不到报错却是权限相关多半就是ACL用户名没传对。SSL场景下Jedis 4.x可以通过构造JedisClientConfig或者在URI里带上ssl参数来开启。内网环境通常不开SSL跨机房传输时建议开启。本地测试如果想临时跳过证书校验可以额外配置insecure但生产环境不要这么搞该配的信任链还是得配。3. 连接池参数设置每个参数背后的业务估算逻辑连接池参数是Jedis使用里最容易被“抄作业”抄出问题的部分。很多人直接把网上的配置粘过来maxTotal一配就是几百上千其实未必适合你的业务。这一节我讲讲每个参数应该怎么根据实际业务去估。3.1 maxTotal的最大连接数到底该怎么估先说结论最大连接数不是越大越好。每条连接都占用文件描述符、内存缓冲Redis服务端也有maxclients上限。配太大光空闲连接就能吃不少资源而且根本不会提升性能。正确的思路是估算并发需求。一个请求从pool.getResource()借到连接到执行完Redis命令归还假设平均占用时间为T秒系统高峰期并发请求数是N。那么理想情况下同时需要的连接数大约是N × T。举个例子。假设业务高峰期QPS是500但每个请求只做一次Redis操作单次操作到响应大概5ms那么同时占用的连接数就是500 × 0.005 2.5。你没看错可能只需要几条连接就够。当然实际不会这么理想Redis操作可能在一次请求里被调用多次还可能涉及序列化和网络波动所以一般再加50%到100%的余量配到几十条就差不多了。如果对T拿不准还有个校准方法先按并发线程数的一半去配然后看连接池的getNumWaiters()指标。如果这个数值长期大于0说明线程在池外排队等连接连接不够往上调如果一直是0说明连接充足甚至可以适当下调。用数据去校准比拍脑袋配几千要靠谱得多。3.2 maxIdle、minIdle和maxWaitMillis的取舍这三个参数里maxIdle和minIdle管的是空闲连接maxWaitMillis管的是没连接时等待的时间。我习惯用下面这张表来定初始值参数默认值建议值理由maxTotal8根据QPS估算默认值太小稍微有点并发就打满maxIdle8与稳定并发数匹配通常不高于maxTotal太大导致低峰期一堆空闲连接占资源minIdle05到10或按稳定并发数减少冷启动和流量回弹时的建连成本maxWaitMillis-1500到1000默认无限等待生产环境可能把线程全卡住maxIdle不建议设成和maxTotal一样大。如果高峰期100条连接低峰期也没必要保持100条空闲让池子慢慢回收一部分保留一个合理的下限就行。maxWaitMillis是特别值得强调的参数。默认值是-1意思是从池里拿不到连接时永久阻塞等待。在低并发时这没问题但一旦连接被借光所有请求线程都会堵在这里表现就是接口超时、线程池被打满而且你从日志里很难一眼看出是连不上Redis还是拿不到连接。设成500到1000毫秒之后最坏情况就是拿连接超时报错线程得以释放系统不至于整体挂掉。3.3 保活检测的正确组合testOnBorrow与testWhileIdleRedis服务端默认有个空闲超时大概300秒会关闭空闲连接。连接池里如果有一些连接长期没人用服务端其实已经断开了但池子不知道。下次某个线程借到这条“死连接”第一次命令发出去就可能读到EOF直接抛异常。这种“借到死连接”的问题在低流量服务里特别常见。解决办法有两个方向第一个是testOnBorrowtrue每次借出前先PING一下但每借一次就多一次网络往返性能有损耗第二个是testWhileIdletrue让后台线程定期扫描空闲连接把已经断掉的踢出去。我更推荐后者因为连接池本来就有空闲驱逐机制开启它不用牺牲正常请求的额外RTT。推荐组合配置config.setTestWhileIdle(true); config.setTimeBetweenEvictionRunsMillis(30000); config.setMinEvictableIdleTimeMillis(60000); config.setNumTestsPerEvictionRun(-1);timeBetweenEvictionRunsMillis是后台扫描线程的执行间隔设30秒一次配合minEvictableIdleTimeMillis连接空闲多久才参与驱逐60秒基本能保证池子里没有长期闲置的僵尸连接。numTestsPerEvictionRun设成-1表示扫描全部空闲连接数据量不大时这样最干净。3.4 一份可直接抄的生产配置综合上面的思路给出一个适合典型互联网业务同机房、中低延迟的配置JedisPoolConfig config new JedisPoolConfig(); config.setMaxTotal(100); config.setMaxIdle(30); config.setMinIdle(10); config.setMaxWaitMillis(1000); config.setTestWhileIdle(true); config.setTimeBetweenEvictionRunsMillis(30000); config.setMinEvictableIdleTimeMillis(60000); config.setNumTestsPerEvictionRun(-1); JedisPool pool new JedisPool(config, 127.0.0.1, 6379, 2000, password, 0);如果你的业务里有BLPOP这类长时间阻塞命令需要把soTimeout单独调大不能用这个默认的2000毫秒否则命令很容易超时。这个等第5章讲超时问题时再细说。4. 借还连接的标准姿势从try-with-resources到批量操作连接池参数配好了代码写得不规范一样会出事。这一节讲的是使用层面的纪律我见过的线上连接耗尽事故几乎都能追溯到某一处“借了没还”。4.1 一条容易踩晕的规则close不是关闭很多人第一次接触Jedis时会有个困惑既然连接池帮我复用了连接那我用完到底该不该close答案是该close但这里close的语义是“归还连接”不是“断开TCP”。区别在于这个Jedis对象是怎么来的。如果是从pool.getResource()借出来的它是池管理的对象调用close()时会触发归还逻辑如果是直接new Jedis(host, port)创建的那close才会真正断开。因此从池里借出的连接必须在finally块里归还否则这条连接会被永久占用。最标准的写法就是try-with-resourcestry (Jedis jedis pool.getResource()) { jedis.set(hello, world); }如果项目里用的是老式代码至少也要确保finally里调用jedis.close()。我看到过不少反面案例获取连接之后正常路径有close异常路径没close结果线上压力一大连接池的活跃数只增不减最终报Could not get a resource from the pool。这种问题特别难查因为代码一眼看过去好像“有释放”实际上只释放了一半。还有一个原则不要在多个方法之间传递借用中的Jedis对象。一个方法内部借、用、还闭环是最清晰的做法。如果有人把连接存在成员变量或ThreadLocal里那基本就是在给未来的事故埋雷。4.2 高频数据类型操作示例字符串、哈希、列表、集合、有序集合这五种数据类型在Jedis里操作起来基本和Redis命令一致。举个例子一个典型的用户token写入场景try (Jedis jedis pool.getResource()) { String userId u_10001; jedis.setex(login:token: userId, 7200, someTokenValue); jedis.hset(user:profile: userId, lastLoginAt, String.valueOf(System.currentTimeMillis())); jedis.rpush(user:recent: userId, String.valueOf(System.currentTimeMillis())); }这里setex带过期时间适合存session、验证码hset适合更新用户资料字段rpush适合记录最近行为列表。如果你存的对象不是String需要自己做序列化Jedis也支持byte[]类型的方法可以用Jedis#set(byte[], byte[])。序列化方案建议统一不要这个key用JDK序列化、那个key用JSON不然混在一起非常痛苦。4.3 Pipeline把多次网络往返压缩成一次如果在一个循环里连续执行几百次set每次都要走一次请求响应网络RTT就成了瓶颈。Pipeline的用途就是把一批命令打包发给Redis再一次读回所有结果。try (Jedis jedis pool.getResource()) { Pipeline pipe jedis.pipelined(); for (int i 0; i 1000; i) { pipe.set(batch:key: i, String.valueOf(i)); } pipe.sync(); }这里有一个容易踩的坑pipeline执行期间连接是独占的。如果用了pipe之后又去调用同一个jedis对象的其他方法命令顺序会乱响应也对不上号。正确做法是要么用pipeline期间不要再发其他命令要么就把这批操作单独放到一条连接上完成用完归还。批量条数也要控制不建议一次塞几万条。我一般习惯每批几百条分批sync。数据量太大时Redis服务端会积压大量响应反而增加内存和网络压力。4.4 事务与Lua脚本Jedis里做事务比较直观就是multi()之后执行一系列命令最后exec()try (Jedis jedis pool.getResource()) { Transaction tx jedis.multi(); tx.set(a, 1); tx.incr(a); ListObject results tx.exec(); }事务期间的连接同样是独占的而且有一个隐蔽的坑如果执行过程中抛了异常忘了exec()也忘了discard()这个连接会一直处于MULTI状态。归还到池里之后下一个线程借到它发一条普通命令进来Redis会把它当作事务里的一条命令排队返回结果完全错乱。这种“事务状态残留污染连接”的问题偶发性极强排查起来非常头疼。所以我的建议是能不用事务就不用改用Lua脚本。Lua脚本本身在Redis里就是原子执行的而且只需要一次请求。比如扣库存时要先判断存量再扣减if tonumber(redis.call(GET, KEYS[1])) 0 then return redis.call(DECR, KEYS[1]) else return -1 endJedis端执行try (Jedis jedis pool.getResource()) { Object result jedis.eval(luaScript, java.util.Collections.singletonList(stock:1001), java.util.Collections.emptyList()); }比multi/exec那套少很多心智负担。4.5 连接池模式下严禁select非0库连接池复用连接之后有一个很容易被忽略的规则不要在一个连接上动态执行SELECT切换数据库。原因在于连接是有“当前数据库”状态的。线程A借了一连接select(1)后归还线程B从池里拿到同一连接它仍然处于db1但代码可能默认操作db0数据读写就全错位了。这个Bug表现特别诡异因为偶尔才发生复现起来要看运气。碰到这个问题几乎只能靠检查代码里的select去定位非常浪费时间。正确的做法有三个一是在创建连接池时就统一指定database参数所有连接固定用同一个库二是不同业务库用不同的JedisPool实例互不影响三是最推荐的一个Redis实例只用db0用key的前缀或命名空间来区分业务。多库在Redis里并不是一个好的隔离手段反而会把连接状态问题引入到你的Java代码里。5. 连接报错排查链路从Connection refused到连接池耗尽连接相关的报错很多人在网上搜到一条就试一条效率很低。这里我按异常类型整理一套排查链路先分类再定位能省不少时间。5.1 先按异常类型分类再决定往哪查Jedis抛出的异常大致可以分成以下几类每类对应的排查方向完全不一样异常类型代表信息排查重点连接建立失败Connection refused / UnknownHost网络、端口、服务进程、bind配置认证失败NOAUTH / ERR Client sent AUTH密码、ACL用户名连接池耗尽Could not get a resource from the pool连接泄漏、池参数、阻塞命令超时Read timed out / connect timed outconnectTimeout/soTimeout、慢命令拿到报错先看是哪一类再去执行对应的排查步骤比无头苍蝇一样搜报错原文高效得多。5.2 Connection refused的完整排查链路Connection refused是新手最常遇见的错误排查顺序我建议这样走先用redis-cli -h 你的host -p 6379 ping测试确认服务端通不通。如果redis-cli能通Java连不上检查进程监听地址。在Redis服务器上执行netstat -anp | grep 6379看监听的是127.0.0.1还是0.0.0.0。很多默认配置只监听本机回环地址外部IP自然连不上。检查Redis的bind配置和protected-mode。如果服务要跨机器访问把bind改成0.0.0.0或者具体的内网IP同时注意保护模式会影响非本机连接。检查防火墙和安全组这个在云上特别常见本机怎么都通从别的机器连就拒绝。检查Jedis配置里的host和port是否写错记住端口默认是6379不是6370。最后看服务端连接数上限。如果maxclients满了日志里会出现连接被拒绝的相关记录。本机redis-cli能通Java程序连不上的绝大多数都卡在bind、protected-mode和防火墙这三件事上。5.3 NOAUTH与认证相关报NOAUTH Authentication required说明Redis设置了密码但客户端没传密码。反过来报ERR Client sent AUTH, but no password is set说明Redis没设置密码客户端却传了。这两个报错信息非常明确基本看了一眼就知道是哪边不匹配。麻烦一点的是Redis 6之后的ACL场景。如果Redis启用了ACL客户端需要传username和password不传username时默认是default用户。有些内部环境把default用户的权限收敛得很严格连上之后很多key不可见报错却是permission denied。处理办法很简单在Jedis构造时把username和password一起传进去。另一个注意点是Jedis 4.x构造方法的参数顺序。密码传错位置可能导致编译不报错运行起来却表现诡异。我遇到过把密码传成database的启动完全正常一执行命令就一堆怪异错误。建议用带参数名的构造方法或IDE提示仔细检查每个参数。5.4 Could not get a resource from the pool先怀疑连接泄漏出现Could not get a resource from the pool时第一反应不应该是“连接池太小调大maxTotal”而是先怀疑有没有借了没还的地方。排查步骤是这样打印连接池当前指标pool.getNumActive()、pool.getNumIdle()、pool.getNumWaiters()。写一个简单的并发测试模拟业务调用观察numActive曲线。如果numActive持续上涨但numIdle却在下降而且压力下去后numActive不回落那基本可以断定有连接泄漏。检查代码里所有获取连接的路径特别是catch块里有没有遗漏close()。try-with-resources是最稳妥的因为它在正常和异常路径都会自动关闭。检查是否存在BLPOP这类阻塞命令。一个线程阻塞在BLPOP上连接就一直借在手里不归还连接池再大也顶不住而且特征就是numWaiters慢慢涨、numActive长期卡在高位。还可以用定时任务实时观察池的状态ScheduledExecutorService scheduler Executors.newScheduledThreadPool(1); scheduler.scheduleAtFixedRate(() - { logger.info(active{}, idle{}, waiters{}, pool.getNumActive(), pool.getNumIdle(), pool.getNumWaiters()); }, 0, 30, TimeUnit.SECONDS);无脑调大maxTotal只是掩盖问题泄漏的连接最终会把服务端的连接数上限也吃满到时候换来的就是Connection refused了。5.5 超时类异常connectTimeout与soTimeout要分清超时异常需要区分是建立连接超时还是命令执行超时。connect timeout发生在TCP连接都还没建立成功的阶段说明网络不通、对端负载过高或者防火墙丢包。这时候排查网络和主机状态把connectTimeout设成1到2秒比较合理不要让它无限等下去。soTimeout也叫read timeout不一样它发生在连接已经建立、命令已经发出去之后迟迟等不到响应。可能原因包括命令本身慢比如执行了KEYS *扫全库、某个大key读取耗时、对方服务端卡顿、网络抖动。如果业务里有BLPOP这类阻塞命令一定要给soTimeout留足余地或者干脆单独为这类命令设置更大的超时。重试这块我的经验是只对幂等的读操作做有限重试写操作不要盲目重试。Redis本身很快出现超时大概率是网络或服务端异常这时候重试太多次反而可能把问题放大。我一个内部项目里就吃过这个亏某个瞬时网络抖动自动重试把请求量放大了好几倍Redis服务端CPU直接飙高。后来改成最多重试一次重试间隔几十毫秒情况就好很多。6. 连接层的性能优化与线上运维经验连接池配好、代码格式写规范之后连接层基本就稳了。但线上跑久了还是会遇到一些性能毛刺和资源问题这节聊几个我在实际操作中很受用的经验。6.1 连接预热把冷启动毛刺干掉很多人不知道JedisPool创建出来之后并不会立刻创建minIdle条连接而是懒创建。也就是说即使你配了minIdle10刚启动时池子里也是空的。如果应用一上线就有流量涌入第一批请求全在忙着建立TCP连接延迟会明显偏高等到连接建好了流量已经过去了。解决方法是启动完成后做一次显式预热for (int i 0; i minIdle; i) { try (Jedis jedis pool.getResource()) { jedis.ping(); } catch (JedisException e) { // 预热失败不要影响启动记录日志或告警即可 } }这段代码要把minIdle作为外部参数传入不要写死。预热时机放在应用启动后期比如Spring的ApplicationReadyEvent里。有一点要注意预热失败不应该抛异常导致应用启动失败因为Redis可能在应用启动后才就绪预热失败打一条告警就够了。6.2 连接池指标监控用数据代替瞎猜前文提到的getNumActive()、getNumIdle()、getNumWaiters()可以做成实时监控曲线。对这三条曲线我的读法是numWaiters持续大于0说明连接不够用或者单条连接被占用时间太长。这时候要看业务里有没有慢操作、阻塞命令。numActive长期接近maxTotal连接池容量接近上限需要扩容或者优化业务对Redis的调用次数。numIdle长期非常高说明maxIdle配大了低峰期冗余连接占资源。监控不需要太复杂用之前那个定时任务把指标推到监控系统就行。如果团队不想写代码JedisPoolConfig也有setJmxEnabled(true)选项可以在本地开启JMX再用Java Mission Control之类的工具看池子状态。不过我个人更推荐把指标上报到统一监控平台因为线上排查时能看到历史曲线比看到瞬时值重要得多。6.3 超时与重试的细节超时参数在配置里通常是一个整体Jedis构造里的2000既会当作connectTimeout也会当作soTimeout。如果业务不同命令差异大可以拆开设置。举例JedisPool pool new JedisPool( config, 127.0.0.1, 6379, 1000, // connectTimeout 2000, // soTimeout password, 0 );connectTimeout设1秒足够因为同机房环境下TCP握手通常就是几十毫秒。soTimeout要根据操作来定普通的get/set 2000毫秒绰绰有余但像BLPOP这类命令就要单独放宽否则业务还没等到阻塞返回就超时了。我还想强调一个实践经验把Redis访问统一封装到一个类里。这样超时配置、重试逻辑、监控上报都可以集中管理而不是散落在各个业务代码中。我见过太多项目里每个Service自己new一个JedisPool或者自己写超时处理出问题时根本无法全局调整只能逐个改代码。6.4 什么时候坚持用Jedis什么时候换Lettuce最后一个话题很多人纠结到底用Jedis还是Lettuce我简单对比一下不吹不黑对比维度JedisLettuce连接模型池化一个连接对应一个Jedis实例多路复用单连接可承载并发请求线程安全非线程安全依赖连接池线程安全可共享实例API风格同步命令映射直白同步异步响应式典型场景老项目、事务多、喜欢可控微服务实例多、连接资源紧张同机房低延迟场景下两者的性能差异通常不是瓶颈真正的瓶颈在序列化、网络和业务调用次数。所以我的观点很明确不要为了换而换。如果项目用的Jedis一直稳定团队也熟悉迁移到Lettuce的收益大概率覆盖不了风险和返工成本。反过来如果服务实例数量巨大每个实例都维护一个较大连接池会把Redis服务端连接数顶得很高那Lettuce的多路复用就值得看一看了。我自己维护的项目里两种客户端都有。老模块因为代码套路固定、业务偏事务型继续用Jedis连接池参数按本文的思路调好之后非常稳新模块在连接资源紧张时用了Lettuce也没有出现性能问题。说实话真正常导致线上问题的不是客户端本身而是连接池参数没按业务调、借还纪律没执行好、监控没跟上。把这些基础做扎实连接Redis这个最底层的环节才不会成为事故的源头。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑