Linux进程与日志排查:进程池、线程与进程、日志留存
2026/9/18 7:03:41 网站建设 项目流程

1. 进程与日志凭什么总被凑在一张排查表里

线上出问题的时候,绝大多数人第一反应是敲top,看到 CPU 或者内存不对劲,再去翻日志。这个动作顺序看着像本能,其实背后有一条很清晰的逻辑:进程回答的是"现在谁在干活、干得怎么样",日志回答的是"过去这段时间它到底说了什么"。前者是快照,后者是流水账,缺任何一个,排查都会变成瞎猜。

举个我自己遇到过的场景。某台跑批任务的机器,凌晨三点告警说负载突然拉高,白天看的时候一切正常。上去top一看,当前占用最高的只是系统自己的几个守护进程,看不出异常。这时候top已经没用了,因为它只反映"此刻"。真正定位到问题靠的是两样东西:一是sarpidstat留下来的历史采样,二是应用日志里那一串在凌晨两点五十八分开始疯狂刷的报错。日志告诉你"什么时候开始的、报了什么错",进程相关的采样数据告诉你"那个时候是谁把 CPU 吃掉了"。

所以"Linux 中查看进程与日志"这件事,本质不是教你背几个命令,而是建立一套观察链路:从当前状态出发,顺着时间线往回找,最后落到具体的那一个 PID 或者那几行日志上。

1.1 为什么先看进程再看日志,而不是反过来

很多人习惯一上来就tail -f日志,觉得日志信息最全。这个做法在"日志量不大、报错很明确"的时候确实快,但它有个致命前提:你得知道日志在哪、叫什么名字。生产环境里一台机器上跑着 nginx、Java 应用、定时任务、数据库,日志散落在/var/log、应用目录、容器内部好几个地方,你连找都得找半天。

而进程是收敛的。ps -ef一条命令出来,所有跑着的东西一目了然,哪个 PID 是陌生的、哪个进程的启动时间对不上、哪个进程的父进程是被谁拉起来的,信息密度极高。先扫一遍进程,相当于先把嫌疑人名单列出来,再去日志里找对应时间段的证词,效率差好几倍。

我个人养成的习惯是三步走:先ps拿全局,再top/pidstat定嫌疑对象,最后带着 PID 和时间点去翻日志。这个顺序在九成以上的故障里都成立。

1.2 进程状态背后的三种典型问题

进程能反映出来的问题,粗分有三类,每一类对应的日志翻法完全不同。

现象进程层面的表现通常去日志里找什么
资源被吃满%CPU%MEM长期高位,load average超过核数慢查询、死循环、大批量任务的开始时间点
进程状态卡住状态位出现D(不可中断睡眠)或Z(僵尸)磁盘 IO 报错、内核日志、父进程异常退出记录
进程反复重启同一个服务每隔几十秒换一次 PID崩溃堆栈、OOM Killer 记录、退出码

这张表是我自己在复盘时总结的,它的价值在于:先给问题归类,再决定去哪本日志里翻。比如你发现状态是D,那基本不用去看应用日志了,直接dmesg -T看内核有没有报磁盘或者 NFS 相关的错,方向对了一次就中。

提示:load average高不等于 CPU 忙。它统计的是"运行 + 等待运行"的进程数,一个卡在磁盘 IO 上的进程同样会让 load 飙高。这时候top里 CPU 使用率可能很低,但 load 是 20,很多人会误判成 CPU 问题。


2. ps 与 top 之外的进程观察视角

ps auxtop是所有人都会的命令,但真正能把它们用出花的人不多。问题在于大多数人只用了这两个命令的默认输出,而默认输出往往不含你需要的列。

2.1 ps 的三种用法,对应三种不同的问题

第一种是ps aux,BSD 风格,优点是有一列STATSTART,能看到进程状态和启动时间。第二种是ps -ef,System V 风格,优点是能清晰看到PPID(父进程 ID),追查"这个进程是谁拉起来的"特别好用。

