如何结合日志系统分析 bug,关系型数据库
日志系统怎么建
一套好用的日志系统,至少要解决:
- 结构化:JSON 格式,字段稳定,便于检索和聚合。
- 关联:traceId、spanId、requestId、userId、orderId。
- 分级:DEBUG、INFO、WARN、ERROR,生产环境控制级别。
- 集中化:ELK/EFK、Loki、Splunk、云日志服务。
- 保留和索引:按天/大小滚动,重要日志长期保留。
- 安全:脱敏手机号、身份证、密码、token,不记录敏感信息。
- 告警:错误率、关键字、超时、队列堆积。
推荐日志字段:
{
"timestamp": "2026-10-18T12:00:00.123Z",
"level": "ERROR",
"service": "order-service",
"traceId": "a1b2c3",
"spanId": "d4e5f6",
"userId": "u1001",
"orderId": "NO10001",
"method": "POST",
"path": "/api/orders",
"status": 500,
"durationMs": 1200,
"error": "duplicate key value violates unique constraint",
"message": "create order failed"
}
排查 bug 的流程
1. 收集事实
- 发生时间、用户、订单号、请求 ID。
- 复现步骤、环境、版本。
- 影响范围:单个用户还是批量。
- 最近变更:发布、配置、数据、依赖。
2. 用关联 ID 串起来
- 从网关日志拿 traceId。
- 在应用日志里查同一 traceId。
- 查数据库、缓存、MQ 的相关操作。
- 按时间排序,还原调用链。
3. 看错误模式
- 异常堆栈、错误码、超时、重试。
- 是否集中在某个实例、某个接口、某个用户。
- 是否和发布、流量、数据量相关。
4. 查数据库状态
- 订单状态、金额、库存是否符合预期。
- 是否有重复数据、脏数据、锁等待。
- 事务是否提交/回滚。
5. 形成假设并验证
- 在测试环境复现。
- 加日志或断点验证假设。
- 修复后加回归用例。
关系型数据库排查
慢查询
MySQL:
-- 开启慢查询日志
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;
-- 查看慢查询
SHOW VARIABLES LIKE 'slow_query_log_file';
PostgreSQL:
-- 使用 pg_stat_statements
SELECT query, calls, total_exec_time, mean_exec_time
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 10;
执行计划
EXPLAIN ANALYZE
SELECT *
FROM orders
WHERE user_id = 1001
AND status = 'pending'
ORDER BY created_at DESC
LIMIT 20;
重点看:
- 是否走索引
- 扫描行数
- 回表次数
- 排序和临时表
- 预估行数和实际行数差异
索引问题
常见索引失效:
- 对字段做函数运算:
WHERE DATE(created_at) = '2026-10-18' - 隐式类型转换:字符串字段用数字查询
- 前导模糊匹配:
LIKE '%abc' - OR 条件部分字段无索引
- 联合索引不满足最左前缀
- 数据分布导致优化器放弃索引
锁和事务
MySQL:
SHOW PROCESSLIST;
SHOW ENGINE INNODB STATUS;
SELECT * FROM information_schema.innodb_trx;
PostgreSQL:
SELECT * FROM pg_stat_activity WHERE state != 'idle';
SELECT * FROM pg_locks;
关注:
- 长事务
- 行锁/表锁等待
- 死锁
- 连接数打满
- 未提交事务
数据一致性
- 唯一约束、外键约束是否生效。
- 事务隔离级别是否符合预期。
- 并发下是否出现超卖、重复下单、金额不一致。
- 幂等键和唯一索引是否覆盖。
常见 bug 模式
| 现象 | 可能原因 |
|---|---|
| 订单重复创建 | 缺幂等键、重试、并发 |
| 库存超卖 | 非原子扣减、锁范围不对 |
| 金额不一致 | 计算精度、优惠券叠加、事务回滚 |
| 接口偶发 500 | 连接池耗尽、超时、空指针 |
| 列表慢 | 缺索引、大偏移、N+1 |
| 数据查不到 | 事务未提交、读写分离延迟、缓存脏数据 |
| 死锁 | 加锁顺序不一致、长事务 |
| 连接数打满 | 连接未释放、池太小、慢查询 |
一个排查示例
现象:用户下单后订单一直显示“待支付”,但支付平台已扣款。
- 用订单号查应用日志,拿到 traceId。
- 按 traceId 查网关、订单服务、支付服务日志。
- 发现支付回调到达,但订单服务更新状态时抛了唯一键冲突。
- 查数据库:
orders表有重复的支付流水号,说明回调重复处理。 - 查代码:回调没有幂等校验,重试时重复插入。
- 修复:支付流水号加唯一索引,回调先查后写,放在同一事务。
- 回归:模拟重复回调,验证只处理一次。
最佳实践
- 日志必须带 traceId 和业务 ID。
- 错误日志包含上下文,但不要泄露敏感信息。
- 慢查询、连接池、锁等待要有监控和告警。
- 关键业务状态变更记录审计日志。
- 排查时先看时间线,再看异常,再看数据。
- 修复后补回归用例和监控指标。
面试回答模板
我会先用 traceId、用户 ID、订单号把网关、应用、数据库和 MQ 的日志串起来,按时间线还原调用链,重点看异常、超时、重试和数据状态。数据库侧会看慢查询、执行计划、索引、锁等待和事务,确认是查询慢、锁冲突还是数据不一致。定位后先复现,再修复,最后补回归用例和监控告警。日志本身要结构化、带关联 ID、脱敏,并接入集中检索和告警。