You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SpringBoot应用读写Cloud Spanner触发LeakedSessionException排查

Spanner会话泄漏异常排查与修复

根因判定

你看到的com.google.cloud.spanner.SessionPool$LeakedSessionException不是配置缺失导致的阻断式业务异常,是Spanner客户端会话池的资源泄漏检测告警:会话从连接池借出后未被正常归还,当该会话对象被JVM垃圾回收时,会话池的兜底检测逻辑会打出该warn级别日志,因此当前业务读写还能正常执行,不会立刻出现请求失败。

你捕获到的异常核心信息如下:

Exception:
com.google.cloud.spanner.SessionPool$LeakedSessionException
Message:
Session was checked out from the pool at 2022-07-13T17:50:07.372Z
Stacktrace 核心调用路径:
com.google.cloud.spanner.SessionPool.checkoutSession(SessionPool.java:2037)
com.google.cloud.spanner.DatabaseClientImpl.singleUse(DatabaseClientImpl.java:101)
com.google.cloud.spring.data.spanner.core.SpannerTemplate.executeQuery(SpannerTemplate.java:448)

风险评估

  • 低流量短期运行场景无直接业务影响:当前连接池仍有可用会话分配给新请求,业务读写逻辑可正常执行
  • 高流量长期运行场景存在明确故障风险:泄漏的会话会持续占用连接池配额,当池内会话全部被泄漏占用后,新请求无法获取数据库连接,会直接抛出连接池耗尽异常,导致所有Spanner操作失败
  • 存在不必要的资源开销:泄漏会话对应的Spanner服务端连接资源不会被主动释放,会持续产生额外的资源占用成本

触发原因

结合你使用的6.10.1版本客户端和调用栈信息,触发原因主要有两类:

  1. 客户端版本已知bug:6.10.1版本的Spanner客户端存在singleUse()只读查询场景的会话归还逻辑缺陷,当查询返回的ResultSet未被显式关闭时,即使查询执行完成,会话也不会被自动归还到连接池,和你调用栈中DatabaseClientImpl.singleUse的调用路径完全匹配
  2. 代码资源释放不规范:如果存在手动从客户端获取会话、手动管理事务的逻辑,没有在finally块或try-with-resources块中完成资源关闭,也会触发会话泄漏

修复方案

按优先级执行即可:

  1. 升级客户端版本(首选方案)
    将google-cloud-spanner依赖升级到6.24.0及以上稳定版本,该版本已经修复了singleUse查询场景的会话自动归还bug,配合对应兼容版本的Spring Data Spanner,无需修改业务代码即可解决绝大多数场景的泄漏问题。
    你当前使用的依赖配置如下,直接替换version字段为高版本稳定版号即可:
    <dependency>
      <groupId>com.google.cloud</groupId>
      <artifactId>google-cloud-spanner</artifactId>
      <version>6.10.1</version>
      <scope>compile</scope>
    </dependency>
    
  2. 规范资源释放逻辑
    排查所有Spanner操作相关代码,所有ResultSet、手动获取的Session、手动开启的TransactionContext必须放在try-with-resources块中执行,确保无论执行成功还是抛出异常,资源都能被自动关闭:
    // 错误写法:不关闭ResultSet,会触发会话泄漏
    ResultSet rs = spannerTemplate.executeQuery(query, params, option);
    while (rs.next()) {
        // 处理结果逻辑
    }
    
    // 正确写法:自动关闭资源
    try (ResultSet rs = spannerTemplate.executeQuery(query, params, option)) {
        while (rs.next()) {
            // 处理结果逻辑
        }
    }
    
    手动管理事务的场景,必须将commit/rollback逻辑放在finally块中,避免异常分支跳过资源释放。
  3. 临时兜底配置(升级过渡阶段使用)
    若暂时无法升级版本,可通过配置会话池参数临时降低泄漏影响:
    # 自动回收检测到泄漏的会话
    spring.cloud.gcp.spanner.session-pool.close-sessions-on-leak=true
    # 配置会话池容量上下限,避免池被快速打满
    spring.cloud.gcp.spanner.session-pool.min-sessions=10
    spring.cloud.gcp.spanner.session-pool.max-sessions=400
    # 回收超过空闲时长的会话
    spring.cloud.gcp.spanner.session-pool.max-idle=10m
    
    注意:该配置仅作临时缓解,无法根治会话泄漏,升级版本和修正代码逻辑才是最终解决方案

内容的提问来源于stack exchange,提问作者codingNubie

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 13:48:19