连接池又双叒枯竭了:Hikari + Stream/Cursor 未关闭的排查与配置复盘
1. 连接池又双叒枯竭了从一次 queryForStream 泄漏说起Hikari 连接池枯竭是 Java 后端最常见的线上事故之一而queryForStream返回的 Stream 忘记 close、MyBatis 的 Cursor 提前 return 没关闭是其中最隐蔽的一类。它不像慢 SQL 那样一眼能看出来业务线程数不满、CPU 不高但数据库连接池 active 直接顶到maximumPoolSize接口统一卡 30 秒然后报Connection is not available。这篇就聚焦这个场景Hikari 连接池被 Stream/Cursor 占住不还怎么用leakDetectionThreshold定位、怎么用 try-with-resources 修复、怎么用连接池指标验证泄漏真的消除了。适合正在用 Spring JdbcTemplate 流式查询、MyBatis Cursor 做大表扫描的 Java 后端同学尤其是遇到过「偶发 502、日志刷 Hikari 超时」但没找到根因的人。先说结论queryForStream返回的 Stream 是懒读取的它背后绑着一个真实的 JDBC Connection不 close 就不归还findFirst()、anyMatch()这类短路操作会提前结束但不会自动释放连接。MyBatis 的CursorT同理本质是流式结果集加连接占用异常路径或提前 return 都会泄漏。下面按「现象 → 定位 → 修复 → 验证」走一遍每一步都能直接复制。2. 前置准备TaoToken 与本地环境在动手排查之前先把模型对话和接入文档的入口准备好方便边查 API 边对照参数。我平时用 TaoToken 做接口联调和文档查询它的模型对话入口可以直接问 Hikari 参数含义接入文档里有完整的 Key 申请和调用示例。模型对话问参数、问报错https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite接入文档看 API 规范https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewriteAPI Keys 管理生成调用凭证https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite控制台看用量和状态https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewriteAPI 基址代码里配置https://taotoken.net/api本地环境需要JDK 8、Spring Boot 2.x/3.x、HikariCPSpring Boot 默认自带、MySQL 5.7/8.0。确认spring-boot-starter-jdbc或mybatis-spring-boot-starter已引入Hikari 是默认连接池不需要额外加依赖。如果你用的是 Druid 或 Tomcat JDBC参数名不同但泄漏原理一致。注意leakDetectionThreshold只在排障阶段打开它会给每个连接加一个定时检查生产长期开启有额外开销。定位到堆栈后立刻关掉。3. 可复制配置Hikari 关键参数与修复骨架3.1 Hikari 参数怎么配才不容易被榨干先给一份可以直接抄的application.yml重点看maximum-pool-size、connection-timeout、max-lifetime、leak-detection-threshold四个。spring: datasource: url: jdbc:mysql://127.0.0.1:3306/order_db?useSSLfalseserverTimezoneAsia/Shanghai username: app_user password: your_password hikari: pool-name: HikariPool-Order maximum-pool-size: 50 minimum-idle: 10 connection-timeout: 10000 validation-timeout: 3000 idle-timeout: 600000 max-lifetime: 1700000 leak-detection-threshold: 10000几个参数的关系要理清connection-timeout是拿不到连接时的等待上限默认 30 秒用户感知就是卡 30 秒然后 500建议压到 10 秒快失败max-lifetime必须小于 MySQL 的wait_timeout默认 1800 秒否则会借到已经被服务端断开的连接报Failed to validate connectionleak-detection-threshold设成 10000 表示连接借出超过 10 秒没还就打印堆栈这是定位泄漏的核心开关。3.2 queryForStream 的修复骨架反例长这样直接返回 Stream短路操作后连接不还。// 反例不要这样写 public OptionalOrderDTO findFirst(LocalDate day) { StreamOrderDTO stream jdbcTemplate.queryForStream( SELECT id,user_id,amount FROM t_order WHERE gmt_create ? AND gmt_create ?, ps - { ps.setTimestamp(1, Timestamp.valueOf(day.atStartOfDay())); ps.setTimestamp(2, Timestamp.valueOf(day.plusDays(1).atStartOfDay())); }, (rs, rowNum) - map(rs) ); return stream.filter(o - o.getAmount().compareTo(BigDecimal.ZERO) 0) .findFirst(); }正确写法用 try-with-resources 兜住 Stream无论正常返回、提前 return 还是抛异常close 都会执行。public OptionalOrderDTO findFirst(LocalDate day) { try (StreamOrderDTO stream jdbcTemplate.queryForStream( SELECT id,user_id,amount FROM t_order WHERE gmt_create ? AND gmt_create ?, ps - { ps.setTimestamp(1, Timestamp.valueOf(day.atStartOfDay())); ps.setTimestamp(2, Timestamp.valueOf(day.plusDays(1).atStartOfDay())); }, (rs, rowNum) - map(rs))) { return stream.filter(o - o.getAmount().compareTo(BigDecimal.ZERO) 0) .findFirst(); } }如果数据量可控几千行以内更省心的做法是直接物化到内存用普通query返回 List再对 List 做 stream 操作连接在方法返回前就已经归还。ListOrderDTO list jdbcTemplate.query(sql, rowMapper, day.atStartOfDay(), day.plusDays(1).atStartOfDay()); return list.stream().filter(o - o.getAmount().compareTo(BigDecimal.ZERO) 0).findFirst();3.3 MyBatis Cursor 的修复骨架Cursor 同样必须包在 try-with-resources 里提前 return 时 try 块会自动 close。Mapper public interface OrderMapper { Select(SELECT id,user_id,amount FROM t_order WHERE status 1) Options(fetchSize Integer.MIN_VALUE) CursorOrderDO scanPaid(); } public long sumPaid() { try (CursorOrderDO cursor orderMapper.scanPaid()) { long total 0; for (OrderDO o : cursor) { total o.getAmount().longValue(); if (total 100000) { return total; } } return total; } }Options(fetchSize Integer.MIN_VALUE)是 MySQL 驱动流式读取的开关只在真正需要流式时加普通查询别乱加否则每次都是一行一行取反而更慢。Cursor 和 Stream 都不能跨线程传递读取线程和连接是绑定的丢到线程池里异步消费必然出问题。3.4 统一封装防止团队误用光靠 code review 挡不住最好封一个工具方法让调用方没法忘记 close。public final class Streams { private Streams() {} public static T, R R withStream(StreamT s, FunctionStreamT, R fn) { try (s) { return fn.apply(s); } } } // 使用 return Streams.withStream( jdbcTemplate.queryForStream(sql, ps, rm), st - st.filter(o - o.getAmount().compareTo(BigDecimal.ZERO) 0).findFirst().orElse(null) );4. 验证请求日志与连接池指标怎么确认泄漏消除4.1 用 leakDetectionThreshold 抓堆栈线上临时开启泄漏检测可以通过启动参数或配置中心动态下发。java -Dhikari.pool.HikariPool-Order.leakDetectionThreshold10000 -jar order-service.jar触发后日志会打印类似这样的堆栈直接指向没关的代码行。HikariPool-Order - Connection leak detection triggered, stack trace follows at com.xxx.order.dao.OrderDao.scanByDate(OrderDao.java:58) at com.xxx.order.service.OrderService.listOrders(OrderService.java:143)看到堆栈后对照第 3 节的骨架改代码改完再观察日志里还有没有新的 leak 记录。4.2 看连接池指标Spring Boot Actuator 暴露 Hikari 指标后可以直接查 active 和 pending。curl -s http://127.0.0.1:8080/actuator/metrics/hikaricp.connections.active curl -s http://127.0.0.1:8080/actuator/metrics/hikaricp.connections.pending修复前 active 长期贴着 50/50pending 排队修复后 active 回落到 10~15pending 归零。这是最直观的验证信号。4.3 看数据库侧会话SHOW PROCESSLIST;修复前会看到大量Sending data状态挂着不走或者空闲但没释放的会话修复后这些长时间残留消失。配合SHOW STATUS LIKE Threads_connected看连接数是否稳定。4.4 压测复现与回归用 JMeter 或 wrk 对下单列表接口打 200 并发持续 5 分钟观察 P99 和错误率。修复前 P99 会飙到 30 秒以上并出现 500修复后 P99 回到百毫秒级无Connection is not available报错。这一步做完基本可以确认泄漏被拔掉了。5. 本篇常见错排查报错一Connection is not available, request timed out after 30000ms这是池子被占满的典型表现不是网络问题。先开leakDetectionThreshold抓堆栈重点搜queryForStream、Cursor、Stream关键字。如果堆栈指向业务代码里的流式查询按第 3 节改。报错二Failed to validate connection通常是max-lifetime大于 MySQLwait_timeout借到了服务端已断开的连接。把max-lifetime调到小于wait_timeout比如 1700000 毫秒对 1800 秒并确认validation-timeout合理。报错三改了 try-with-resources 但 active 还是高检查是不是有 Stream 被返回到了方法外或者 Cursor 被丢进了线程池异步消费。Stream/Cursor 的生命周期必须和连接绑定在同一个线程、同一个方法内。另外确认没有在Transactional里做流式查询后长时间持有事务不提交连接也不还。报错四leakDetectionThreshold开了但没日志确认 pool-name 和参数前缀对得上-Dhikari.pool.HikariPool-Order.leakDetectionThreshold里的 pool-name 要和配置里一致。用配置中心动态刷新时注意 Hikari 部分参数不支持热更新需要重启或重建数据源。报错五MyBatis Cursor 迭代到一半抛异常try-with-resources 会在异常路径自动 close但如果你手动cursor.close()又在外层 catch 里再操作 cursor会报已关闭。统一用 try-with-resources不要在 finally 里重复 close。6. 长期编码与 Agent 场景的接入建议如果你在做的是长期编码、Agent 工具链或者需要频繁调用模型做代码审查单次对话入口不够用建议走 Coding Plan把模型能力接进日常开发流。Coding Plan长期编码/Agent 场景https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewriteClaude Code 接入说明https://taotoken.net/claude-code?utm_sourcetaotoken_aicg_blog_endutm_contentclaude_codeutm_campaignrewrite回到连接池这件事最后补一个我踩过的坑queryForStream配合Transactional时即使 Stream 关了如果事务没提交连接也不会立刻归还。排查时别只盯着 Stream把事务边界一起看。另外maximum-pool-size不是越大越好50 已经能扛住大多数业务盲目调到 200 只会把数据库连接数打满问题从应用层转移到 DB 层。先把泄漏堵住再谈扩容。

