1. 把 date 命令吃透:比起看时间,它更是脚本里的定时神器
很多人觉得 date 命令不就是打印一下当前时间吗?这想法没毛病,但只对了一半。在真实的生产环境里,date 是我写脚本时用到频率最高的命令之一,不是因为我要看时间,而是因为我要给文件命名、要算日志时间差、要生成定时任务的触发条件。如果你只会 date 不带参数地按回车,那你基本浪费了这个命令一半以上的价值。
先看最基础的用法,date 直接输出当前系统时间,格式大致是 Tue Apr 8 14:23:45 CST 2025。这个格式对机器来说不友好,对人来说也不够直观。所以我习惯用 date "+%Y-%m-%d %H:%M:%S" 这种自定义格式来输出,%Y 是四位年份,%m 是两位月份,%d 是日期,%H 是小时,%M 是分钟,%S 是秒。这套格式符你在 man date 里都能查到,但真正干运维的,至少要能把下面这几个记到肌肉记忆里:
| 格式符 | 含义 | 典型输出 |
|---|---|---|
%F |
完整日期,等价于 %Y-%m-%d |
2025-04-08 |
%T |
完整时间,等价于 %H:%M:%S |
14:23:45 |
%s |
从1970-01-01 00:00:00 UTC到现在的秒数 | 1744086225 |
%u |
星期几,1-7,1代表周一 | 2 |
%j |
一年中的第几天,001-366 | 098 |
%N |
纳秒 | 123456789 |
1.1 用 date -d 做日期运算,写备份脚本不再头大
date -d 是真正的杀手锏。它的作用是解析一个日期字符串,然后按你给的格式输出。关键在于,这个字符串可以带运算表达式。比如我想拿到昨天的日期,直接 date -d "yesterday" "+%F" 就行;想拿到七天前的日期,date -d "7 days ago" "+%F";想拿到下个月第一天,date -d "next month" "+%Y-%m-01"。
这在写日志清理脚本时特别实用。我有一次负责一台跑了好几年的应用服务器,磁盘快满了,排查下来发现是应用的历史日志文件堆积。当时写清理脚本,核心逻辑就是找到所有超过30天的日志文件并删除,但删除之前要先确认这些文件的日期格式。我用的定位方式就是先 date -d "30 days ago" "+%Y%m%d" 拿到一个基准日期,然后拿这个基准日期去和文件名里的日期字段做比较。简单粗暴,但确实是生产里最常见的用法。
date -d 还支持解析标准格式的日期字符串,比如 date -d "2025-04-01 12:00:00" "+%s" 可以拿到这个时间点的时间戳。反向操作也有,date -d @1744086225 可以把时间戳反解成人能读的格式。这种双向转换特别适合处理程序日志里的时间戳字段,你不需要写Python脚本,单靠 date 命令就能完成日志时间的清洗和换算。
1.2 date -s 手动校准服务器时间,什么时候才会用到
date -s 是直接设置系统时间的命令,比如 date -s "2025-04-08 14:30:00"。老实说,现在的服务器基本都用NTP服务自动同步时间,日常根本不需要手动设时间。但有一种情况你会用到它:测试环境里要模拟某个特定时间点的业务逻辑,比如验证优惠券过期、证书到期、定时任务触发等场景,又不能真的去改NTP服务器,这时候手动 date -s 就派上用场了。
不过我要提醒一句,date -s 改的是系统时间,不是硬件时间(CMOS时间)。如果你改完系统时间之后,想让它对硬件时间也生效,得手动执行 hwclock -w 把系统时间写回硬件。反过来,如果开机发现系统时间不对但硬件时间是对的,执行 hwclock -s 把硬件时间同步到系统。这一对命令你最好一起记,否则很容易出现"我明明设置了时间,重启又变回去了"的诡异问题。
还有一个细节经常被忽略:date 命令显示的时间是系统时间,时区由 /etc/localtime 决定。如果你发现服务器时间比北京时间差8个小时,不要急着 date -s,先看看时区是不是UTC。确认时区是否正确,用 timedatectl 命令查看,如果时区不对,执行 timedatectl set-timezone Asia/Shanghai 就能拉回来。这个坑我在新交付的云服务器上踩过不止一次,很多基础镜像默认时区是UTC,你往上部署应用,日志时间戳全是错的,排查问题的时候会非常困惑。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. top 命令:系统状态的第一现场,别只盯着 CPU 那一行
top 应该算得上是运维人每天打开服务器后第一个敲的命令。但说实话,大部分人用 top 就是看一眼CPU使用率和内存占用,然后就没有然后了。这个用法不能说错,但有点浪费。top 是一个动态更新的系统监控工具,它展示的信息密度极高,每一行都有它的价值,每一列都有它的意义。下面我按自己的阅读习惯来拆一遍。
2.1 第一屏怎么看:load average 才是系统压力的核心指标
top 第一行显示的是一天中的当前时间、系统已运行时间、登录用户数、系统平均负载。这里最有价值的是最后那个三个数字,load average: 0.08, 0.03, 0.01,分别代表过去1分钟、5分钟、15分钟的平均负载。
很多初学者搞不清楚这个负载数值到底意味着什么。它的本质是处于可运行状态和不可中断睡眠状态的进程平均数量。单核CPU的机器,负载接近1.0意味着CPU接近满负荷;四核CPU的机器,负载接近4.0才说明CPU接近饱和。很多网上的教程会告诉你"负载超过1就有问题",这个说法在单核机器上勉强成立,但放到多核机器上就会误导人。正确判断方式是看负载值除以CPU核心数,比值超过0.7就该警惕,超过1.0说明CPU已经成为瓶颈了。
还有一个经验之谈:光看1分钟负载容易误判,15分钟负载才能反映趋势。如果1分钟负载很高但15分钟负载很低,说明是突发流量或临时任务导致的尖峰,系统可能本身没问题;如果15分钟负载持续走高,那就要认真排查是不是有进程在慢慢泄漏资源或者业务量在持续增长。
2.2 进程列表怎么快速定位问题进程:按 CPU 和内存排序
top 默认按CPU使用率降序排列进程,但很多时候你需要按内存排序。这时候不用退出重进,直接在 top 界面里按 M 键就能按内存排序,按 P 键切回CPU排序,按 N 键按PID排序。这三个快捷键必须记牢,因为生产环境里你根本来不及慢慢鼠标点点点。
还有一个很实用的键是 c,按下之后会显示完整的命令行,而不只是进程名。很多时候你光看进程名根本不知道它在干什么,比如一堆 java 进程,每个占的内存都很大,你按 c 才能看到每个进程的启动参数,判断哪个是真正的业务进程,哪个是中间件进程。我之前排查过一台内存溢出的应用服务器,一进去 top 看到三个Java进程,每个都占了几十G内存,当时就是靠按 c 看完整命令行才分辨出哪个是主业务进程,哪个是消息队列客户端,缩小了排查范围。
top 里还有一个很少被提到的键,u,按下之后输入用户名,可以过滤只看某个用户的进程。排查CPU飙升时,如果怀疑是某个用户起的恶意进程,或者只想关注某个应用账号的进程消耗,这个键比 pgrep + top 组合要直接得多。
2.3 按 1 查看每个 CPU 核心的负载,别被"平均"骗了
多核服务器上,CPU使用率其实是一个非常不直观的数值。默认情况下 top 展示的是所有核心的平均使用率,但实际运行中经常出现单核跑满、其他核心空闲的情况。这时候你需要按数字 1,top 就会展开显示每个CPU核心的使用率。
按 1 之后你会看到 %Cpu0、%Cpu1 这样的独立行,每一行都包含us(用户态)、sy(内核态)、ni(nice优先级)、id(空闲)、wa(I/O等待)、hi(硬件中断)、si(软件中断)、st(被虚拟化偷走的时间)这几项。这里重点看两个:wa 代表I/O等待,如果这个数值居高不下,说明磁盘或网络I/O有瓶颈,不是CPU不够,加CPU也解决不了;st 代表虚拟机被宿主机抢占的CPU时间,如果云服务器上 st 很高,说明宿主机超售严重,你去投诉云服务商之前拿这个数据当证据就够了。
CPU有瓶颈和磁盘有瓶颈的处理方向完全不同,前者考虑加CPU、优化代码、降负载,后者要考虑换SSD、优化SQL、增加缓存。你第一眼的位置如果放错了,后面整个排查方向都会跑偏。
2.4 僵尸进程和 load average 的关系:top 里最容易被忽略的坑
top 进程列表里偶尔会看到状态为 Z 的进程,这就是僵尸进程。僵尸进程已经结束运行但父进程没有回收它的退出状态。它不占用CPU、不占用内存,但每一个僵尸进程都会占据一个PID,大量堆积会导致无法创建新进程。
有一次我处理一台流量入口服务器,业务方反馈新请求进来经常超时,我登上去 top 一看,进程列表底部有几十个 Z 状态进程。这些僵尸进程本身不吃资源,但它们的父进程是Nginx,父进程没有正确处理子进程退出后的回收逻辑。解决思路很明确,重启Nginx让所有子进程重新拉起,同时提醒开发排查worker进程的回收机制。如果你遇到类似场景,不要试图去 kill -9 僵尸进程,因为它已经死了,你杀不掉它;你需要杀掉或者重启它的父进程,系统才会去回收僵尸子进程。
3. 不只是 date 和 top:运维日常最高频的命令组合实战
到了这里,你可能会说:date 和 top 我理解了,但光靠这两个命令明显撑不起日常运维工作。确实如此。一个合格的运维工程师,掌握的从来不是单个命令,而是围绕某个场景的一组命令组合。下面我会按最常见的几个运维场景,把高频命令串起来讲,你拿过去就能直接改来用。
3.1 磁盘占用排查:df 和 du 的组合拳
磁盘写满是最常见的线上故障之一,处理流程我建议固定下来:先 df -h 看整体挂载点的使用率,定位哪个分区满了;再 cd 到对应目录,用 du -sh * 统计当前目录下每个子目录的大小,逐层往下定位到具体是哪个目录把空间吃了。
df -h 里的 -h 是human-readable的意思,会以G、M这样的单位展示,不加这个参数显示的是字节数,看起来非常痛苦。du -sh * 里的 -s 是summary,只显示汇总大小;-h 同样是人类可读单位;* 是当前目录下所有非隐藏文件。如果你要包含隐藏文件,写成 du -sh .[!.]* *。
真实生产里我遇到过一种情况:df 显示磁盘满了,但 du 统计下来根目录所有文件加起来远小于磁盘总容量。这种诡异现象大概率是有文件被进程删除了但仍然被进程占用着,du 统计不到,但空间就是没释放。解决方法是 lsof | grep deleted 找到被删除但仍被占用的文件,确认对应进程后重启它,空间才会真正释放出来。这个坑我第一次遇到时排查了将近两个小时,一开始怎么都想不通,后来才反应过来是日志文件被 rm 了但写入进程还握着文件描述符。
3.2 进程端口与连接排查:ss 取代 netstat 是趋势
查端口监听状态,netstat -tlnp 是老牌命令,但很多新版Linux发行版默认已经不装 netstat 了,而是推荐用 ss。ss -tlnp 和 netstat -tlnp 输出格式几乎一样,-t 是TCP,-l 是监听状态,-n 是数字显示端口不反解域名,-p 是显示进程PID和名称。区别在于 ss 的速度快很多,尤其在连接数上万的情况下,netstat 会卡到让你怀疑服务器死机了,ss 几乎秒出。
要查某个具体端口被哪个进程占用,可以 ss -tlnp | grep 8080。要查服务器上当前的TCP连接状态统计,ss -s 一条命令就能看到总的established、time-wait、close-wait数量。这个命令在排查并发连接异常时特别好用,如果发现 time-wait 数量高得离谱,大概率是短连接场景下没有开启连接复用,网络层和应用层配置需要一起调优。
我个人现在的习惯是:能用 ss 就不用 netstat,能写全参数就写全参数,而且是 -tlnp 四个参数一把梭。到了新环境先确认有没有 ss,没有就 yum install iproute2 或者 apt install iproute2,比装 netstat-tools 要更符合现代Linux的发展方向。
3.3 内存排查:free 命令的三个关键数字
free -h 是查看内存使用率的基础命令,但很多人被输出里的 buff/cache 一栏搞迷糊了。看到buff/cache占用高就紧张,其实大可不必。Linux的设计哲学是"空闲内存不如拿来用",所以它会尽量把内存用作页缓存(page cache)来加速文件读写,这部分内存在其他程序需要时是可以被回收的。
真正要关注的是 available 这一列,它才是系统实际可用内存的真实估算值。free -h 显示的第二行 Mem: total used free shared buff/cache available,注意看 available 而不是 free,因为 free 已经把buff/cache全部排除掉了,但available考虑了cache的可回收性,参考价值更大。
真正内存不足的信号是:available 已经很低、used 持续走高、同时系统开始使用swap(用 free -h 看Swap的used字段增长)。一旦发现swap使用量在持续增长,说明物理内存真的不够了,要么加内存,要么优化应用的内存占用,要么调低JVM堆内存配置。不要一看到swap有使用就紧张,少量使用swap是正常的,尤其是那些申请了超大堆内存但实际用不满的Java应用。
3.4 系统日志定位:journalctl 和 tail -f 的配合
排查线上问题时,日志是唯一能还原现场的证据。传统的日志排查流是 tail -f /var/log/messages 或者 tail -f /var/log/syslog,实时跟踪系统日志输出。但现代Linux系统大多使用systemd,系统日志由 journald 管理,用 journalctl 命令查看会更方便。
journalctl -f 是实时跟踪模式,类似 tail -f;journalctl -u nginx.service 只看某个systemd单元(服务)的日志;journalctl --since "30 minutes ago" 只看最近30分钟的日志;journalctl -p err 只看error级别及以上的日志。这些参数组合起来非常灵活,比如排查Nginx启动失败,直接 journalctl -u nginx --since "1 hour ago" -p err 就能拿到最近一小时内Nginx启动错误的核心信息。
journald 的日志是二进制存储的,不能用普通的 cat 命令直接读取,但 journalctl 命令本身并不难记。你只要记住三个最常用的:-u 指定服务单元,-f 跟随模式,--since 时间过滤,基本就能覆盖90%的场景。如果你用的发行版是把journal日志持久化到磁盘的,日志文件在 /var/log/journal/ 目录下,重启服务不会丢日志;如果目录不存在,说明journal是内存模式,重启就丢,需要手动创建目录并设置文件权限才能持久化,这个配置项叫 Storage=persistent。
3.5 高频指令速查:一张表记下日常运维的"万能钥匙"
前面讲的都是单个场景,这里我整理一个自己平时最常用的小抄,不一定覆盖所有命令,但都是我真实摸过、踩过坑之后留下来的:
| 场景 | 命令组合 | 备注 |
|---|---|---|
| 查磁盘使用率 | df -h |
先看整体,定位哪个分区满 |
| 查目录占用 | du -sh * |
逐层深入,定位具体目录 |
| 查端口监听 | ss -tlnp |
比netstat快,推荐优先用 |
| 查进程状态 | ps aux | grep xxx 或 top 后按 c |
top适合动态跟踪,ps适合静态快照 |
| 查内存 | free -h |
重点看available列 |
| 系统日志 | journalctl -u 服务名 --since "30 min ago" |
分服务查日志效率最高 |
| 跟踪日志 | tail -f /var/log/xxx.log |
实时观察应用输出 |
| 系统版本 | uname -a 和 cat /etc/os-release |
排查内核与发行版信息 |
| 运行时间与负载 | uptime |
快速了解系统状态,三个负载值都有 |
| 网络连通性 | ping -c 4 目标IP 和 curl -v http://目标 |
ping不通查网络,curl无响应查服务 |
这套组合拳基本能覆盖日常巡检、故障初判、信息收集这几类工作。运维的核心能力不是背下几百条命令然后表演默写,而是遇到问题的时候能快速缩小范围,把"不知道哪里有问题"变成"就这几台机器、这几个指标、这几个进程有问题"。上面的每一条组合拳都是往这个方向使劲的。
4. 我将近十年的运维心得:真正理解命令背后的原理,遇到问题才不会慌
命令本身不难,难的是面对一个模糊的故障现象,你能快速选出正确的命令组合,并正确解读输出结果。这里我分享三个最典型的心得,也是我在实际排障中最常用来验证自己判断的思路。
4.1 常见问题速查表:症状、命令、结论用一张表说清楚
| 故障现象 | 首选命令 | 关键看什么 | 可能的结论 |
|---|---|---|---|
| 系统响应慢、CPU高 | top 按P排序 |
哪个进程的CPU高 | 应用代码死循环或流量突增 |
| 系统响应慢、CPU一般、但在等I/O | top 看wa列 |
wa值是否持续偏高 | 磁盘I/O瓶颈 |
| 磁盘显示满但找不到大文件 | df -h + lsof | grep deleted |
是否有deleted文件被进程占用 | 文件被删但仍被进程占用,需重启进程 |
| 服务启动失败 | journalctl -u 服务名 -p err --since "10 min ago" |
报错关键信息 | 配置错误、端口占用、依赖服务未启动 |
| 某个端口连不上 | ss -tlnp |
端口是否在LISTEN状态 | 服务没启动或被防火墙拦截 |
| 服务器时间不对 | date + timedatectl |
时区是否正确 | 时区配置错误或NTP同步失败 |
| 内存不足但free有空间 | free -h |
available值是否很低 | buff/cache可回收,但可用内存已紧 |
| 某个脚本定时任务没执行 | date + crontab -l + journalctl -u crond |
定时任务触发时间与系统时间是否一致 | 系统时间与预期不符,或cron配置错误 |
这张表的用法不是等你出问题了才看,而是建议你先把表里的每条命令都亲手在测试机上跑一遍,把输出格式看熟了。真到了线上出问题的时刻,你连打开查表的功夫都未必有,肌肉记忆才是最可靠的。
4.2 排查流程的思维定式:从整体到局部,从系统到应用
运维排障最怕的就是没有章法,东敲一下西看一下。我自己的固定思维框架是四步走:
第一步,uptime 和 top 快速判读系统整体状态,是负载高、CPU忙、内存储紧张还是磁盘有瓶颈,先定位"系统层面有没有问题"。
第二步,如果系统层面正常,进入应用排查。ss -tlnp 确认端口监听状态,journalctl 或 tail -f 看应用日志,ps aux 确认进程是否存在、运行时长、父进程是谁。这一步解决"应用层面是不是正常"。
第三步,如果单机层面看起来都正常但业务依然异常,把视角拉大到网络和上下游依赖。ping 测本机到依赖服务的连通性,curl -v 测接口响应,df -h 确认日志盘还有没有空间。这一步解决"是不是别的环节拖垮了我"。
第四步,如果以上都正常,大概率是应用代码或业务逻辑本身的问题,把第三方的日志、监控数据、错误堆栈拉出来,拉上开发和DBA一起碰。这一步解决"是不是代码缺陷或数据异常"。
这套流程看起来简单,但真正执行的时候非常容易跳步。我第一次独立排障时,直接钻到应用日志里翻了一个多小时,最后发现是日志盘满了导致应用写不进日志但还在持续写,白白浪费了大量时间。从那以后我养成了习惯:任何故障,先花30秒看系统整体状态再做深入。
4.3 新手最容易踩的坑:别再犯这三个低级错误
第一个坑:top 里看到CPU高就直接 kill 进程。这个问题经常出现在新手身上。CPU高不代表进程有问题,可能是正常的业务高峰期,可能是它正在处理大量请求。正确做法是先用 top 按 c 看完整命令行,了解这个进程是干什么的,再结合时间点判断是正常负载还是异常消耗。我记得有一次,某台服务器在每天晚上8点左右CPU就会飙到90%以上,业务方很紧张,结果我们查日志发现是系统的定时备份任务正好排在那个时间点,属于完全正常的行为。如果没有先做判断就盲目重启进程,反而会触发新的故障。
第二个坑:重启服务之前不收集现场信息。很多运维新人在遇到服务异常时会下意识选择最快路径,直接重启服务,觉得重启完了就好了。但如果不去收集异常发生时的日志、进程状态、内存快照、网络连接信息(这些信息靠journalctl、ss、ps、free等命令收集),问题虽然暂时消失,风险却一点都没有排除。比如有一个常见情况是内存泄漏,服务重启后内存占用会回落到低位,看起来一切正常,但运行几天后又开始报警,如果不收集现场信息,就只能反复重启同一个服务,持续处于被动状态。正确做法是每次重启前强制自己先做一轮信息收集,最好能把日志目录、进程列表、内存状态、网络连接状态都记录下来,哪怕最后没派上用场,也比事后抓瞎强得多。
第三个坑:忽略系统时间和时区的影响。你可能会觉得时间问题有什么好说的,但真实生产里因为时间不对引发的故障实在太多了:日志时间戳和实际时间对不上导致排查困难、定时任务在错误的时间被触发、证书校验因为系统时间和实际时间差太多而失败。我之前在一台云服务器上部署过一个需要调用外部支付接口的服务,突然有一天所有请求都返回证书错误,
