🔍 1. 线上问题排查 — 从现象到根因的完整思路
🧠 排查思路总流程
1
确认现象:到底什么问题?接口慢?报错?挂了?
为什么先确认:不同现象排查路径完全不同,方向错了浪费大量时间
↓
2
缩小范围:前端?网关?服务?数据库?网络?
为什么缩小:像医生问诊,先判断是哪个科室的问题,不是一上来就全身CT
↓
3
看日志:搜索关键字,看异常堆栈
为什么先看日志:成本最低、信息最多,90%的问题看日志就能定位
↓
4
工具定位:jstack/jmap/Arthas/tcpdump 按需选择
为什么按需选择:每个工具都有场景限制,用错工具会干扰线上服务
↓
5
找到根因:不是"接口报错",而是"第42行空指针因为订单ID没传"
为什么追到根因:只修表面问题会反复出现,要修根本原因
↓
6
修复验证:修完要验证:1) 问题是否消失 2) 是否引入新问题 3) 加监控防复发
为什么验证:修完不代表修好了,没验证的修复等于没修
📋 排查命令速查表
| 场景 | 第一步命令 | 为什么先做这个 | 第二步 | 第三步 |
| 接口500 | grep "Exception" app.log | 最快定位报错代码行 | 看异常堆栈 | 找到根因修复 |
| 接口慢 | tail -f access.log | grep ">1s" | 先确认是偶发还是持续 | Arthas trace定位 | 优化SQL/加缓存 |
| CPU飙高 | top -Hp <pid> | 先找到哪个线程吃CPU | jstack看线程栈 | 修死循环/正则 |
| 内存泄漏 | jmap -histo <pid> | head -20 | 先看什么对象最多 | jmap -dump + MAT分析 | 修复泄漏引用 |
| 死锁 | jstack <pid> | jstack自动检测死锁 | 看锁获取顺序 | 统一加锁顺序 |
| GC频繁 | jstat -gcutil <pid> 1000 | 看各代内存使用率 | 调整堆/GC算法 | 观察是否改善 |
| 网络不通 | ping <ip> | 先确认网络通不通 | telnet端口 | tcpdump抓包 |
| 磁盘满 | df -h | 先看哪个分区满 | du -sh找大文件 | 清理/扩容 |
📝 关键命令详解
在文件中搜索关键字,排查时最常用的第一个命令
排查问题时日志通常几GB,不可能肉眼翻,必须用grep快速定位
接口报错看异常、搜索订单号、过滤特定时间段的日志
# 搜索异常关键字(显示行号)
grep -n "NullPointerException" app.log
# 搜索某个订单的所有日志
grep "ORDER-2024-001234" app.log
# 搜索最近10分钟的ERROR日志
find /var/log/app -name "*.log" -mmin -10 | xargs grep "ERROR"
# 搜索异常并显示前后3行上下文(看清完整堆栈)
grep -n -C 3 "Caused by" app.log
# 统计ERROR日志出现次数(判断严重程度)
grep -c "ERROR" app.log
实时查看日志文件末尾,排查线上问题时"盯着看"
线上问题往往需要看"正在发生什么",tail -f 可以实时看到新产生的日志
服务刚报错需要实时观察、复现问题时盯着日志看
# 实时跟踪日志(最常用)
tail -f /var/log/app/app.log
# 实时跟踪 + 过滤ERROR
tail -f app.log | grep "ERROR"
# 看最后100行
tail -100 app.log
# 同时跟踪多个日志文件
tail -f app.log error.log
分页查看大文件,支持搜索、跳转,比cat/More强大很多
日志文件动辄几GB,cat直接刷屏,less可以搜索和翻页
日志文件太大需要搜索、需要前后翻页看上下文
# 打开大文件
less app.log
# 进入less后的操作:
# /关键字 → 向下搜索
# ?关键字 → 向上搜索
# n → 下一个搜索结果
# N → 上一个搜索结果
# G → 跳到文件末尾
# g → 跳到文件开头
# q → 退出
# F → 类似tail -f实时跟踪
💡 排查最佳实践
- 先看日志再动工具:90%的问题看日志就能定位,不要上来就jmap/jstack,这些工具对线上有影响
- 带上traceId:日志中一定要有traceId,微服务场景下靠时间戳搜日志是灾难
- 保留现场:出问题时先dump再重启,别急着重启把现场毁了
- 二分法排查:不确定哪行代码有问题?注释一半,看问题是否消失
- 对比法:正常环境 vs 异常环境,找差异,往往差异就是根因
- 时间线梳理:按时间线看"什么时间改了什么 → 什么时间开始出问题"
⚠️ 常见坑
- grep大文件卡死:几个GB的日志直接grep会很慢 → 用
grep -m 10 限制输出行数先看前几条
- 日志级别没开DEBUG:排查时发现INFO日志不够 → 临时改日志级别:
Arthas: logger --name com.example --level DEBUG
- 容器里没有工具:Docker容器通常没有jstack/jmap → 用
docker exec 或用Arthas
- jmap导致STW:
jmap -dump 会触发Full GC导致服务暂停 → 非紧急时用 -XX:+HeapDumpOnOutOfMemoryError 自动dump
- 日志被轮转了:今天的问题搜不到,因为昨天的日志已经被压缩成 .gz → 用
zgrep "关键字" *.gz
☕ 2. JVM 工具 — 医院类比理解每把"手术刀"
jps = 挂号台
→
先查有哪些Java进程
jstat = 体温计
→
快速量体温,看GC和内存概况
jmap = CT扫描
→
深入看内存细节,但耗资源
jstack = 心电图
→
看线程在干什么,死锁一目了然
Arthas = 微创手术
→
不用重启,直接观察和修改运行中的JVM
MAT = 病理分析
→
离线分析heap dump,找到泄漏根因
🧠 JVM问题排查思路流程
1
jps -l — 找到Java进程PID
为什么:所有JVM工具都需要PID,这是第一步
↓
2
jstat -gcutil <pid> 1000 — 快速看GC和内存概况
为什么:jstat几乎零开销,先快速判断是内存问题还是GC问题
↓
3
按症状选择深入工具:
- CPU高 →
top -Hp + jstack
- 内存问题 →
jmap -histo + jmap -dump + MAT
- 线程卡住 →
jstack
- 不确定 →
Arthas dashboard 全局看
为什么:每个工具都有适用场景,用错工具浪费时间还可能影响服务
📋 JVM命令速查表
| 命令 | 一句话 | 为什么用 | 开销 | 什么时候用 |
jps -l | 列出Java进程 | 所有工具都需要PID | 零 | 排查第一步 |
jstat -gcutil | 看GC和内存 | 最快了解JVM健康状况 | 极低 | GC频繁/内存疑似不足 |
jmap -histo | 看对象分布 | 快速看什么对象占内存最多 | 中 | 内存泄漏/OOM |
jmap -dump | 导出堆快照 | 离线用MAT详细分析 | 高(会STW) | 深度内存分析 |
jstack | 线程dump | 看线程在做什么,自动检测死锁 | 中 | 死锁/线程卡/CPU高 |
jinfo | 看JVM参数 | 确认线上JVM配置是否正确 | 低 | 确认堆大小/GC算法等 |
📝 每个命令详解
列出当前机器上所有Java进程的PID和主类名
JVM的所有工具都需要PID,这是排查的第一步。没有PID,后面的jstat/jmap/jstack都用不了
每次排查JVM问题的第一步,或者用Arthas前先知道PID
# 列出所有Java进程
jps -l
# 输出示例:
# 12345 com.example.OrderApplication
# 67890 sun.tools.jps.Jps # 这是jps自己
# 为什么用 -l:
# 不加 -l 只显示短类名,多个服务可能同名
# 加 -l 显示完整包名,精确区分
每隔N毫秒打印一次GC和各代内存使用率百分比
jstat是开销最小的JVM监控工具,对线上几乎无影响。先看概况再决定用重量级工具,避免上来就jmap导致STW
1)怀疑GC频繁 2)想快速了解内存使用情况 3)确认JVM参数是否合理
# 每1秒打印一次,看10次
jstat -gcutil 12345 1000 10
# 输出解读:
# S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
# 0.00 45.23 67.89 82.34 95.12 91.56 234 1.234 5 2.345 3.579
# | | | | | | | | | | |
# | | | | | | | | | | └─ 总GC时间(秒)
# | | | | | | | | | └─ Full GC次数
# | | | | | | | | └─ Full GC总耗时
# | | | | | | | └─ Young GC总耗时
# | | | | | | | └─ Young GC次数
# | | | | | | └─ 元空间使用率
# | | | | └─ 方法区/元空间使用率
# | | | └─ 老年代使用率(>80%要警惕)
# | | └─ Eden区使用率
# | └─ Survivor1区使用率
# └─ Survivor0区使用率
# 🧠 关键看什么:
# 1. O列(老年代) > 80% 且持续增长 → 可能内存泄漏
# 2. FGC列(Full GC)持续增长 → GC频繁,需要调优
# 3. FGCT列(Full GC时间)很长 → STW严重,影响用户体验
统计JVM堆中各种对象的数量和大小,按大小排序
OOM时第一反应是"什么对象占满了内存",jmap -histo不用导出dump就能快速看,比jmap -dump轻量得多
1)内存持续增长怀疑泄漏 2)OOM后想快速看什么对象最多 3)不想用MAT做深度分析时
# 快速查看堆中对象分布(推荐先用这个)
jmap -histo 12345 | head -20
# 输出示例:
# num #instances #bytes class name
# 1: 1234567 123456789 [B (byte数组)
# 2: 567890 45678901 java.lang.String
# 3: 123456 23456789 com.example.Order
# 🧠 怎么看:
# - #instances 列:对象数量异常多 → 可能在批量创建没释放
# - #bytes 列:占用内存异常大 → 这就是内存消耗大户
# - class name 列:看到自己项目的类名 → 重点关注
# - 只看head -20:通常问题就在前几个类中
# 只看存活对象(排除GC后可回收的,更准确)
jmap -histo:live 12345 | head -20
# ⚠️ 注意:加了 :live 会触发Full GC!
导出整个JVM堆的快照文件(hprof),用MAT离线深度分析
jmap -histo只能看"什么对象多",但看不到"谁引用了这些对象导致无法回收"。导出dump后用MAT可以看GC Roots引用链,找到泄漏根因
1)jmap -histo发现可疑对象但不确定谁引用 2)内存泄漏需要精确定位 3)OOM后深度分析
# 手动导出堆dump
jmap -dump:format=b,file=/tmp/heap.hprof 12345
# 🧠 为什么路径用 /tmp:
# dump文件通常很大(和堆大小相当),/tmp 通常有足够空间
# OOM时自动dump(推荐在JVM启动参数中配置)
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/data/dumps/
# 🧠 为什么推荐自动dump:
# OOM发生时服务已经快挂了,你可能来不及手动jmap
# 自动dump可以在OOM瞬间保存现场,重启后分析
# 用MAT分析(本地电脑操作)
# 1. 下载 Eclipse MAT
# 2. 打开 heap.hprof 文件
# 3. 点击 Leak Suspects(泄漏嫌疑报告)
# 4. 查看 Dominator Tree(占内存最大的对象树)
# 5. 查看 GC Roots 引用链(谁引用导致无法回收)
# ⚠️ 警告:jmap -dump 会触发 Full GC (STW)
# 线上服务会暂停几秒到几十秒,高峰期慎用!
打印JVM中所有线程的堆栈信息,自动检测死锁
线程问题(死锁、卡住、CPU高)只能通过线程栈定位。jstack能看到每个线程在执行什么代码、在等什么锁
1)怀疑死锁 2)请求卡住不返回 3)CPU高要找哪个线程 4)线程池耗尽
# 获取线程dump
jstack 12345 > thread_dump.txt
# 🧠 为什么重定向到文件:
# 直接输出到终端不好搜索,重定向后可以用grep搜索特定线程
# CPU高的标准排查流程:
# Step 1: 找到Java进程PID
jps -l
# Step 2: 找到CPU最高的线程ID
top -Hp 12345
# 假设线程ID是 67890
# 🧠 为什么要 top -Hp:
# 普通top只能看到进程级别,-Hp能看到进程内每个线程
# Java进程内部有几十上百个线程,要找吃CPU的那个
# Step 3: 线程ID转16进制
printf "%x\n" 67890
# 输出: 10932
# 🧠 为什么要转16进制:
# jstack输出的线程ID是16进制,top显示的是10进制
# 必须转换才能对应上
# Step 4: 在dump中搜索
jstack 12345 | grep "0x10932" -A 30
# 死锁检测(jstack自动报告)
jstack 12345 | grep -A 20 "DEADLOCK"
# 输出:
# Found one Java-level deadlock:
# "Thread-1" waiting to lock ... which is held by "Thread-0"
# "Thread-0" waiting to lock ... which is held by "Thread-1"
# 建议多dump几次对比
jstack 12345 > dump1.txt
sleep 5
jstack 12345 > dump2.txt
# 🧠 为什么dump两次:
# 一次可能是巧合,两次都在同一位置 → 大概率就是问题点
查看和修改JVM运行时参数
排查时经常需要确认"线上JVM配置到底对不对",比如堆大小、GC算法、是否开启了某些参数
1)确认线上JVM配置 2)不确定某个参数是否生效 3)需要临时调整日志级别
# 查看所有JVM参数
jinfo 12345
# 查看某个特定参数
jinfo -flag MaxHeapSize 12345
jinfo -flag UseG1GC 12345
# 🧠 常用查看项:
# -XX:+PrintGCDetails → GC日志是否开启
# -Xmx / -Xms → 堆大小设置
# -XX:+UseG1GC → 用的什么GC算法
阿里开源的Java诊断工具,不用重启就能观察和修改运行中的JVM
jstack/jmap都需要PID而且功能有限,Arthas可以直接看方法调用、参数、返回值,甚至热更新代码。线上排查神器
1)接口慢要定位哪个方法耗时 2)想看方法的入参和返回值 3)确认线上代码版本 4)临时改日志级别
# 启动Arthas
java -jar arthas-boot.jar
# 选择你的Java进程
# 常用命令速查:
# 全局面板(CPU/内存/线程/GC一目了然)
dashboard
# 追踪方法耗时(最常用!)
trace com.example.service.OrderService createOrder
# 输出:
# +---[200ms] createOrder()
# +---[150ms] checkStock() ← 这个方法最慢!
# +---[30ms] saveOrder()
# +---[20ms] publishEvent()
# 观察方法入参和返回值
watch com.example.service.OrderService createOrder "{params, returnObj, throwExp}" -x 2
# 反编译确认线上代码版本
jad com.example.service.OrderService
# 查看线程状态
thread
thread -n 3 # CPU最高的3个线程
thread -b # 找死锁
# 临时修改日志级别(不用重启!)
logger --name com.example --level DEBUG
# 生成火焰图
profiler start
# 等30秒
profiler stop --format html
💡 JVM工具最佳实践
- 先用轻量工具再看重量级:jstat(零开销)→ jmap -histo(轻量)→ jmap -dump(重量级,会STW)
- JVM启动参数必须配:
-XX:+HeapDumpOnOutOfMemoryError + -XX:+PrintGCDetails + -Xloggc,出问题时才有现场
- jstack建议dump多次:一次可能是巧合,两次都在同一位置才是问题点
- Arthas比原生工具好用:能trace/watch就不要用jstack/jmap手动分析
- 容器环境提前准备:Docker里通常没有JDK只有JRE,jmap/jstack可能用不了 → 提前装Arthas
- dump文件及时转移:dump文件很大,占磁盘会导致其他问题 → 分析完就删
⚠️ JVM常见坑
- jmap -dump导致STW:线上高峰期千万别用!先用
jmap -histo 快速看,确认需要深度分析再dump
- jmap -histo:live触发Full GC:加了 :live 会先做Full GC再统计,等于强制清理了一遍,看出来的数据不是真实情况
- top -Hp的PID和jstack的nid对不上:top显示10进制,jstack显示16进制,必须 printf "%x" 转换
- Arthas选错进程:一台机器多个Java进程时,选错了就看不到想要的数据 → 先 jps -l 确认
- dump文件占满磁盘:8G堆的dump约8G,加上压缩可能更大 → dump前
df -h 检查剩余空间
🐳 3. Docker & K8s — 容器化命令速查
🧠 Docker日常操作思路
1
docker ps — 看容器在不在、状态正不正常
为什么先ps:容器起不来是最常见的问题,先看状态
↓
2
docker logs — 看容器日志找报错原因
为什么看日志:容器Exited/Restarting通常都是应用报错导致
↓
3
docker exec — 进入容器内部排查
为什么exec:有些问题日志看不出来,需要进容器看配置文件、网络、进程
↓
4
docker inspect — 看容器详细配置
为什么inspect:端口映射、环境变量、挂载卷等配置是否正确
📋 Docker命令速查表
| 命令 | 一句话 | 为什么用 | 常用场景 |
docker ps | 看运行中的容器 | 确认容器状态 | 部署后验证、排查容器挂没挂 |
docker ps -a | 看所有容器(含已停止) | Exited的容器也需要查看 | 容器起不来、莫名退出 |
docker logs | 看容器日志 | 容器内应用报错只能看日志 | 容器异常退出、接口报错 |
docker exec -it | 进入容器内部 | 像SSH登录服务器一样看容器 | 查看配置、手动调试 |
docker build | 构建镜像 | 代码→镜像的第一步 | 打包部署 |
docker run | 启动容器 | 运行镜像 | 本地测试、部署服务 |
docker compose up | 启动多容器 | 一键启动全套依赖环境 | 本地开发(MySQL+Redis+App) |
docker inspect | 看容器配置详情 | 确认IP/端口/挂载/环境变量 | 网络不通、配置问题 |
docker images | 查看本地镜像 | 确认镜像版本、清理磁盘 | 回滚时确认旧版本镜像在不在 |
📝 关键命令详解
查看正在运行的容器列表
部署后的第一步就是确认容器在不在跑。STATUS列能看出容器是Up还是Exited/Restarting
每次部署后、排查容器问题时
# 看运行中的容器
docker ps
# 输出关键列:
# CONTAINER ID IMAGE STATUS PORTS NAMES
# a1b2c3d4 myapp:1.0 Up 2 hours 0.0.0.0:8080->8080 myapp
# 看所有容器(包括已停止的)
docker ps -a
# 🧠 为什么加 -a:
# 容器启动失败后会变成Exited状态,ps看不到,必须 -a
# 只看容器ID(用于批量操作)
docker ps -q
查看容器内应用的日志输出
容器里没有SSH,不能直接登录看日志。docker logs是看容器日志的唯一方式
容器启动失败、应用报错、需要看运行时日志
# 看容器日志
docker logs myapp
# 实时跟踪日志(最常用)
docker logs -f myapp
# 看最后100行
docker logs --tail 100 myapp
# 看最近30分钟的日志
docker logs --since 30m myapp
# 带时间戳(看日志产生的时间)
docker logs -t myapp
在运行中的容器内执行命令,通常用来进入容器内部
容器内环境可能和宿主机不同,需要进容器确认文件、网络、进程状态
1)确认配置文件是否正确 2)在容器内手动测试连通性 3)查看容器内进程
# 进入容器内部(最常用)
docker exec -it myapp /bin/bash
# 🧠 为什么要 -it:
# -i 交互模式,-t 分配伪终端
# 不加就是执行完命令就退出,不能交互
# Alpine镜像没有bash,用sh
docker exec -it myapp /bin/sh
# 不进入,直接执行一条命令
docker exec myapp cat /etc/hosts
docker exec myapp ping mysql
构建镜像和启动容器
Docker化部署的标准流程:代码→build成镜像→run成容器。理解这个流程才能做CI/CD
打包部署、本地测试新镜像
# 构建镜像(注意最后的 . 代表当前目录)
docker build -t myapp:1.0 .
# 🧠 为什么用 -t:
# -t 给镜像打标签(名字:版本),不然后面引用不了
# 启动容器
docker run -d --name myapp -p 8080:8080 myapp:1.0
# 🧠 每个参数为什么:
# -d 后台运行(不占终端)
# --name 给容器起名字(不然系统随机命名,不好记)
# -p 端口映射(宿主机:容器)
# 带环境变量启动
docker run -d --name myapp \
-p 8080:8080 \
-e SPRING_PROFILES_ACTIVE=prod \
-e DB_HOST=mysql \
-v /data/logs:/app/logs \
myapp:1.0
# 🧠 为什么要 -v 挂载日志:
# 容器删了日志就没了,挂载到宿主机可以持久保存
一键启动多容器编排(开发环境必备)
本地开发需要MySQL+Redis+应用一起启动,手动一个个docker run太痛苦。compose一个命令搞定
本地开发环境搭建、多人协作统一环境
# 一键启动(后台运行)
docker compose up -d
# 看所有服务状态
docker compose ps
# 看某个服务日志
docker compose logs -f app
# 停止所有服务
docker compose down
# 重新构建并启动
docker compose up -d --build
📋 K8s 命令速查表
| 命令 | 一句话 | 为什么用 | 什么时候用 |
kubectl get pods | 看Pod状态 | 部署后第一步就是看Pod在不在跑 | 部署验证、排查Pod问题 |
kubectl describe pod | 看Pod详情 | Pod起不来时看Events找原因 | ImagePullBackOff/CrashLoopBackOff |
kubectl logs | 看Pod日志 | 应用报错看日志 | 接口报错、启动失败 |
kubectl exec | 进入Pod | Pod内排查问题 | 确认配置、手动测试 |
kubectl apply -f | 应用YAML配置 | 部署/更新资源 | 发布新版本、修改配置 |
kubectl scale | 扩缩容 | 流量高峰扩容 | 大促/流量突增 |
kubectl rollout undo | 回滚 | 发布出问题快速恢复 | 发布后出Bug |
kubectl get svc | 看Service | 确认服务暴露方式 | 网络不通、端口问题 |
# 看Pod状态(排查第一步)
kubectl get pods
kubectl get pods -n production # 指定namespace
kubectl get pods -o wide # 看IP和所在节点
# Pod起不来?看详情
kubectl describe pod order-service-abc123
# 🧠 重点看 Events 部分:
# ImagePullBackOff → 镜像拉不下来
# CrashLoopBackOff → 容器启动就崩
# OOMKilled → 内存不够
# 看日志
kubectl logs order-service-abc123
kubectl logs -f order-service-abc123 # 实时跟踪
kubectl logs --tail=100 order-service-abc123 # 最后100行
# 进入Pod
kubectl exec -it order-service-abc123 -- /bin/bash
# 回滚
kubectl rollout history deployment/order-service # 看历史版本
kubectl rollout undo deployment/order-service # 回滚上一版
kubectl rollout undo deployment/order-service --to-revision=3 # 回滚指定版本
# 扩缩容
kubectl scale deployment/order-service --replicas=5
💡 Docker/K8s 最佳实践
- 多阶段构建:Dockerfile用FROM...AS build + COPY --from=build,镜像体积减少70%
- 非root用户运行:RUN adduser + USER app,安全底线
- 健康检查必配:livenessProbe + readinessProbe,K8s才知道你的服务是不是真的健康
- 优雅停机:server.shutdown=graceful + terminationGracePeriodSeconds,部署不停服务
- 日志挂载出来:-v /data/logs:/app/logs,容器删了日志还在
- .dockerignore:排除node_modules/.git/target等,构建快、镜像小
- 镜像版本别用latest:myapp:1.0.3,不然后面分不清哪个版本在跑
⚠️ Docker/K8s 常见坑
- 容器内时区不对:默认UTC → 加
-e TZ=Asia/Shanghai 或挂载 /etc/localtime
- 容器删了数据没了:没做卷挂载 → 数据库容器重启后数据全丢
- 端口映射写反:
-p 宿主机:容器,别写反了,写反了访问不到
- OOMKilled:容器内存超限被杀 → 加大resources.limits.memory或排查内存泄漏
- ImagePullBackOff:私有镜像仓库没登录 →
docker login + imagePullSecrets
- K8s中不要裸跑Pod:用Deployment管理,Pod挂了自动重建
💾 4. 数据库命令速查
🧠 MySQL问题排查思路
1
连得上吗? mysql -h -u -p — 先确认能不能连
为什么先确认连接:连不上数据库,后面所有操作都白搭
↓
2
什么SQL在跑? SHOW PROCESSLIST — 看当前正在执行什么
为什么看这个:慢查询卡住整个数据库,先找到是哪条SQL
↓
3
为什么慢? EXPLAIN — 看执行计划,走了索引没
为什么看执行计划:慢90%是因为没走索引,EXPLAIN一眼看出来
↓
4
修! 加索引/优化SQL/改配置
为什么最后才修:前面定位准了,修起来才有针对性
📋 MySQL命令速查表
| 命令 | 一句话 | 为什么用 | 什么时候用 |
mysql -h -u -p | 连接MySQL | 所有操作的前提 | 手动查数据、排查连接问题 |
SHOW PROCESSLIST | 看正在执行的SQL | 找到慢查询/锁等待 | 数据库慢、卡死 |
EXPLAIN | 看SQL执行计划 | 确认走了索引没 | SQL慢、排查性能 |
SHOW INDEX FROM | 看表的索引 | 确认有没有对应索引 | 怀疑缺索引 |
SHOW STATUS LIKE | 看MySQL状态变量 | 看连接数/QPS/慢查询数 | 性能监控 |
SHOW VARIABLES LIKE | 看MySQL配置 | 确认配置是否合理 | 调优时确认参数 |
mysqldump | 导出数据 | 备份/迁移数据 | 数据备份、环境同步 |
查看MySQL当前正在执行的所有SQL语句和连接状态
数据库卡死时,99%是某条SQL太慢或锁了。PROCESSLIST一眼看到是哪条SQL在搞事
1)数据库响应慢 2)怀疑有锁等待 3)连接数满了要看谁占着
# 看当前所有连接和SQL
SHOW PROCESSLIST;
# 输出关键列:
# Id User Host db Command Time State Info
# 123 app 10.0.1.5:xxx mydb Query 120 Sending data SELECT * FROM orders WHERE...
# ↑ ↑
# 执行了120秒! 这条SQL
# 🧠 重点看:
# - Time列:执行时间长的SQL就是罪魁祸首
# - State列:Waiting for table metadata lock → 有DDL操作锁表了
# - Command列:Sleep → 空闲连接,太多说明连接池配置可能有问题
# 杀掉卡住的SQL(慎用!)
KILL 123;
# 🧠 为什么慎用:事务可能没提交,杀掉后数据可能不一致
# 完整版(显示完整SQL)
SHOW FULL PROCESSLIST;
查看SQL的执行计划,分析MySQL怎么执行这条SQL
SQL慢90%是因为没走索引。EXPLAIN一眼看出来是全表扫描还是索引查询
1)SQL慢要优化 2)加了索引想确认是否生效 3)面试常问
# 在SQL前加EXPLAIN
EXPLAIN SELECT * FROM orders WHERE user_id = 123;
# 🧠 输出关键列解读:
#
# type列(最重要!):
# ALL → 全表扫描(最差,必须优化)
# index → 全索引扫描(还是扫全部,只是扫索引文件)
# range → 范围扫描(BETWEEN/>/ <,还可以)
# ref → 索引查找(好!)
# const → 主键查找(最好!)
#
# key列:
# NULL → 没用索引!这就是慢的原因
# idx_user_id → 用了这个索引(好)
#
# rows列:
# 预估扫描行数,越大越慢
#
# Extra列:
# Using filesort → 额外排序(需要优化)
# Using temporary → 用了临时表(需要优化)
# Using index → 覆盖索引(好!)
# 🧠 优化思路:
# type=ALL → 加索引
# Extra有filesort → 优化ORDER BY的索引
# rows很大 → 缩小查询范围
📋 Redis 命令速查表
| 命令 | 一句话 | 为什么用 | 什么时候用 |
redis-cli ping | 测试连接 | 确认Redis能不能连上 | 连不上Redis时 |
redis-cli info | 看Redis状态 | 看内存/连接数/命中率 | 内存满了、性能问题 |
redis-cli dbsize | 看key数量 | 确认数据量 | 大key排查 |
redis-cli --scan | 扫描key | 找特定key(不用KEYS *) | 查找某个业务的key |
MEMORY USAGE | 看key占多少内存 | 大key影响性能 | 内存泄漏排查 |
查看Redis服务器状态信息
Redis内存满了、慢了,先看info了解全貌,而不是猜
1)Redis内存使用情况 2)命中率低(缓存没效果) 3)连接数异常
# 看全部信息
redis-cli info
# 只看内存相关
redis-cli info memory
# 🧠 重点看:
# used_memory_human → 当前用了多少内存
# maxmemory_human → 最大内存限制
# keyspace_hits → 缓存命中次数
# keyspace_misses → 缓存未命中次数
# 命中率 = hits / (hits + misses)
# 命中率 < 80% → 缓存策略有问题
# ⚠️ 禁止用 KEYS * 命令!
# KEYS * 会阻塞Redis,线上用等于自杀
# 用 SCAN 代替:
redis-cli --scan --pattern "order:*"
💡 数据库最佳实践
- 慢查询日志必须开:
slow_query_log=ON + long_query_time=1,不然出问题不知道哪条SQL慢
- EXPLAIN是第一优化工具:SQL慢先EXPLAIN,看type和key列
- Redis禁止KEYS *:用SCAN替代,KEYS *会阻塞整个Redis
- 大key拆分:单个key超过10KB就算大key,要拆分成小key
- 连接池配置合理:MySQL连接池大小不是越大越好,通常20-50够用
- 备份!备份!备份!:mysqldump定时备份,出了事还能恢复
⚠️ 数据库常见坑
- 索引加了但没生效:用了函数/类型转换/OR → 索引失效 → 用EXPLAIN验证
- Redis keys *把线上搞崩:生产环境绝对禁止!用 SCAN 替代
- MySQL连接数满了:
SHOW PROCESSLIST 看谁占着,Sleep连接太多 → 调小wait_timeout
- 慢SQL杀不掉:
KILL 语句可能回滚很久 → 大事务的KILL比等它执行完还慢
- Redis没设过期时间:缓存永不过期 → 内存越来越大 → 所有key都设TTL
🐧 5. Linux 日常命令速查
🧠 Linux排查思路:按"体检项目"来
1
查进程:
ps / top — 谁在跑?谁吃资源?
为什么先看进程:服务挂没挂、CPU/内存谁吃了,一眼就知道
↓
2
查磁盘:
df -h / du -sh — 空间够不够?
为什么看磁盘:磁盘满了服务直接挂,而且报错信息不明显
↓
3
查网络:
ping / telnet / netstat — 通不通?
为什么查网络:微服务之间调不通是最常见的线上问题之一
↓
4
查文件:
find / grep / tail — 找到日志/配置/大文件
为什么查文件:排查最终都要看日志、找配置、清理大文件
📋 Linux命令速查表
| 类别 | 命令 | 一句话 | 为什么用 |
| 进程 | ps aux | 看所有进程 | 找某个服务在不在跑 |
top / htop | 实时看CPU/内存 | 谁在吃资源? |
kill -9 | 强制杀进程 | 进程卡死无法正常退出 |
| 磁盘 | df -h | 看磁盘使用率 | 磁盘满了吗? |
du -sh * | 看每个目录大小 | 哪个目录占空间最多? |
find -size +100M | 找大文件 | 磁盘满了找谁占的 |
| 文件 | find / -name | 按名字找文件 | 找配置文件在哪 |
grep -rn "text" | 搜索文件内容 | 在代码/日志中搜关键字 |
tail -f | 实时看文件末尾 | 看日志实时输出 |
wc -l | 统计行数 | 日志有多少行/ERROR有多少 |
| 权限 | chmod 755 | 修改文件权限 | 脚本无法执行 |
chown user:group | 修改文件所有者 | 文件权限不对 |
📝 关键命令详解
实时查看系统CPU、内存使用和进程列表
线上服务器卡了,第一件事就是看谁在吃CPU/内存。top是Linux自带,htop更友好但需要装
1)服务器变慢 2)CPU/内存飙高 3)某个进程疑似卡死
# 实时监控
top
# 🧠 top 中的关键信息:
# %CPU 列 → 按CPU排序,找吃CPU的进程
# %MEM 列 → 按内存排序,找吃内存的进程
# 按 P 键 → 按CPU排序
# 按 M 键 → 按内存排序
# 按 q 键 → 退出
# 看某个进程的线程(Java排查必备)
top -Hp 12345
# 🧠 为什么要 -Hp:
# Java进程内部有几十上百个线程
# 普通top只能看到进程级别的CPU
# -Hp能看到进程内每个线程的CPU使用
# 然后配合jstack定位问题代码行
查看磁盘空间使用情况
磁盘满是非常隐蔽的问题——服务报各种莫名其妙的错,其实就是磁盘满了写不了日志/临时文件
1)服务莫名报错 2)日志写不进去 3)MySQL提示磁盘满
# 看各分区使用率
df -h
# 🧠 重点看 Use% 列:
# > 80% 就要注意
# > 95% 就很危险了
# 100% 服务直接挂
# 看当前目录下每个文件夹多大
du -sh *
# 🧠 为什么用 -sh:
# -s 只显示总计(不列子目录)
# -h 人类可读(显示M/G而不是字节数)
# 找大文件(>100M的文件)
find / -type f -size +100M -exec ls -lh {} \;
# 清理日志的常用命令
# 清空日志文件但保留文件(不删文件)
> app.log
# 🧠 为什么不rm:rm后Java进程还在往原文件写,磁盘不会释放
# 用 > 清空文件内容,进程继续写同一个文件
按条件查找文件
找配置文件、找日志、找大文件,find是最强大的文件搜索工具
1)找不到配置文件在哪 2)找大文件清理磁盘 3)找最近修改的文件
# 按名字找文件
find / -name "application.yml"
# 按名字模糊查找
find / -name "*.log"
# 找最近10分钟修改过的文件
find /var/log -mmin -10
# 找大文件
find / -type f -size +100M
# 找到后删除(危险!先确认再删)
find /tmp -name "*.tmp" -mtime +7 -delete
# 🧠 -mtime +7 → 7天前修改的
💡 Linux最佳实践
- rm前先ls确认:
ls /tmp/old_logs/ 确认内容再 rm -rf,养成习惯
- 清日志用 > 不用 rm:
> app.log 清空内容但保留文件,进程继续写不会丢日志
- 用tmux/screen:SSH断开进程不会死,长时间跑的任务一定要在tmux里
- alias常用命令:
alias ll='ls -la',减少重复输入
- 磁盘报警提前设:>80%报警,不要等到100%才发现
⚠️ Linux常见坑
- rm -rf /:终极灾难 → 永远先cd到目录再rm,不要用绝对路径rm
- 磁盘满了rm文件空间没释放:进程还在用这个文件 →
lsof | grep deleted 看谁占着,kill进程或重启
- top里CPU超过100%:多核机器上一个进程可以占多个核 → 400%=4个核
- 权限不够:Permission denied →
sudo !! 用root重跑上一条命令
🌐 6. 网络排查命令速查
🧠 网络不通排查思路
1
DNS解析:
nslookup / dig — 域名能解析出IP吗?
为什么先看DNS:域名解析不了,后面全白搭。ping不通可能就是DNS的问题
↓
2
网络通不通:
ping — IP能通吗?
为什么ping:最基础的网络连通性测试。ping不通就是网络层的问题
↓
3
端口通不通:
telnet / nc — 服务端口能连吗?
为什么看端口:ping只测网络层,端口通不通是传输层。服务在跑但端口没暴露=不通
↓
4
HTTP层:
curl — 接口能调吗?返回什么?
为什么curl:端口通了不等于服务正常,要实际调接口看返回
↓
5
抓包:
tcpdump — 数据包到底发出没?带了什么?
为什么抓包:上面都不行说明数据包有问题,抓包看原始数据
📋 网络命令速查表
| 命令 | 一句话 | 为什么用 | 什么时候用 |
ping | 测试网络连通性 | 最基础的网络排查 | 服务调不通第一步 |
telnet | 测试端口连通性 | ping通但端口不通=服务没暴露 | 确认端口是否开放 |
curl | 发送HTTP请求 | 实际调接口看返回 | 接口调试、确认服务正常 |
nslookup / dig | DNS解析 | 域名能解析出IP吗 | 域名访问不了 |
netstat / ss | 看端口/连接状态 | 服务在监听哪个端口 | 端口占用、连接数 |
tcpdump | 抓网络包 | 看原始数据包内容 | 数据包到底发出没 |
发送HTTP请求,API调试的瑞士军刀
接口调不通,浏览器有缓存和Cookie干扰。curl是最纯净的HTTP客户端,看到的就是服务器返回的原始内容
1)确认接口是否正常 2)看响应头 3)POST接口调试 4)测试内网接口
# GET 请求
curl http://localhost:8080/api/orders/123
# 看响应头(调试CORS/鉴权必备)
curl -I http://localhost:8080/api/orders/123
# 看完整请求和响应(最常用)
curl -v http://localhost:8080/api/orders/123
# POST 请求(JSON)
curl -X POST http://localhost:8080/api/orders \
-H "Content-Type: application/json" \
-d '{"productId":1,"quantity":2}'
# 带Token
curl -H "Authorization: Bearer eyJhbG..." \
http://localhost:8080/api/orders
# 只看HTTP状态码
curl -o /dev/null -s -w "%{http_code}" http://localhost:8080/api/health
查看网络连接和端口监听状态
端口冲突、连接数过多、TIME_WAIT太多,都得靠这个看
1)端口被占用 2)连接数异常 3)确认服务在监听哪个端口
# 看所有监听端口
netstat -tlnp
# 或(ss更快)
ss -tlnp
# 🧠 输出解读:
# Local Address:Port → 0.0.0.0:8080 表示监听所有网卡
# → 127.0.0.1:8080 表示只监听本机(外部访问不了!)
# 看某个端口被谁占了
lsof -i :8080
# 或
netstat -tlnp | grep 8080
# 看TCP连接状态统计(排查TIME_WAIT)
netstat -ant | awk '{print $6}' | sort | uniq -c | sort -rn
# 输出:
# 324 ESTABLISHED
# 156 TIME_WAIT ← 太多说明连接没复用
# 🧠 为什么有时用ss不用netstat:
# ss更快(直接读内核),netstat在高并发时很慢
# 两者输出格式类似,习惯用哪个都行
抓取网络数据包,网络排查的终极武器
ping/telnet/curl都试过了还是找不到问题?抓包看原始数据,看到底发出去没、返回了什么
1)请求到底发出去没 2)数据包内容是否正确 3)第三方回调没收到
# 抓8080端口的所有包
tcpdump -i any port 8080 -A -s 0
# 🧠 参数解读:
# -i any → 监听所有网卡
# port 8080 → 只看8080端口的包
# -A → 以ASCII显示(能看HTTP内容)
# -s 0 → 抓完整包(不截断)
# 抓包保存到文件(用Wireshark分析)
tcpdump -i any host 10.0.0.5 -w capture.pcap
# 抓HTTP POST请求
tcpdump -i any -A -s 0 'tcp port 8080 and (((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0)'
# 🧠 实际排查流程:
# 1. tcpdump -i any port 8080 -A -s 0
# 2. 触发请求
# 3. 看输出:有没有请求数据包?有没有响应?
# 4. 如果有请求没响应 → 服务端问题
# 5. 如果没请求 → 网络层问题/请求根本没发出去
💡 网络排查最佳实践
- 按层排查:DNS → ping → telnet → curl → 抓包,从底层到高层,逐层排除
- curl -v最常用:看完整请求和响应,包括重定向、Header,一目了然
- TIME_WAIT太多:通常不是bug,是短连接太多 → 改用长连接/连接池
- 防火墙别忘了查:iptables -L 看防火墙规则,可能是防火墙挡了
- DNS缓存问题:改了DNS但还解析到旧IP → 清DNS缓存或换8.8.8.8
⚠️ 网络排查常见坑
- ping通但telnet不通:ping是ICMP协议,不等于TCP端口通 → 必须telnet验证端口
- curl能通但浏览器不行:浏览器有CORS限制/Cookie问题 → curl -v看细节
- 服务只监听127.0.0.1:外部访问不了 → 改成0.0.0.0监听
- tcpdump输出看不懂:原始包不好读 → 保存pcap用Wireshark图形化分析
📦 7. Git 版本控制命令速查
🧠 Git日常操作思路
1
改代码前:
git pull — 先拉最新代码
为什么先pull:不拉最新就改,大概率冲突。先pull再改,冲突最少
↓
2
改完代码:
git add + git commit — 暂存并提交
为什么分两步:add选择要提交的文件(不提交无关修改),commit写提交信息
↓
3
推送到远端:
git push
为什么必须push:本地commit不算完,别人看不到,push到远端才算交付
↓
4
合并代码:
git merge / PR — 合到主分支
为什么用PR:代码审查 + 自动化测试,直接合容易引入Bug
📋 Git命令速查表
| 场景 | 命令 | 为什么用 | 注意 |
| 看状态 | git status | 改了哪些文件?暂存了没? | 每次操作前后都看一下 |
| 看差异 | git diff | 具体改了什么内容? | commit前一定要看 |
| 暂存 | git add . | 把修改加入暂存区 | . 表示全部,也可以指定文件 |
| 提交 | git commit -m "msg" | 记录这次修改 | 信息要写清楚 |
| 推送 | git push | 同步到远端 | 多人协作必须push |
| 拉取 | git pull | 同步远端最新代码 | 每天开始工作前pull |
| 看日志 | git log --oneline | 看提交历史 | 回滚前先看日志 |
| 回滚 | git revert | 撤销某次提交 | 安全,不会丢历史 |
| 暂存工作 | git stash | 临时保存未提交的修改 | 切分支时不想丢修改 |
| 切分支 | git checkout -b | 创建并切换新分支 | 每个功能一个分支 |
临时保存当前未提交的修改,工作区恢复干净
改了一半的代码需要切分支修Bug,但不想提交半成品。stash=把工作台上的东西暂时收进抽屉
1)切分支但不想提交半成品 2)拉代码冲突但不想commit 3)临时切去做别的事
# 保存当前修改
git stash
# 带备注保存
git stash save "订单功能改到一半"
# 查看保存列表
git stash list
# 恢复最近一次保存
git stash pop
# 恢复指定保存
git stash apply stash@{0}
两种回滚方式,效果不同
很多人分不清revert和reset,用错了可能丢代码或搞乱远端历史
# git revert:安全回滚(推荐)
# 创建一个新提交来撤销指定提交,不丢历史
git revert abc123
# 🧠 为什么推荐revert:
# 相当于"反向操作",比如之前是+10行,现在-10行
# 历史完整保留,多人协作安全
# git reset:硬回滚(危险!)
# 回退到某个提交,之后的提交全丢
git reset --hard abc123
# ⚠️ reset --hard 会丢失未提交的修改!
# 只在本地用,永远不要 reset 已push的提交!
# 🧠 记忆方法:
# revert = 撤销(加一个反向操作)→ 安全
# reset = 回退(删除之后的操作)→ 危险
💡 Git最佳实践
- commit信息写清楚:
fix: 修复订单库存扣减竞态条件,不要写 fix bug
- 小步提交:改完一个小功能就commit,不要攒一大堆一起提交
- 每天pull:每天开始工作前
git pull,减少冲突
- 用PR不用直接merge:代码审查 + CI检查,防止引入Bug
- git push前git diff:确认要推送的内容,避免推错
- .gitignore配好:排除node_modules/target/.idea等,不要提交垃圾文件
⚠️ Git常见坑
- merge冲突吓到:正常现象!打开冲突文件手动选保留哪部分 → git add → git commit
- push被拒绝:远端有新提交 →
git pull --rebase → 再push
- reset --hard后想恢复:
git reflog 看操作历史,reset回去
- 敏感信息push了:密码/密钥 → 立即改密码 + 用git filter-branch清除历史
- 大文件push失败:GitHub限制100MB → 用git lfs或删除大文件
🚀 8. 部署发布命令速查
🧠 发布上线思路
1
构建:
mvn package / docker build — 打包代码
为什么先构建:代码不能直接跑,需要编译+打包成可部署的产物
↓
2
部署:
docker run / kubectl apply — 把产物放到服务器
为什么用容器部署:环境一致,不会出现"我电脑上能跑"
↓
3
验证:
curl / 查日志 — 确认服务正常
为什么必须验证:部署完不验证等于没部署,接口不通你可能都不知道
↓
4
监控:
看Grafana/日志 — 持续观察
为什么持续监控:有些问题不是部署后立刻出现,内存泄漏可能几小时后爆发
↓
5
有问题就回滚:
kubectl rollout undo
为什么回滚优先:线上问题先恢复服务,再慢慢排查。不要在线上调试
📋 部署命令速查表
| 场景 | 命令 | 为什么用 | 注意 |
| Maven打包 | mvn clean package -DskipTests | 编译+打包成jar | 生产环境不要skipTests! |
| 运行jar | java -jar app.jar | 直接运行(简单场景) | 加上JVM参数 |
| 后台运行 | nohup java -jar app.jar & | SSH断开不停止 | 日志重定向到文件 |
| Docker构建 | docker build -t myapp:1.0 . | 构建Docker镜像 | 注意最后的. |
| Docker部署 | docker run -d -p 8080:8080 myapp:1.0 | 容器化部署 | 配好环境和挂载 |
| K8s部署 | kubectl apply -f deploy.yaml | 声明式部署 | 先validate再apply |
| K8s回滚 | kubectl rollout undo deploy/xxx | 秒级回滚 | 回滚后验证 |
| 查看服务 | systemctl status app | 看系统服务状态 | 传统部署方式 |
后台运行程序,SSH断开也不会停
SSH连服务器跑jar,一关终端进程就死了。nohup让进程脱离终端继续跑
简单部署场景(不用Docker时)、跑一次性脚本
# 后台运行jar包
nohup java -jar app.jar --spring.profiles.active=prod > app.log 2>&1 &
# 🧠 每个参数为什么:
# nohup → 忽略SIGHUP信号(关终端不杀进程)
# > app.log → 标准输出到文件
# 2>&1 → 错误输出也到文件(2=stderr, 1=stdout, &1=引用stdout)
# & → 后台运行
# 查看后台进程
jobs -l
# 停止后台进程
kill <PID>
# ⚠️ 更推荐用systemd管理
# nohup不够优雅:不能自动重启、不能开机自启
生产环境Java应用推荐JVM参数
JVM参数不对,性能可能差几倍。堆太小频繁GC,堆太大STW久。需要根据应用特点调
# 生产环境推荐JVM参数
java -server \
-Xms2g -Xmx2g \ # 堆大小:初始=最大(避免动态扩缩容)
-XX:+UseG1GC \ # G1垃圾收集器(JDK9+默认)
-XX:MaxGCPauseMillis=200 \ # 目标GC停顿时间200ms
-XX:+HeapDumpOnOutOfMemoryError \ # OOM时自动dump
-XX:HeapDumpPath=/data/dumps/ \ # dump文件路径
-Xlog:gc*:file=/data/logs/gc.log \ # GC日志(JDK11+语法)
-Dspring.profiles.active=prod \ # 激活生产环境配置
-jar app.jar
# 🧠 每个参数为什么:
# -Xms=-Xmx:避免堆动态扩缩容,减少不必要的GC
# G1GC:大堆首选,可控制停顿时间
# HeapDumpOnOutOfMemoryError:出问题才有现场
# GC日志:排查GC问题的必须品
# JDK8的GC日志写法不同:
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/data/logs/gc.log
💡 部署最佳实践
- 部署前备份:旧版本镜像/包留好,出问题秒级回滚
- 灰度发布:先10%流量验证,没问题再100%,降低风险
- 部署后必验证:curl健康检查接口 + 看日志有无ERROR
- 回滚优先于修复:线上出问题先回滚恢复服务,再慢慢排查
- JVM参数必须配:HeapDumpOnOutOfMemoryError + GC日志,不配出问题就是盲人摸象
- 优雅停机:server.shutdown=graceful + terminationGracePeriodSeconds
- 发布窗口:选择低峰期发布(凌晨/周末),出问题影响小
⚠️ 部署常见坑
- nohup日志满了磁盘:日志轮转不配,跑了几个月日志几十G → 配logback滚动策略
- 忘了加JVM参数:OOM时没有dump、GC日志也没开 → 出问题两眼一抹黑
- 数据库迁移忘跑:新版本加了字段但没跑DDL → 启动就报错
- 配置文件不对:连的还是开发环境的数据库 → spring.profiles.active配错
- 回滚后数据不一致:新版本改了数据结构,回滚代码但数据库没回 → 注意兼容性
- K8s镜像拉不下来:私有仓库没配imagePullSecrets → 提前配好