相关新闻

二分法寻找峰值:无序数组也能二分?原理与边界详解

二分法寻找峰值:无序数组也能二分?原理与边界详解

前阵子复盘二分法系列题目,LeetCode 162《寻找峰值》让我印象最深。第一眼看到这道题,我相信多数人的反应和我一样:数组元素不是有序的,凭什么用二分法?要判断nums[i]是不是峰值,不是得同时比较左右两个邻居…

2026/9/30 8:21:03 阅读更多 →
GSM网络拓扑结构全解:从BSS/NSS分工到接口排障实践

GSM网络拓扑结构全解:从BSS/NSS分工到接口排障实践

简介:GSM网络拓扑结构讲义以PPT形式系统梳理了数字移动通信网络的核心架构与信令机制,适合通信工程专业学生、网络规划与优化工程师快速建立GSM系统整体认知。内容从网络拓扑入手,详细讲解TMSC、MSC、BSC、BTS及HLR/VLR/AUC等关键网元的功能与…

2026/9/30 8:20:54 阅读更多 →
把deepin跑进WSL 2:DDE桌面安装与踩坑全指南

把deepin跑进WSL 2:DDE桌面安装与踩坑全指南

把deepin跑进WSL里,听上去有点像“给我的Windows装个Linux桌面”的绕远路操作,但WSL 2配合WSLg发展到现在,这已经不是个噱头,而是一套能正经干活、能体验完整DDE桌面的方案。无论你是想在不折腾双系统和虚拟机的前提下&#xff0c…

