本地测试没问题,上线之后出问题了,怎么排查是哪方面的问题?

1 个回答

  • 沙之影
    明知不可为而为之就是我最大的诚意
    # 一、30 分钟快速定位路线图 1. **界定问题类型** * 功能错误(报错、错单、逻辑错) * 性能劣化(慢、超时、突增 5xx) * 稳定性(间歇失败、偶发超时、雪崩) 2. **先看指标再看日志** * 网关/服务 5xx、P99 延时、QPS、错误率;上游/下游是否同步抖动。 * 迅速圈定是**单服务**还是**链路某段**的问题。 3. **对比环境差异(Diff)** * 配置、依赖、版本、资源限额、网络策略、数据规模/热点、特性开关。 * 任何不一致都优先怀疑。 4. **最小化可复现** * 在**同一线上环境**做灰度/只打一个实例、固定流量重放;必要时二分回滚找出“哪次发布引入的”。 # 二、定位思路(按层分治) ## 1)配置/发布差异(最常见) * **配置中心**:prod.yml 与 local/dev 有无差异(超时时间、连接池、读写分离、重试、限流)。 * **环境变量/特性开关**:线上默认开启了缓存、限流、熔断、异步等,本地关闭。 * **编译产物**:JDK 版本、依赖冲突(同名不同包版本)、资源文件缺失(区分大小写的路径/容器只读)。 * **数据库/ES/Redis**:连接串、库/索引/前缀错连(连到只读库或旧索引)。 * **时区/时钟**:UTC vs CST;NTP 漂移导致签名、token、缓存过期异常。 ## 2)数据与流量(本地数据小、线上数据大) * **数据规模**:线上数据量/索引碎片/冷热分布导致计划退化(DB 执行计划与本地不同)。 * **脏/坏数据**:枚举值/NULL/字符集不一致触发边界逻辑。 * **热点键**:同一 key 高并发导致锁/队列堆积。 * **真实流量模式**:突发、抖动、长尾;压测曲线≠真实线上。 ## 3)资源与系统(容器/主机差异) * **CPU/内存/FD**:cgroup 限额、OOM/Kill、ulimit -n 过低、GC 频繁。 * **磁盘/IO/网络**:I/O wait 高、丢包、DNS 解析慢、TLS 握手慢。 * **内核参数**:TIME\_WAIT、SYN backlog、端口耗尽。 * **安全策略**:防火墙/网段策略/ServiceMesh/Sidecar 拦截。 ## 4)并发与时序(本地不出、线上出) * **线程池/连接池**:池满、饥饿、拒绝策略;回收慢造成“看似卡死”。 * **锁/事务**:行锁/表锁/死锁;跨系统超时+重试→幂等缺失导致脏写/重复扣减。 * **读写分离与延迟**:支付类读到从库旧数据导致误判。 * **分布式一致性**:消息投递至少一次未幂等、Outbox/Inbox 缺失、补偿逻辑没兜住。 # 三、不同问题的“对症检查清单” ## A. 直接 5xx/功能异常 * 应用日志:按 **traceId** 从网关一路串起来看第一处抛错。 * 查**配置项**:和本地差异(超时、重试、路由、索引名、库名、时区)。 * 权限/证书:云秘钥、SSL 证书、白名单、跨 VPC 访问。 ## B. 变慢/超时 * **系统层**:`top|vmstat 1|iostat -x 1|sar -n TCP,DEV 1` * **网络层**:`ss -s | ss -antp | dig +trace | curl -w` 观测握手/解析 * **JVM**:堆/元空间、GC、线程数、类加载 * `jcmd <pid> VM.flags`、`jstat -gcutil <pid> 1s 30` * `jstack -l <pid>` 看阻塞点/死锁 * **DB/ES**:慢 SQL、执行计划、连接池 hit、Replica 落后 * MySQL:`show processlist; explain analyze; show engine innodb status\G` * ES:`/_cluster/health?level=indices`、慢查询日志 * **缓存**:命中率、穿透/击穿(热点 key 限流/本地缓存/互斥锁) ## C. 间歇失败/偶发 * **时间/时区/过期**:token、签名、缓存 TTL、CRON 触发点。 * **熔断/限流**:是否被网关或下游限流;看看滑窗内错误率。 * **雪崩链路**:上游重试叠加下游超时→排查重试策略与时间预算。 # 四、Java / Spring 常用“证据抓取” * **Actuator**:`/actuator/health|metrics|threaddump|heapdump` * **线程池状态**:活跃/队列/拒绝次数(暴露自定义 metrics)。 * **GC 日志**(务必在 prod 开):`-Xlog:gc*,safepoint:file=/logs/gc.log:utctime,level,tags` * **统一 traceId**:日志加 `%X{traceId}`,链路一跳查到头。 * **JFR**(线上短时打开):快速定位热点与锁争用。 # 五、数据库/ES“本地没事线上出事”的典型坑 * **执行计划不同**:统计信息旧、SQL 文字略差异导致索引没命中(用 **force index** 或重写 where 顺序)。 * **隔离级别**:线上 REPEATABLE READ + 长事务导致锁等待;读写分离延迟导致错读。 * **迁移缺失**:表结构/索引在 prod 未升级;ES mapping 在 prod 老版本导致字段类型冲突。 * **连接池**:`maxLifetime` > DB `wait_timeout` 导致“半死连接”;池满拒绝。 # 六、网络与基础设施检查 * **DNS**:线上走内网 DNS,本地走公共 DNS;打开 `RES_OPTIONS=single-request-reopen` 规避某些环境下 AAAA 延时。 * **IPv6/IPv4**:只在 prod 开了 IPv6,目标服务不通。 * **代理/Sidecar**:mTLS/证书、路由规则、超时/重试与应用层叠加。 # 七、回滚与二分法 * 若无法快速定位: 1. **回滚到上一个稳定版本**验证是否发布引入; 2. **灰度二分**:逐步还原变更(代码/配置/依赖/基础设施)找出元凶; 3. 对“可疑开关”做 **动态开关** 与 **限流保护**。 # 八、上线前的“防坑清单”(以后少掉坑) * **配置对齐**:prod 仿真环境,开同等限额/同等中间件。 * **真实数据回放**:选取生产样本流量脱敏回放做压测。 * **可观测性**:RED(Rate/Errors/Duration)、关键池化指标、慢日志、trace 必开。 * **幂等与超时重试**:明确上/下游重试窗口与幂等键。 * **熔断/隔离/限流**:保护下游与自己(线程池隔离、舱壁、降级兜底)。 * **灰度 + 自动回滚**:错误率/延时阈值触发。
    发布于 2025-08-13 15:22 •IP属地 北京
    举报
    评论
    收藏