第三种是自定义列,这才是真正干活用的:

ps -eo pid,ppid,user,stat,%cpu,%mem,etime,cmd --sort=-%cpu | head -20

这条命令的每个字段都有用意。%cpu%mem按 CPU 降序排,etime是进程已经运行的时长(格式是天-时:分:秒),stat是状态。为什么加etime?因为它能帮你判断"这个进程是不是刚起来的"。一个跑了三天的进程突然吃满 CPU,和一个刚起来十秒就吃满 CPU 的进程,性质完全不同,前者可能是逻辑缺陷积累,后者可能是在做初始化或者进入了死循环。

--sort=这个参数经常被忽略。默认ps是按照 PID 排序的,PID 大小和资源占用毫无关系,所以默认输出里 CPU 最高的进程可能排在中间某一行。要快速找到嫌疑人,必须显式排序。

2.2 top 里的交互按键才是精华

top打开之后,很多人就盯着刷新的数字看,等着它自己换。其实按几个键,信息量立刻不一样。

H可以切换成显示线程,这个时候原本一个 Java 进程会展开成几十行,每一行是一个线程。找到那个 CPU 特别高的线程 ID(TID)之后,可以拿这个数字去jstack里对应,这是定位 Java 死循环的标准动作。

1可以展开每个 CPU 核心的使用情况。如果发现某一个核心长期 100% 而其他核心闲着,那大概率是单线程瓶颈,跟并行度设置有关,不是整体负载问题。

P按 CPU 排序,按M按内存排序,按T按运行时间排序。这三个切换键我基本每次上去都要按一遍。因为默认排序是 CPU,但有些问题恰恰是内存泄漏引起的,光看 CPU 是看不出来的。

还有一个组合值得记住:

top -H -p 12345

直接盯住某一个 PID 的所有线程,CPU 高的线程一眼就能跳出来。这比在全量 top 里翻要快得多,尤其是在一台跑了几百个进程的机器上。

2.3 /proc 目录:所有进程信息的原始出处

pstoppidstat这些工具,数据其实都来自同一个地方:/proc。这个目录是内核暴露出来的虚拟文件系统,不占磁盘空间,进程一退出目录就消失了。

进去看一眼就明白:

ls /proc/12345 cat /proc/12345/status cat /proc/12345/cmdline | tr '\0' ' ' ls -l /proc/12345/fd

status里有内存占用、线程数、状态这些细节。fd目录列出的是这个进程打开的所有文件描述符,链接指向的实际文件一目了然。