2026/9/30 8:20:53 阅读更多 →

最新新闻

Science Skills药物化学技能集:ChEMBL、PubChem与openFDA如何驱动AI智能体完成药物研发数据查询

Science Skills药物化学技能集:ChEMBL、PubChem与openFDA如何驱动AI智能体完成药物研发数据查询

Science Skills药物化学技能集:ChEMBL、PubChem与openFDA如何驱动AI智能体完成药物研发数据查询 【免费下载链接】science-skills GDM Science Skills to speed up agentic scientific workflows with better grounding and higher token efficiency. Integrate ins…

2026/9/30 16:45:26 阅读更多 →
英飞凌TC264卡丁快跑:PMSM电机控制与GTM定时器实战解析

英飞凌TC264卡丁快跑:PMSM电机控制与GTM定时器实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/30 16:45:26 阅读更多 →
格西烽火:可编程串口协议解析引擎与变量驱动调试平台

格西烽火:可编程串口协议解析引擎与变量驱动调试平台

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/30 16:45:26 阅读更多 →
VMware虚拟机安装与使用全攻略:避坑指南与网络配置详解

VMware虚拟机安装与使用全攻略:避坑指南与网络配置详解

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/30 16:45:26 阅读更多 →
专利撰写实战:从权利要求书到说明书的核心技巧解析

