全部学科
NodeJS全栈
nodejs
Python全栈
python
小程序首页
📅 2026-05-20 6 分钟 ✍️ juanwangdev

开发调试技巧

日志不仅是查看 SQL 的工具,更是排查问题的利器。本文介绍如何通过日志快速定位慢查询、参数错误、N+1 问题等常见开发陷阱。

定位慢查询

通过日志时间差判断

DEBUG 日志自带时间戳,通过对比 SQL 执行前后的时间差可判断查询耗时:

SQL
2026-05-20 10:30:15.120 [main] DEBUG c.e.mapper.UserMapper.selectById - ==>  Preparing: SELECT ...
2026-05-20 10:30:15.122 [main] DEBUG c.e.mapper.UserMapper.selectById - ==> Parameters: 1(Long)
2026-05-20 10:30:15.830 [main] DEBUG c.e.mapper.UserMapper.selectById - <==      Total: 1

10:30:15.12210:30:15.830,耗时约 708ms,明显偏慢。

排查步骤

步骤操作说明
1复制日志中的 SQL 到数据库客户端去掉 Preparing: 前缀,替换 ? 为实际参数
2使用 EXPLAIN 分析执行计划检查是否走索引、是否全表扫描
3检查是否缺少索引对 WHERE、ORDER BY 字段加索引
4检查数据量表数据过大时考虑分页或归档
Java
-- 复制 SQL 并手动执行
EXPLAIN SELECT id, username, email FROM users WHERE id = 1;

排查参数传递错误

参数类型不匹配

日志中参数值后面会标注类型,如 (String)(Long)(Integer)

XML
==> Parameters: admin(String)

如果 Mapper 方法签名与 XML 中参数类型不一致,会报异常:

Java
// Mapper 接口
User selectById(@Param("id") Long id);
XML
<!-- XML 映射 -->
<select id="selectById" resultType="User">
    SELECT * FROM users WHERE id = #{id}
</select>

如果传入的是字符串 "1" 而非 Long,日志显示:

YAML
==> Parameters: 1(String)

此时可能触发类型转换异常,通过日志中的类型标注快速发现。

参数为空值

参数为 null 时,日志会明确显示:

YAML
==> Parameters: null

常见于对象属性未赋值就传入查询条件,导致查询结果不符合预期。

集合参数展开

使用 <foreach> 的 IN 查询,日志中会逐个显示参数:

XML
List<User> selectByIds(@Param("ids") List<Long> ids);
text
<select id="selectByIds" resultType="User">
    SELECT * FROM users WHERE id IN
    <foreach item="id" collection="ids" open="(" separator="," close=")">
        #{id}
    </foreach>
</select>

日志输出:

text
==>  Preparing: SELECT * FROM users WHERE id IN (?,?,?)
==> Parameters: 1(Long), 2(Long), 3(Long)

如果集合为空,日志显示:

text
==>  Preparing: SELECT * FROM users WHERE id IN ()

此时 SQL 语法报错,可在日志中快速发现空集合问题。

定位 N+1 查询问题

使用 <collection><association> 延迟加载时,日志中会出现大量重复 SQL:

text
10:30:15 DEBUG c.e.mapper.UserMapper.selectAll - ==>  Preparing: SELECT * FROM users
10:30:15 DEBUG c.e.mapper.UserMapper.selectAll - ==> Parameters: 
10:30:15 DEBUG c.e.mapper.OrderMapper.findByUserId - ==>  Preparing: SELECT * FROM orders WHERE user_id = ?
10:30:15 DEBUG c.e.mapper.OrderMapper.findByUserId - ==> Parameters: 1(Long)
10:30:15 DEBUG c.e.mapper.OrderMapper.findByUserId - ==>  Preparing: SELECT * FROM orders WHERE user_id = ?
10:30:15 DEBUG c.e.mapper.OrderMapper.findByUserId - ==> Parameters: 2(Long)
10:30:15 DEBUG c.e.mapper.OrderMapper.findByUserId - ==>  Preparing: SELECT * FROM orders WHERE user_id = ?
10:30:15 DEBUG c.e.mapper.OrderMapper.findByUserId - ==> Parameters: 3(Long)

1 次主查询 + N 次关联查询 = N+1 问题。

解决方式

方案说明
使用 JOIN 查询在 XML 中编写 JOIN SQL,一次性查出关联数据
开启嵌套查询的懒加载设置 lazyLoadingEnabled=true,按需加载
使用 <collection>fetchType="eager"明确指定 eager/lazy 策略

事务回滚排查

开启事务日志可看到提交和回滚过程:

text
logging:
  level:
    org.apache.ibatis.transaction: DEBUG

日志输出:

text
10:40:20 DEBUG o.a.i.t.jdbc.JdbcTransaction - Opening JDBC Connection
10:40:20 DEBUG o.a.i.t.jdbc.JdbcTransaction - Setting autocommit to false
10:40:21 DEBUG o.a.i.t.jdbc.JdbcTransaction - Committing JDBC Connection

如果发生回滚,日志中会显示:

text
10:40:21 DEBUG o.a.i.t.jdbc.JdbcTransaction - Rolling back JDBC Connection

结合异常堆栈,可快速定位是哪一步操作触发了回滚。

调试实战技巧

技巧一:临时开启 SQL 日志

text
# 开发环境临时开启全部 MyBatis 日志
logging:
  level:
    org.apache.ibatis: DEBUG

注意:仅用于开发调试,生产环境务必关闭,否则日志量巨大。

技巧二:SQL 格式化输出

MyBatis 内置的 STDOUT_LOGGING 可直接在控制台输出格式化 SQL:

text
<settings>
    <setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>

输出效果:

text
==>  Preparing: SELECT id, username, email FROM users WHERE id = ? 
==> Parameters: 1(Long)
<==    Columns: id, username, email
<==        Row: 1, admin, admin@example.com
<==      Total: 1

无需引入额外依赖,适合快速调试。

技巧三:对比预期与实际 SQL

开发时先写好预期 SQL,再通过日志对比实际执行的 SQL 是否一致:

对比项说明
SQL 结构WHERE 条件、JOIN 是否正确
参数顺序#{} 占位符顺序是否对应
动态 SQL<if> 条件是否按预期拼接

要点总结

  • 通过日志时间戳差值判断 SQL 执行耗时,结合 EXPLAIN 分析慢查询
  • 日志中参数类型标注帮助发现类型不匹配问题,null 值一目了然
  • N+1 问题表现为大量重复关联查询,可通过 JOIN 或懒加载解决
  • 事务日志显示回滚时,结合异常堆栈定位触发点
  • STDOUT_LOGGING 无需额外依赖,适合快速调试场景

文章存放路径:articles/MYBATIS/入门/日志配置与调试/开发调试技巧.md

📝 发现内容有误?点击此处直接编辑

← 上一篇 SQL 日志输出
下一篇 → 日志框架集成
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

长按或扫描二维码,立即体验

扫码体验小程序
马上就来
使用微信扫描二维码
立即体验完整题库