这个目录最实用的场景是排查"文件被哪个进程占用"。比如你想卸载一个挂载点,系统提示device is busy,很多人第一反应是lsof | grep,但如果系统里进程特别多,lsof全量扫一遍会非常慢。这时候直接看/proc/*/fd里有没有指向那个路径的链接更快,或者用fuser -m /mnt/data,它是直接查内核表,不走全量遍历。

cmdline这一项也值得多说一句。它显示的是进程启动时的完整参数,用\0分隔,所以直接cat出来是连在一起的,得用tr '\0' ' '转一下。用它来判断一个进程是不是被正确传参启动,比看ps里被截断的COMMAND列靠谱得多——ps aux的输出在某些终端下会被截断,误判成"参数不对"的时候不少。

2.4 pgrep、pstree、pidstat 的分工

这三个工具各有各的地盘,用对了省很多事。

pgrep -af keyword按名字或者完整命令行匹配进程,返回 PID。比ps aux | grep keyword好在两点:一是不会把自己这条 grep 命令也匹配进去(这个坑几乎每个新手都踩过),二是配合-f能匹配完整命令行,适合那些名字很短、容易被误匹配的进程。

pstree -p用树形结构展示进程的父子关系。排查"谁启动了谁"这类问题,没有任何命令比它更直观。我曾经排查过一个奇怪的重复进程问题,ps里看到两个一模一样的服务,但不知道哪个是"正主",用pstree -p一看就清楚了:一个是 init 系统直接拉起来的,另一个是某个脚本 fork 出来的,后者才是多余的。

pidstatsysstat包里带的,专门做按进程的周期采样:

pidstat -p 12345 1 10

意思是对 12345 这个进程,每秒采样一次,一共采十次。输出里会分别给出用户态 CPU、内核态 CPU、等待 IO 的占比。这个拆分的价值在于:如果一个进程的 CPU 大部分花在内核态,那问题可能出在系统调用太频繁(比如大量小文件读写);如果都在用户态,那才是应用代码本身在算。方向一分开,优化思路完全不同。


3. 线程、进程与进程池:看懂数量背后的含义

热词里同时出现了"进程池""线程与进程""进程通信",说明这块概念上的混淆确实普遍。而在排查现场,概念搞不清楚会直接导致判断错误。

3.1 线程和进程在排查中的实际差别

教科书上说进程是资源分配的单位,线程是调度的单位。落实到排查上,这句话意味着:同一个进程下的多个线程共享内存,所以一个线程崩了可能整个进程跟着挂;而不同进程之间内存隔离,一个挂了不影响另一个

这个差别带来的直接后果是日志行为不同。多线程程序里,多个线程写同一个日志文件,如果没做好同步,日志会交错、错乱,甚至丢行。你看到日志里两行莫名其妙连在一起,很可能不是程序 bug,而是两个线程同时写文件造成的。多进程程序反而没这个问题,每个进程写自己的文件,或者靠日志框架保证追加写入的原子性。

所以当你看到日志"内容对不上"的时候,第一步应该判断这个服务是多线程还是多进程模型,而不是直接怀疑业务逻辑。

3.2 进程池为什么会让 ps 里冒出一堆同名进程

进程池的设计初衷是复用,避免频繁创建销毁的开销。但它的副作用是:ps -ef | grep 服务名会返回一大串看起来一模一样的进程。

这里最容易犯的错是把工作进程当成主进程去 kill。比如 nginx 会有一个 master 和若干 worker,你 kill 掉一个 worker,master 会立刻再拉起一个新的,看起来像是"杀不死"。真正要停服务,得 kill master,然后它会给 worker 发信号让它们优雅退出。

怎么区分主次?看PPID。父进程是 1(被 init 系统接管)或者父进程是某个管理进程的,通常是主进程;父进程指向主进程 PID 的,是它派生出来的工作进程。用pstree -p 主PID一眼就能看出这棵树的形状。

还有一个更隐蔽的情况:同一个服务被启动了两遍。表现就是进程池规模翻倍,CPU 和内存占用也翻倍,但功能看起来正常,所以很难被发现。判断方法是看主进程的启动时间和监听端口——如果两个进程都监听了同一个端口,那说明用到了SO_REUSEPORT这类多进程共享端口的技术,是有意为之;如果一个监听失败在重启循环里,那就得去日志里找Address already in use这条报错。

3.3 僵尸进程和孤儿进程,别被名字吓到

僵尸进程(状态Z,也叫defunct)不是"坏进程",它其实是已经执行结束、但父进程还没来得及回收它退出状态的进程。它不占 CPU、不占内存,只占一个进程表项。所以看到一两个僵尸进程完全不用慌。

真正的问题是僵尸进程数量持续增长,那说明父进程没有正确处理SIGCHLD,子进程退出后没人回收,进程表项会越积越多,最终可能耗尽。查法很简单:

ps -eo stat,pid,ppid,cmd | awk '$1 ~ /^Z/'

找到之后,处理方式不是 kill 僵尸进程本身(它已经死了,kill 不掉),而是处理它的父进程——要么修父进程的代码,要么把父进程正常重启一次,让它重新初始化并回收。

孤儿进程则相反,父进程先退出了,它被 init(PID 1)接管。这种进程没什么危害,只是PPID变成了 1,看起来有点特别。

D状态(不可中断睡眠)值得单独提一句,因为它最容易被误判。这个状态通常是进程在等磁盘 IO 或者网络文件系统响应,此时它不响应任何信号,包括 kill -9。你 kill 不掉它是正常的,得等 IO 返回或者恢复底层存储。遇到这种情况,方向应该转向查存储层,dmesg -T里经常能找到线索。


4. 日志放在哪:从 /var/log 到 journalctl 的完整地图

知道进程是谁之后,下一步就是找它的日志。Linux 上日志的分布其实有三套体系,很多人只熟悉其中一套,遇到另外两套就抓瞎。

4.1 传统文件日志:/var/log 那一堆

这是最老派也最直观的一套,日志就是普通文件,用catlesstail就能看。常见的几个:

文件记录内容什么时候看它
/var/log/messages系统级通用消息(部分发行版是 syslog)系统层面莫名报错
/var/log/secureauth.log登录、认证、提权记录排查异常登录、账号问题
/var/log/cron定时任务的执行记录任务没跑、跑了但失败
/var/log/dmesg内核环形缓冲区的快照硬件、驱动、OOM 相关

注意/var/log/messages这个文件在不同发行版上名字不一样,有的是syslog,有的是messages,有的两者都有但分工不同。这个差异经常让跨发行版操作的人踩坑,判断方法是直接ls /var/log/看一眼,别凭记忆敲。

dmesg有个很实用的小细节:默认输出的时间戳是"自系统启动以来的秒数",看着很痛苦。加-T参数会转成人类可读的日期时间:

dmesg -T | tail -50 dmesg -T | grep -i -E 'oom|killed process'

第二条命令专门抓 OOM Killer 的记录。进程突然消失、而且日志里没有任何"主动退出"的痕迹时,第一件事就应该是查这个。OOM Killer 杀进程不会给应用留遗言,只会在内核日志里留一行记录,上面写着被杀的进程名、PID 和它的内存评分。

4.2 systemd 体系:journalctl 的用法逻辑

现在的发行版基本都用 systemd,日志被统一收进了 journal。它的好处是结构化、带索引、可以按服务、按时间、按优先级过滤,比 grep 文件强太多。

几个我每天都用的组合:

journalctl -u nginx.service --since "2024-01-01 08:00" --until "2024-01-01 09:00" journalctl -u myapp -f journalctl -p err -b journalctl -k -b -1

第一条按服务加时间范围过滤。第二条-f是实时跟随,等价于tail -f,但不用你去找文件在哪。第三条-p err只显示 error 及以上级别,-b限定本次启动。前两条逻辑很直白,第三条才是真正省时间的——日志刷得太快的时候,只留错误级别,噪音立刻降下来

第四条-k -b -1是看内核日志,-b -1表示"上一次启动"。这个参数的价值在于排查重启原因:机器重启过,你当前会话看到的是本次启动的日志,重启那一刻发生了什么,得看-b -1才有。

journalctl还能输出 JSON:

journalctl -u myapp -o json-pretty -n 1

每条日志除了MESSAGE字段,还有_PID_COMM_SYSTEMD_UNIT这些元数据。用jq解析之后可以做统计分析,比如按 PID 分组看哪个进程报错最多。这个玩法在排查"重复报错但不知道源头"的问题时特别有效。

有一个坑必须提醒:journal 默认是持久化还是内存态,取决于配置。如果/var/log/journal目录不存在,日志只存在内存里,重启就没了。要长期留存必须手动创建这个目录并重启服务:

mkdir -p /var/log/journal systemctl restart systemd-journald

很多人抱怨"重启前的日志怎么都找不到",八成就是这个原因。

4.3 应用日志:文件、标准输出与容器

应用日志的形态取决于它怎么部署的。直接跑在宿主机上的服务,一般写在自己的目录里,比如/opt/app/logs/app.log。这种日志的关键是找到路径,路径通常写在配置文件里,grep -r "log" /opt/app/conf/一般能翻出来。

用容器跑的,日志默认走标准输出,由容器运行时接管:

docker logs -f --tail 100 container_name docker logs --since 30m container_name docker logs --since "2024-01-01T08:00:00" container_name

--tail指定从末尾看多少行,--since指定时间起点。这两个参数一定要加。不加的话,一个跑了三个月的容器,docker logs会把几十万行一次性吐出来,终端直接卡死。这个坑我踩过一次,后来养成了习惯:看容器日志永远先--tail,再-f

还有一个组合值得记:

docker logs -f container_name 2>&1 | grep --line-buffered -E "ERROR|Exception"

2>&1把标准错误也接过来(很多程序的报错走的是 stderr,不接过来会漏),--line-buffered保证 grep 逐行输出而不是攒一批再吐,实时性才有保障。少了这个参数,你会觉得"日志怎么半天不刷新",其实是缓冲区在作怪。

如果同一个服务跑了多个副本,用docker compose logs -f service_name可以合并看,它会自动加上容器名前缀,哪个副本报错一目了然。

4.4 日志轮转:为什么你的日志文件会显示"包含100..."

日志不可能无限增长,撑爆磁盘的后果是整个系统出问题。所以几乎所有发行版都自带logrotate,按天或按大小切割、压缩、删除旧文件。

配置一般在/etc/logrotate.d/下面:

/opt/app/logs/app.log { daily rotate 180 missingok notifempty compress delaycompress copytruncate }

这里每个指令都值得解释一遍。

daily是每天切割一次。rotate 180是保留 180 份,这正好对应热词里"审计日志如何留存 180 天"的需求——如果按天切,rotate 180就是半年的量。missingok是文件不存在时不要报错(很实用,避免因为日志文件还没生成就导致轮转任务告警)。notifempty是空文件不切。compress是压缩旧日志省空间,delaycompress是延迟一次再压缩,避免刚切完的日志被压了还在被程序写。

copytruncate这个指令要重点说。默认情况下 logrotate 是靠"重命名 + 通知程序重新打开文件"来工作的,但有些程序不响应这个信号,重命名之后它还在往原来的文件句柄里写,结果就是新文件是空的,写入的内容全跑到被重命名的旧文件里去了。copytruncate的做法是"复制一份再清空原文件",避开了这个问题,代价是复制和清空之间可能丢一点点日志。如果你遇到"日志切割之后内容不见了"或者"切割后新旧文件都对不上",八成就是这个参数没配

/var/log目录下经常能看到带数字后缀的文件,比如app.log.1app.log.2.gz,就是轮转的产物。.gz的是压缩过的,看的时候得zcat或者zless,直接cat出来是乱码——这也是一些人以为"日志文件损坏了"的原因。

手动验证轮转配置是否正确,不用等第二天:

logrotate -d /etc/logrotate.d/myapp

-d是 debug 模式,只打印它打算做什么,不会真的执行。想真跑一次就用-f强制轮转。这个技巧在刚配好轮转规则的时候特别有用,先 dry-run 一遍看看路径和权限对不对,比等到半夜轮转失败再排查强。


5. 把进程和日志串起来:三条典型排查链路

前面讲的是工具和知识,这一段讲怎么把它们串成一条能跑通的链路。我挑三个最常见、也最能体现"进程 + 日志"配合的场景。

5.1 端口或文件被占用:从报错到定位

报错信息一般是这样的:Address already in usedpkg: 错误: 另外一个进程已经为 dpkg 前端锁 加锁device is busy。这三条本质上都是同一类问题——某个资源被另一个进程占着

排查链路是这样的:

ss -lntp | grep :8080 lsof -i :8080 fuser -v 8080/tcp

ssnetstat快,-l是监听状态,-n是数字端口,-t是 TCP,-p是显示进程。这条命令直接告诉你谁在监听这个端口。如果ss的输出里进程信息是空的,通常是因为权限不够,加sudo再看。

如果是文件锁,比如 dpkg 的场景,那锁文件的位置是固定的:/var/lib/dpkg/lock-frontend/var/lib/dpkg/lock。判断方式很简单——先看有没有另一个包管理进程真的在跑

ps aux | grep -E 'apt|dpkg' | grep -v grep

如果有,等它跑完就好,硬删锁文件会导致包数据库损坏。如果没有,说明是上次异常退出留下的僵尸锁,这时候删掉锁文件再重试是安全的。

这个判断顺序很重要。看到锁就去删,是很危险的习惯,因为真正的并发操作被破坏之后,修复成本远高于等几分钟。

5.2 CPU 或内存异常的定位过程

这是一个完整链路,我按实际操作顺序写:

第一步,拿到全局视图,找嫌疑人:

ps -eo pid,ppid,user,%cpu,%mem,etime,cmd --sort=-%cpu | head -10

第二步,对嫌疑 PID 做持续观察,看它是短暂尖峰还是持续高位:

pidstat -p 12345 1 10

第三步,如果是多线程服务,下钻到线程:

top -H -p 12345

拿到高 CPU 的 TID 之后,把它转成十六进制,再去jstack的输出里搜索对应的nid。这是 Java 服务定位热点线程的标准做法:

printf '%x\n' 12346 jstack 12345 | grep -A 30 'nid=0x303a'

第四步,如果怀疑是内存问题而不是 CPU,看进程的内存明细:

cat /proc/12345/status | grep -E 'VmRSS|VmSize|Threads' pmap -x 12345 | tail -5

VmRSS是实际占用的物理内存,VmSize是虚拟内存。这两个数差距特别大的时候,可能是 mmap 了很多文件,不一定是真泄漏。Threads是线程数,线程数持续增长基本可以确定是泄漏。

第五步,带着确定的时间点去翻日志:

journalctl -u myservice --since "10 min ago" | grep -i -E 'error|exception|timeout'

这里最关键的是"带着时间点去翻"。没有时间范围,你会在一堆日志里迷失;有了时间范围,再配合刚才观察到的现象(CPU 尖峰、内存增长),日志里的那条关键报错会自己跳出来。

我还想强调一点:不要一上来就 kill -9kill -9是 SIGKILL,进程没有任何机会做清理,正在写的文件可能损坏,正在处理的请求会直接断掉。正确顺序是先kill(默认 SIGTERM)给一次优雅退出的机会,等几秒,不行再kill -9。这个习惯在很多场景下能避免二次故障。

5.3 实时对照:一边跟日志一边看进程

有些问题需要观察进程和日志的对应关系,比如"每次请求进来,内存涨一点再降回去"这种。这时候需要两个窗口配合。

窗口一,实时跟日志:

tail -F /opt/app/logs/app.log | grep --line-buffered -E "request_id|ERROR"

窗口二,定时采样进程状态:

while true; do date '+%H:%M:%S' ps -o pid,%cpu,%mem,rss,cmd -p 12345 --no-headers sleep 2 done

这样每两秒打印一次进程的资源占用,和时间戳对在一起,再和日志里的时间戳对照,就能建立起"某个操作导致资源变化"的因果链。

tail这里用-F而不是-f,区别在于-F会在文件被轮转、被替换之后自动重新打开。日志轮转每天发生一次,用-f的话,轮转到零点你的跟踪就断了,而且不会提示你。这个细节知道的人不多,但影响很大。


6. 让这套观察能力长期可用:留存、采集和几个容易忽略的点

排查能力不光靠临场反应,还靠平时有没有把数据留住。出事的时候才发现日志只留了三天、或者根本不知道进程历史占用,那就只能干瞪眼。

6.1 日志留存策略怎么定

留存时长取决于用途。运行日志一般保留 7 到 30 天就够,因为问题通常几天内就暴露了。审计类的日志需要更久,180 天是个常见要求,因为很多合规场景要求能回溯半年。

实现方式上,单机的做法就是前面讲的logrotate,把rotate设成对应的天数:

daily rotate 180 compress

这样保留 180 个按天切割的压缩文件,刚好半年。

但要注意一个隐形成本:180 天的日志体积可能远超预期。一个每秒写几百行日志的服务,一天就是一个 GB 级别,压缩之后可能还有几十 GB,180 天下来磁盘直接满。所以策略上要分两档:近期(比如 7 天)不压缩保留原始文件方便查,远期压缩存储,更早的转存到别的地方或者直接删除。logrotate可以用maxage配合不同的配置实现,也可以分成多个配置文件处理不同目录。

判断磁盘会不会被撑满,可以用这个思路估算:单日日志量 × 保留天数 × 压缩比(文本日志压缩比通常在 5:1 到 10:1 之间)。算完和剩余磁盘空间对比一下,心里就有数了。

6.2 交互式操作的日志:容易被忽略的一环

有个细节很多人不注意:你在终端里敲的命令、看到输出,默认是不留痕的。等到想复盘"刚才那个报错到底是什么",终端早就滚没了。

解决办法是用script命令把会话录下来:

script -a /var/log/session-$(date +%F).log

执行之后,当前 shell 的所有输入输出都会被记录到那个文件里,退出时敲exit或者按Ctrl+D结束。-a是追加模式,同一天的多次操作会累积到同一个文件。

远程连接工具普遍也有保存会话日志的功能,把滚动缓冲区和日志文件都打开,是个好习惯。排查问题时最怕的不是查不到原因,而是"刚才明明看到过这个报错,现在找不着了"

6.3 应用层日志里最值得盯的几类

系统日志记录的是"系统层面发生了什么",但业务问题的线索往往在应用自己的日志里。几类特别值得配置好、盯紧的:

第一类是慢查询日志。数据库的慢查询日志(比如 MySQL 的slow_query_log)记录执行时间超过阈值的语句,这个阈值一般设在 1 秒左右。它的价值在于在上游服务还没报错之前,提前发现性能劣化的趋势。CPU 突然高的原因,十次里有三次能在慢查询日志里找到对应的时间点。

第二类是错误日志。很多框架默认只输出 info 级别,真正的问题被淹没了。生产环境把级别调到 warn 或 error,信噪比会好很多。

第三类是访问日志。它能给出请求量、响应时间、状态码分布。当进程数突然变多或者 CPU 变高时,先看访问日志里请求量是不是涨了,能一秒排除掉"流量突增"这个可能性。

配置这些日志的时候有个原则:日志里必须带时间戳、进程 PID 或者请求 ID。没有请求 ID 的多进程服务,日志就是一堆没法串起来的碎片;有了它,grep request_id就能把一次请求在所有进程、所有环节留下的痕迹串成一条线。这个投入是一次性的,但排查效率的提升是长期的。

6.4 我在实际使用中的几点体会

用这套方法排查了这些年,有几个体会比较深。

第一,命令的组合比单个命令的熟练度更重要。top的人很多,但知道top -H -p加上pidstat加上jstack这条链路的人少得多。真正解决问题靠的是把几个工具串起来,而不是把某一个工具的参数背全。

第二,先分类再动手,比直接上手快。看到 CPU 高就往 CPU 方向查,看到内存高就往内存方向查,这是本能。但前面提到的load高而 CPU 低的情况、D状态进程 kill 不掉的情况,都需要先判断类型,否则会在错误的方向上浪费大量时间。

第三,留一手永远比事后补救省事。日志多留几天、会话日志开着、关键指标做个定时采样,这些平时看着多余的动作,在真正出事的时候价值最大。我现在的习惯是,任何一台新上的机器,先把日志轮转配好、journal 持久化打开、异常检测脚本挂上,配置工作量不到半小时,但省下的是后面无数次的抓瞎。

第四,不要迷信任何一个命令的输出。psCOMMAND列会截断,top的 CPU 百分比是多核情况下的相对值(单核跑满在四核机器上显示 100% 而不是 25%,这个口径容易误导),docker logs默认只给标准输出不给标准错误。这些细节不搞清楚,很容易被"看起来对"的数据带偏。多源交叉验证,是排查里最该有的自觉。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询