专利撰写实战:从权利要求书到说明书的核心技巧解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/30 16:45:26 阅读更多 →
treg Idempotent-Key 多租户隔离原理:你的请求为何不会拿到别人的结果

treg Idempotent-Key 多租户隔离原理:你的请求为何不会拿到别人的结果

treg Idempotent-Key 多租户隔离原理:你的请求为何不会拿到别人的结果 【免费下载链接】treg OpenRouter for agent tools. Join community here: https://discord.gg/6mQYYfFMAn 项目地址: https://gitcode.com/GitHub_Trending/treg/treg treg 是一个面向 …

2026/9/30 16:44:21 阅读更多 →

日新闻

Base64 图片头部特征识别:从文件头到格式判断的完整指南

Base64 图片头部特征识别:从文件头到格式判断的完整指南

1. 项目概述:为什么说看懂 base64 图片头部是基本功这几年跟 base64 打交道的机会越来越多,后端接口返回图片、前端渲染验证码、小程序里存小图、还有一些老系统导出报表,动不动就给你一段长到怀疑人生的 base64 字符串。很多人拿到字符串就直…

2026/9/30 0:00:35 阅读更多 →
Java公交站牌广告管理系统:JSP+Servlet+MySQL实战落地指南

Java公交站牌广告管理系统:JSP+Servlet+MySQL实战落地指南

简介:本资源是一份面向Java初学者与课程设计学生的公交站牌广告灯箱管理系统毕业设计文档,聚焦城市公共广告资源信息化管理痛点,提供从需求分析到技术实现的完整方案。文档采用标准学术论文结构,含摘要、英文摘要、目录及五章正文…

2026/9/30 0:00:35 阅读更多 →
用 Redis Lua 构建大模型 API 多租户原子配额治理体系

用 Redis Lua 构建大模型 API 多租户原子配额治理体系

我去年年底接了一个内部 AI 平台的治理需求,背景很直接:公司把 DeepSeek、MiniMax 这类大模型 API 统一封装成内部网关,开放给几个业务团队用。结果第一个月账单出来,额度直接超了 4 倍。仔细查日志,发现原因并不复杂—…

2026/9/30 0:00:35 阅读更多 →

周新闻

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解 【免费下载链接】spirula-studio Cross-vendor 3D Gaussian Splatting trainer - video to splat to mesh, Vulkan or CUDA. 项目地址: https://gitcode.com/GitHub_Trending/sp/spirula-studio Sp…

2026/9/30 13:14:22 阅读更多 →
SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南 模板网站太丑不够用?别急着加滤镜,那是治标不治本。很多老板盯着后台流量掉得眼红,却还在纠结首页Banner的圆角是不是3像素。这就像穿着西装去挖土,姿势不对,努力白费。我整理这份 速查手册…

2026/9/29 16:41:41 阅读更多 →
FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏

FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏

FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏 【免费下载链接】FireRed-OpenStoryline FireRed-OpenStoryline is an AI video editing agent that transforms manual editing into intention-driven directing through natural language …

2026/9/30 13:14:49 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/29 19:29:29 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/29 5:58:00 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/30 15:27:04 阅读更多 →