DM数据库排查

你提到的问题是:

  • 达梦数据库中执行一条 SELECT ... OFFSET 0 ROWS FETCH NEXT 100001 ROWS ONLY 查询;

  • Spring 应用中(使用 Druid 数据源)耗时 40-60 秒

  • 但使用数据库工具(如达梦客户端)执行同样 SQL,耗时仅 1-2 秒

这个差异很大,以下是可能的原因分析和排查建议:


🧠 一、根本原因分析

✅ 1. JDBC/Druid 预取行数(fetchSize)不合理

重点关注!

JDBC 默认一次从数据库中 “拉取”行数较少(例如 10 或 100),当你查 100001 行时,会进行多次网络往返(round trip),非常耗时

而达梦数据库客户端可能默认设置为更大的 fetchSize(例如 5000 或 10000),一次性取大量数据,速度快很多

这是你这种情况最常见的性能瓶颈之一。


✅ 2. 慢在 Druid 连接池的数据传输而非 SQL 执行本身

你可以通过设置:

yaml
spring.datasource.druid.filters=stat

或者通过打印 SQL 执行时间 vs 方法总耗时 来区分:

  • SQL 本身可能只耗时 2 秒;

  • 数据通过 JDBC 和 Java 对象序列化耗时 30+ 秒。


✅ 3. 返回的数据量太大,Java 端内存压力高

返回 100001 行,每行 7 个字段(可能含字符串),最终 Java 会构造 10 万个对象,加上 JVM GC、对象创建、框架封装等,性能开销大。

特别是:

  • 如果你用了 MyBatis,每行都会转为 Java Bean,成本更高;

  • 如果使用了分页插件或者 ResultHandler,也可能被慢处理。


✅ 4. 未使用预编译参数

使用 PreparedStatement 才会避免重复 SQL 编译。

你可以打开 druid SQL 日志(或设置 log4jdbc)看是否有参数替换。若是拼接 SQL 方式,也会导致执行计划不缓存,变慢。


✅ 5. 网络或连接层存在瓶颈

  • Spring 应用和数据库不在同一台机器上;

  • 网络带宽低或 RTT(往返时间)高;

  • JDBC/Druid 层对大结果集传输不友好。


🛠 二、解决建议(分步骤)

✔️ 1. 增加 fetchSize

在你的代码中手动设置:

java
PreparedStatement ps = connection.prepareStatement(sql); ps.setFetchSize(10000); // 建议 5000 ~ 20000,根据业务调整

如果你使用的是 MyBatis,可以通过拦截器或者配置中设置:

xml
<select id="selectLargeData" fetchSize="10000"> SELECT ... </select>

或者在 Druid 数据源配置中加入:

yaml
spring.datasource.druid.default-fetch-size=10000

✔️ 2. 确认是否慢在网络还是 SQL 执行本身

打印 SQL 执行时间:

java
long start = System.currentTimeMillis(); ResultSet rs = stmt.executeQuery(); System.out.println("SQL 执行耗时:" + (System.currentTimeMillis() - start));

再测量 while(rs.next()) 的数据读取和转对象的耗时,两者对比可得知:

  • SQL 本身慢 → 优化索引、执行计划

  • 数据传输慢 → 优化 fetchSize 或网络


✔️ 3. 限制返回行数,避免大结果集分页

你目前用的是:

sql
FETCH NEXT 100001 ROWS ONLY

建议使用合理分页,比如:

  • FETCH NEXT 5000 ROWS ONLY

  • 或者限制查询条件,避免一次拉取 10w 条。


✔️ 4. 确认是否使用了分页插件或 Wrapper

一些分页插件(如 PageHelper)可能会自动包裹 SQL、再执行 count 查询,拖慢性能。务必确认。


✔️ 5. 启用 SQL 日志 / 分析执行计划

开启 druid 监控或数据库日志:

yaml
spring.datasource.druid.filters=stat,wall,log4j spring.datasource.druid.log-slow-sql=true spring.datasource.druid.slow-sql-millis=3000

查看慢 SQL 或执行计划确认是否走索引。


🧪 三、验证建议

  1. 确认 SQL 执行耗时 vs 总耗时

  2. 尝试设置 fetchSize = 10000 测试性能

  3. 将返回数量从 10w 降到 5000,观察差异

  4. 确认 Druid 是否设置了 maxWait, socketTimeout, readTimeout 等参数


✅ 总结重点

原因优先级解决建议
JDBC 默认 fetchSize 太小 🟥 高 设置 fetchSize=10000
大量数据对象转换慢 🟧 中 分页限制条数 / 批处理
网络瓶颈或数据拉取慢 🟧 中 查看数据库与应用是否同机
SQL 实际执行很快 🟨 次 需打印 JDBC 耗时进行验证
SQL 没走索引 🟨 次 查看执行计划,确认是否需要优化
posted @ 2025-08-06 16:49  箛獨劍  阅读(50)  评论(0)    收藏  举报