news 2026/9/15 3:26:28

服务器故障排查清单:12种常见问题定位与处理全指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
服务器故障排查清单:12种常见问题定位与处理全指南

做服务器运维这些年,我最怕听到的一句话不是“服务器挂了”,而是电话那头补一句“你自己看吧,我啥也没动”。半夜两点的机房告警,周末的微信轰炸,新手接手一台来历不明的服务器,面对的往往是一个黑盒加一堆模棱两可的现象。但时间长了你会发现,服务器故障和人体疾病一样,大部分都是常见病、多发病,来来回回就那么些套路。这篇文章我把这些年高频踩中的12种服务器基础故障整理成了一份排查清单,每种都给出明确症状、判定方法和处理路径。刚入行的运维照着走一遍基本能解决八成问题,兼职管服务器的开发和小团队负责人也能拿来当速查手册。

1. 排错框架先行:三层定位法和证据链思维

1.1 三层定位法:网络层、系统层、应用层

排查服务器故障最大的坑,不是现象多,而是现象会骗人。比如“网站打不开”这个描述,既可能是指网络不通、也可能是指DNS解析失败、还可能是Nginx进程死了、甚至可能是后端数据库连接数满了。如果你一上来就直接重启服务器,问题可能暂时“消失”,但根因还在,过两天还会再犯。

我把排查思路收敛为三层定位法:先判断问题出在哪一层,再决定要不要动手。下面是这三层分别关心什么:

定位层核心问题典型命令
网络层链路通不通,端口通不通ping、telnet、ip a、route、ss
系统层资源够不够,进程活没活top、free、df、ps、dmesg
应用层服务本身是否正常工作curl、日志文件、服务状态

顺序很重要。很多时候应用层报错,根子却在系统层或网络层。比如MySQL报连接超时,你先去看MySQL配置,翻了半天发现是磁盘满了导致写入阻塞——这就是典型的层级判断错误。我的习惯是:先确保网络层通,再看系统层资源,最后才进应用日志里抠细节。

1.2 排错黄金步骤:先取证,后变更

除了层级判断,我还有个铁律:排障过程中先收集证据,再做变更。很多新手一看到服务挂了就立刻重启,相当于警察没到现场就把尸体烧了,根因永远查不出来。

我通常遵循一条“排错六步”:

复现现象 → 收集日志/状态快照 → 提出假设 → 用最小代价验证 → 执行修复 → 观察验证。

比如磁盘满这种问题,你在df -h那一刻看到的数据就是关键证据。如果直接删文件释放空间,空间是有了,但你可能永远不知道是什么把磁盘写满的——下次它还会再满。正确的做法是:先df -hdu -sh /*把现场拍下来,定位到具体是哪个目录在膨胀,再决定要清理还是要切割。这个习惯让我少踩了无数个“治标不治本”的坑。

2. 网络与连接故障:ping不通、SSH失败、端口假监听

2.1 故障一:服务器完全ping不通

这是最经典的“服务器挂了”,但原因往往非常低级,大概率是线路问题而不是整机宕机。

按链路顺序排查,别跳步:

第一步看本机网络状态。登录服务器控制台(云平台的控制台或者物理机的KVM/IPMI)执行ip a,看网卡有没有IP,状态是不是UP。物理机还要去机房看网卡灯亮不亮,虚拟化环境要看虚拟机网卡有没有被平台踢掉。很多“ping不通”其实只是网卡没起来或IP漂移了。

第二步ping网关。ip route先看默认路由,然后ping网关地址。网关通说明物理链路和二层转发没问题;网关不通,问题就出在这台机器到交换机之间,可能是网线松动、交换机端口VLAN配错、或者网卡驱动挂了。

第三步ping外网IP。网关通但外网不通,大概率是路由或防火墙问题。查iptables -L -n的OUTPUT链、firewalld区域规则,以及是否有策略路由把流量引到了错误方向。

第四步最后再怀疑安全组。云服务器尤其要注意,系统内部怎么查都通,但外部ping不通,八成是控制台安全组的入方向规则没放行ICMP。也别急着加规则,先确认安全组所属的实例是不是这台机器。

有次同事给一台新服务器配IP,就是子网掩码抄错了一位,结果本网段能通、跨网段全丢包。这种问题排查起来心累,但按链路顺序走一遍,十分钟内必然定位。

2.2 故障二:能ping通,但SSH连不上

能ping通说明网络层是通的,问题收窄到了远程管理通道。SSH连不上要分三种情况处理,千万不要混为一谈:

  • 连接超时:TCP层都没通。多半是防火墙丢弃了22端口包,或者云安全组没放行22端口。先telnet IP 22试一下,超时基本就是被防火墙拦了。
  • 连接被拒:能到达服务器但端口没监听。systemctl status sshd看服务状态,再用ss -tlnp | grep 22确认sshd到底有没有监听。被拒还有一个常见原因是ssh改了非标准端口,你还在敲默认22。
  • 认证失败:能连上但密码/密钥不对。这时候别再瞎试密码了,直接看/var/log/secure(CentOS系)或/var/log/auth.log(Ubuntu系),翻到最后几条日志,是密码错还是公钥校验失败一目了然。

还有一个特别容易踩的坑是host key冲突。重装系统后,用SSH客户端连接同一台服务器,会报REMOTE HOST IDENTIFICATION HAS CHANGED。这是客户端在保护你——防止中间人攻击。解法很简单:编辑本地~/.ssh/known_hosts,删掉对应IP的那一行,重新连接即可。千万别图省事把整个known_hosts文件清空或者关掉StrictHostKeyChecking,那是把大门锁拆了。

顺便说一句,用VSCode远程开发连服务器时,如果改了SSH端口,记得在~/.ssh/config里给这台主机定义一个别名和Port参数,否则默认还是走22端口,连不上是必然的。

2.3 故障三:端口明明在监听,外部却访问不了

这个故障我见得太多了,尤其是刚部署完Web服务的新手最容易遇到。症状是:你自己在服务器上curl http://127.0.0.1:8080一切正常,但浏览器从外部访问就是打不开。

ss -tlnp查一下监听地址,大概率能看到这样一行:

LISTEN 0 128 127.0.0.1:8080 0.0.0.0:*

问题就在这里。127.0.0.1:8080意味着服务只绑定了回环地址,外网流量根本进不到这个进程。很多应用默认会绑定到localhost,比如某些版本的Nacos、Redis、MySQL、Spring Boot内置Tomcat,如果你部署后没改bind地址,外部当然访问不了。

解决办法是改配置,把监听地址改到0.0.0.0或者具体的内网/公网IP。但这里我要多说一句:运行时绑定到127.0.0.1在不少场景下是刻意的安全设计。比如数据库只允许应用本机访问,就不应该为了图方便改成0.0.0.0。要先想清楚这个端口到底需不需要对外暴露,再动手改。

另一个外部访问不了的原因是云服务器安全组。系统防火墙放行了,服务也绑到了0.0.0.0,但云平台控制台安全组的入方向规则没放行该端口,照样访问不了。这个经常会和系统防火墙混淆,排查顺序固定为:服务监听地址 → 系统防火墙 → 云安全组 → 硬件/云防火墙。

3. 资源耗尽三件套:CPU飙高、内存枯竭、磁盘打满

3.1 故障四:CPU使用率飙升

CPU飙高是服务器运维的日常,问题是怎么判断是正常业务负载还是异常程序在作怪。

先看top,按CPU排序,找到占CPU最高的进程。但在这里我要先纠正一个常见误区:load average高不代表CPU满。它统计的是处于运行态和不可中断睡眠态的进程数,磁盘I/O等待也会让负载飙升。不信你在一个传统机械硬盘的机器上做大量小文件读写,load average可能冲到10以上,但CPU的ussy都很低,大把时间花在wa(I/O wait)上了。

如果确认确实是CPU高,再分us(用户态)和sy(系统态):

us高:业务进程在消耗CPU,下一步用top -H -p PID看线程维度,再用pidstat -t -p PID 1看具体哪个线程。Java应用可以jstack导出线程栈,Python/PHP就strace -p PID看到底在调什么系统调用。常见肇事者包括:没加索引的慢查询、死循环、正则灾难性回溯、客户端连接疯长。

sy高:CPU耗在内核态,典型的场景是虚拟化环境的网络软中断(可以用top里看si那一栏)、系统调用过于频繁、或者被某些内核模块拖累。还有一种特殊情况是服务器被当作跳板机,大量转发流量进来,也会导致sy高。

这里必须要提一下挖矿木马。中招的机器CPU使用率直接飙到100%,top里能看到一个名字随机或伪装成系统服务的进程,比如kdevtmpfsi这种。处理步骤是:先用ls -l /proc/PID/exe找到可执行文件路径,再用lsof -p PID看它打开了哪些文件,接着crontab -l查有没有可疑计划任务,最后检查~/.ssh/authorized_keys有没有被塞入陌生公钥。杀掉进程只是开始,不把后门清干净,过几天它还会回来。

3.2 故障五:内存耗尽触发OOM Killer

内存问题比CPU更容易迷惑人,因为Linux的内存管理机制和Windows不一样。很多人看到free -h里used很高、free很低就慌了,其实只要available那一列还有富余,就不需要紧张。

available是一个重要指标,它估算出在不触发swap的情况下,系统还能分配多少内存给新进程。Linux会把大量空闲内存用作page cache(文件缓存),这部分缓存内存是可以在内存紧张时自动让出来的。所以判断内存够不够用,看available而不是裸的free

真正需要警惕的是OOM Killer。当系统内存耗尽、swap也被吃满时,内核会挑选一个进程杀掉来释放内存——而且它挑的不一定是你以为的那个进程。症状就是服务突然消失,dmesg -T | grep -i oom能看到内核日志:

Out of memory: Killed process 1234 (mysqld) total-vm:8388608kB

我见过最典型的案例:Java应用-Xmx8g跑在8G内存的机器上,堆内存+线程栈+元空间+GC开销叠起来早就超了物理内存,一启动没多久就被OOM掉。这种其实很好排查,因为日志里会持续复现。

处理内存问题的几个要点:

  • free -h先确认available是否告急,cat /proc/pressure/memory可以看内存压力;
  • ps aux --sort=-%mem | head找内存消耗大户;
  • 查swap:swapon -scat /proc/swaps,swap长期被大量使用,说明物理内存确实不够了;
  • 按业务性质调整:Java调堆大小,Redis设置maxmemory,数据库优化连接数。

临时急救可以直接sync && echo 3 > /proc/sys/vm/drop_caches清理page cache,但这只是治标,根本解法是扩容内存或者优化应用内存占用。

3.3 故障六:磁盘空间写满或inode耗尽

磁盘满是最常见的“服务器突然写不了数据”的原因。排查流程分两步:

先看容量:df -h定位哪个分区满了,然后用du -sh /目录逐层往下找,定位到大目录再du --max-depth=1往下钻。等找到真凶,可以用find / -size +1G -exec ls -lh {} \;快速找出超大文件。

再看inode:有时候df -h显示还有空间,但应用就是报“No space left on device”,这时候要查df -i。inode耗尽是因为文件系统里小文件数量太多,把索引节点全占光了。常见于邮件队列、PHP session目录、Docker容器产生的大量JSON日志。处理思路是删除无用的海量小文件,并修改应用让它不再疯狂生成。

还有一个隐藏很深的坑——文件删了但空间没释放。有进程一直持有某个已删除文件句柄,磁盘空间明明被占用,但你ls找不到这个文件。典型场景是日志文件被误删,但写日志的进程(Nginx、Tomcat等)还开着那个文件句柄。用lsof +L1 | grep deleted找到这些进程,重启或重载进程,空间才会真正释放。

遇到磁盘满先别急着删文件,一定先du定位、再确认是不是有进程占着句柄,最后再动手清理。之前有个项目就是这种情况,运维删了access.log,但Nginx不重载,空间一直不释放,业务一直接连报错,重启Nginx之后才恢复。

4. 系统层翻车现场:时间漂移、开机失败、RAID降级

4.1 故障七:系统时间悄悄漂移

时间问题是最容易被低估的故障类型,但它造成的破坏极其迷惑——HTTPS证书校验失败、Kerberos票据失效、数据库主从复制错乱、日志时间排序全乱。有一种非常诡异的现象:系统登录突然提示“凭证过期”或“证书错误”,你查证书明明有效,最后发现是服务器本地时间偏了好几十分钟。

排查非常简单:

date timedatectl status

如果时间不对,首选方案是配置chrony做时间同步。编辑/etc/chrony.conf,加上国内可用的NTP服务器:

server ntp.aliyun.com iburst server ntp.tencent.com iburst pool 2.pool.ntp.org iburst

改完重启服务:

systemctl restart chronyd chronyc sources -v

我自己维护的一批机器都配置了chrony作为NTP客户端,并要求监控告警里加上时间偏移量检查。另一个之前踩过的坑:在云服务器上手动执行hwclock --systohc把系统时间写进了硬件时钟,结果重启之后时间反而更乱了。正确做法是用timedatectl set-ntp true交给系统自动管理。

如果你负责的是一组内网服务器,还可以挑一台机器作为时间服务器,其他机器统一从它同步。做法是在这台服务器上配置chrony允许内网网段访问:

allow 192.168.0.0/16

其他机器上把server指向这台时间服务器的内网IP,这样整个集群的时间就能保持一致。这个方案在无外网的内网环境里尤其有用,比让每台机器自己找公网NTP稳定得多。

4.2 故障八:开机进入emergency mode或GRUB起不来

服务器无法开机,是最让人头皮发麻的故障,但其中绝大多数问题出在一个很基础的配置上——/etc/fstab写错了

我见过太多这种场景:新加了一块数据盘,手滑写错了UUID或者挂载路径,执行reboot之后系统直接卡在emergency mode,登录进去一看,根文件系统被挂载成只读。这是因为systemd在启动时发现某个挂载项失败,为了安全起见进入了紧急模式。

处理流程:

  1. 通过物理控制台或云控制台的VNC/救援模式进入系统;
  2. 执行mount -o remount,rw /把根分区切成可写;
  3. vim /etc/fstab检查刚才写的挂载项,错误的行先注释掉;
  4. blkid查出正确的UUID,或确认挂载路径的目录确实存在;
  5. 再执行umount -a测试挂载配置是否正确(谨慎使用),然后reboot

如果连GRUB都起不来,问题更严重一些。常见原因有:引导分区损坏、内核文件丢失、或者双系统安装时引导记录被覆盖。处理思路是使用系统安装盘的rescue模式启动,chroot进原系统环境后重新生成引导:

mount /dev/sda2 /mnt mount /dev/sda1 /mnt/boot chroot /mnt grub2-mkconfig -o /boot/grub2/grub.cfg grub2-install /dev/sda

这里有个经验教训:执行grub2-install之前一定要确认BIOS里的启动顺序和磁盘识别顺序一致。前些年有台服务器就是装到了sdb上,重启后系统直接找不到引导。别问我怎么知道的。

4.3 故障九:RAID阵列降级或损坏

每个把RAID当备份用的人,迟早要吃大亏。RAID不是备份,它的核心目的是保障单块磁盘故障时业务不中断,但绝不防误删、不防勒索病毒、不防双盘同时故障。

排查RAID故障的第一步是确认当前状态:

cat /proc/mdstat mdadm --detail /dev/md0

软件RAID看这两个命令就够了。硬件RAID需要厂商工具,比如LSI/MegaRAID用MegaCli64,戴尔用perccli,惠普用hpssacli。这些工具能查到物理磁盘状态和控制器日志。

当界面显示degraded状态时,说明阵列里有一块盘掉了,数据还在但已经失去冗余保护。这时候最忌慌乱,按以下顺序处理:

  1. mdadm /dev/md0 -f /dev/sdb把坏盘标为fail;
  2. mdadm /dev/md0 -r /dev/sdb从阵列中移除;
  3. 物理更换新硬盘;
  4. mdadm /dev/md0 -a /dev/sdb把新盘加入阵列;
  5. 查看cat /proc/mdstat确认rebuild进度。

整个重建过程会持续一段时间,期间系统I/O负载会明显升高。如果是生产环境,建议在业务低峰期操作,同时做好临时备份。

还要强调两个容易犯的错误:一是新换的硬盘容量不能小于阵列中其他盘,我做RAID5时曾经插了一块不同容量的盘,结果识别不出来,白白浪费时间;二是RAID5在单盘降级期间如果再挂一块盘,数据基本救不回来,这种时候不要盲目重建阵列,第一时间联系专业数据恢复才是止损的正确方式。磁盘出现大量I/O error(看dmesg)或者SMART日志开始报警时,就该提前换盘了,别等降级了才行动。

5. 应用层疑难杂症:Web假死、FTP传不动、日志雪崩

5.1 故障十:服务进程活着,但Web站点“假死”

这个故障的迷惑性在于:你ps -ef | grep nginx能看到master进程和worker进程都在,但网站就是打不开。这种就叫“服务假死”。

排查顺序我建议从外到内:

先看本机健康状态:curl -I http://127.0.0.1看是否响应。如果这里都卡住,问题在服务自身。

再看连接状态:ss -s看连接总数和TIME_WAIT数量。如果TIME_WAIT堆积到几万,说明短连接负载过高,需要开连接复用。但注意,net.ipv4.tcp_tw_reuse可以开,net.ipv4.tcp_tw_recycle慎开——它会在NAT环境下把正常用户连接给重置掉。

然后看Nginx日志:/var/log/nginx/error.log是定位问题的金矿。如果发现大量worker_connections are not enough,说明worker_connections配置太小,或者worker进程数不够,需要调大。还有一种情况是背压问题:Nginx本身没毛病,但后端的PHP-FPM或Java服务挂掉了,导致所有请求都在等待后端响应。

PHP-FPM的经典症状是日志里大量出现:

WARNING: [pool www] server reached pm.max_children

这说明PHP-FPM的pm.max_children已经耗尽,新来的PHP请求全部排队等待,Nginx随之超时。处理办法是:调大pm.max_children,同时排查后端有没有慢请求把进程池占满——通常用slowlog或者request_slowlog_timeout找出哪些脚本卡住了。

对于长期运营的业务,最终解法往往不是临时调参,而是加缓存、加机器、做限流。之前一个线上商城就是高峰期图片请求把Nginx worker全占满,加了页面缓存之后才彻底解决。

5.2 故障十一:FTP能连上,但列目录/传文件卡死

FTP这个协议设计于上世纪70年代,它在NAT和防火墙环境下工作时,能踩的坑基本都被人踩遍了。

先理解FTP一个反直觉的地方:FTP用两个连接,一个控制连接(21端口),一个数据连接(动态端口)。能不能登录看的是控制连接,能不能列目录、传文件看的是数据连接。所以会出现“能登录但列目录卡死”这种诡异现象。

FTP有两种数据连接模式:

模式数据连接建立方式NAT/防火墙环境表现
主动模式(Active)服务器主动连接客户端的随机端口客户端在内网时基本必挂
被动模式(Passive)客户端连接服务器指定的随机端口需要防火墙放行对应端口段

绝大多数现代网络环境都要求使用被动模式。服务器的vsftpd配置需要明确被动端口范围:

pasv_enable=YES pasv_min_port=30000 pasv_max_port=31000

然后防火墙必须放行这个端口段。只放行21端口的防火墙规则,几乎是所有FTP问题的共同答案。

Windows Server上配置IIS FTP也会遇到相同问题,控制面板勾选FTP服务后,防火墙虽然自动生成了规则,但通常只放行21。要么手动加上被动端口段,要么干脆放弃纯FTP。

我的建议是:能不用FTP就别用FTP。现在的SFTP基于SSH,一个22端口全部搞定,不需要被动端口段,传输加密,权限体系也成熟。Windows Server装了OpenSSH Server之后就能直接SFTP,Linux本来就用ssh自带的sftp子系统,没必要抱着几十年前的老协议不放。

5.3 故障十二:系统日志雪崩式增长,拖垮整台机器

日志本身是帮我们排查故障的,但如果日志失控,它自己就会变成一个故障源。

症状通常是这样:某个服务开始疯狂报错,日志文件以GB级速度增长,导致/var分区被写满,系统出现各种莫名其妙的问题。更恶心的是,日志雪崩往往和磁盘满互为因果——磁盘满触发更多写失败,写失败产生更多错误日志,形成恶性循环。

我曾经处理过一个游戏服项目:Nginx access.log几个月没切割,一个文件涨到30GB,然后把/var分区占满,数据库和存档同盘,差点把玩家数据搞没。从那以后,所有服务器的日志切割都纳入了我的基础配置清单。

应急清理命令:

truncate -s 0 /var/log/nginx/access.log

注意用truncate而不是rm,直接删文件会让持有句柄的进程继续往这个“幽灵文件”里写,空间根本释放不了。

根治方案是logrotate按天切割:

daily rotate 30 compress delaycompress copytruncate

copytruncate参数很重要——日志文件被复制后原文件被清空,不需要重启服务进程。

还有journald日志系统也要管,它默认存储在/var/log/journal里,时间久了也能占掉大量空间:

journalctl --vacuum-size=500M

同时修改/etc/systemd/journald.conf

SystemMaxUse=500M

最后建议把重要日志集中起来,统一发到ELK或Loki这类日志平台。这样既避免日志堆在单台服务器上挤占磁盘,排查问题时也能一次搜索多台机器,比挨个上去tail -f效率高太多了。

排障这一年一年做下来,最大的敌人其实不是故障本身,而是“猜”。不带证据就上手操作,和蒙着眼睛拆炸弹没什么区别。这套排查思路陪我处理过几百次事故,不能说每次都完美,但至少让我在紧急时刻不慌——因为我知道先看哪一层、先收集什么证据,再决定动什么刀。

另外很建议你给自己维护的服务器建一个“故障档案”,用最普通的Markdown文件记录:日期、故障现象、排查过程、根因、处理命令、后续预防措施。别嫌麻烦,半年后翻出来看,你会发现那些曾经的“疑难杂症”都已经变成你最快的定位经验。服务器踩坑是踩不完的,但每踩一次都记下来,下一次就会快很多。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/15 3:26:09

AI时代CLI工具复兴:高效开发与自动化实践

1. AI Agent时代CLI复兴现象解析最近半年在开发者社区观察到一个有趣现象:当各大科技公司都在为AI Agent开发华丽的图形界面时,一批以Codex CLI、Gemini CLI为代表的新型命令行工具却逆势崛起。我的团队在开发AI辅助编程工具时,最初也设计了完…

作者头像 李华
网站建设 2026/9/15 3:24:52

嵌入式低功耗策略:收益量化、风险权衡与平衡之道

做嵌入式这些年,我最深的体会是:低功耗策略这项工作是典型的“表面越简单,背后越复杂”。一块电池、一颗MCU、一个无线模组,看起来只要让设备多睡一会儿就能省电,可真把功耗曲线打出来,你会发现每一微安都在…

作者头像 李华
网站建设 2026/9/15 3:24:43

万岳网校源码深度拆解:直播课堂与多端部署实战指南

简介:2024最新万岳开源网校源码,是一套面向教育培训机构、职业院校及独立讲师的开源在线教学系统。采用原生语言开发、多端互通,集教学、学习、管理、互动、营销于一体,支持视频直播、语音直播、PPT直播,覆盖大班课、小…

作者头像 李华
网站建设 2026/9/15 3:24:29

不懂代码也能改,WordPress点击量改热度完整流程

不懂代码也能改,WordPress点击量改热度完整流程 想搞懂 WordPress点击量改热度 却卡在手不会写代码?这简直是无数中小企业主和独立开发者的噩梦。别慌,今天咱们就把这事儿掰开揉碎了讲,给你一套 完整流程 ,哪怕你连FTP都没登过,照着做也能把文章的热度逻辑改得明明白白。…

作者头像 李华
网站建设 2026/9/15 3:23:39

华为OD机考《游戏分组》五种语言解法:DFS、背包与细节避坑

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/15 3:23:35

长时间通勤自救指南:4小时通勤的时间与精力管理

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华