磁盘I/O性能排查利器:iostat核心指标与实战案例

1. 为什么排查磁盘问题我总是第一个打开 iostat

干运维和后台开发这些年,只要线上出现“服务卡顿”“接口超时”“数据库突然变慢”,我下意识就会敲一条命令看看磁盘到底在干嘛。这条命令就是iostat。它不是万能的,但绝对是最先该看的那个——因为磁盘 I/O 性能问题往往藏得很深,CPU 高了你一眼就能看到,内存满了 top 也能立刻暴露,唯独磁盘这块,你不主动去测去查,它可能默默拖垮整个业务,而你还在傻乎乎地调应用代码。

iostat是 Linux sysstat 软件包里的一个性能分析工具,专门用来监控系统的输入输出设备负载情况,说人话就是:它告诉你磁盘当前有多忙、读写速度是多少、I/O 请求排了多长的队、每个请求等了多久才被处理完。这些信息对定位“存储瓶颈”至关重要。

这篇内容我打算从一个实际使用者的角度,把 iostat 的指标、参数、使用场景、常见坑全部梳理一遍。不管你是刚入行的运维新人,还是写后端服务的开发,这篇内容都能帮你少走弯路。我尽量不说废话,直接用我这么多年踩过的坑和验证过的方法来讲。

2. iostat 到底能干什么,不能干什么

2.1 能立刻回答的问题

拿过一台出问题的机器,iostat 能帮我在十秒内回答这几件事:

  • 磁盘的读写吞吐量(每秒传输多少 KB/MB)是否已经逼近硬件上限。
  • I/O 请求的平均响应时间(await)是否高得离谱,比如正常零点几毫秒,结果变成几十毫秒。
  • 磁盘设备的利用率(%util)是否长期处于 100% 附近,也就是“打满了”。
  • 多个磁盘或分区之间负载是否均衡,是不是有人把数据全写到了同一块盘上。

这些信息拿到手,基本就能判断“是磁盘本身不行,还是应用程序把磁盘搞垮了”。

2.2 不能回答的问题

但 iostat 也有明显的盲区。它只能告诉你“磁盘很忙”,但没法告诉你“到底是谁在写”。这时候需要配合pidstat或iotop去看具体进程。另外,它提供的是聚合后的平均值,突发性的瞬时抖动容易被“平均”掉。比如某块盘 1 秒内的峰值延迟是 100ms,但 iostat 默认 2 秒间隔取均值,出来后可能是 5ms,掩盖了真相。

所以,我对 iostat 的定位很明确:它是一个“第一现场勘察工具”,先把问题定性,再结合其他工具做定量和定位。

3. 核心指标逐个拆解:这些数字到底代表什么

iostat 输出内容分两部分:CPU 使用情况和设备使用情况。CPU 部分大家比较熟,重点看设备部分的几个字段。我第一次用的时候被svctm这个名字误导了很久,后面详细说。

3.1 设备报告核心字段

以iostat -x 1 3为例,你会看到类似这样的输出:

Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util vda 0.00 0.00 0.00 1.00 0.00 4.00 8.00 0.00 0.50 0.00 0.50 0.50 0.05

逐字段拆解:

  • r/s 和 w/s:每秒完成的读请求数和写请求数。注意这里的单位是“次数”,不是数据量。IOPS(每秒读写次数)主要就是看这两个值。
  • rkB/s 和 wkB/s:每秒读写的数据量,单位是 KB。吞吐量的核心指标。
  • avgrq-sz:平均每次 I/O 请求的数据大小。如果这个值很小(比如 8KB),说明应用在做大量的小块随机读写;如果很大(比如 1MB),说明在做顺序大块读写。这个字段能帮你判断应用访问模式。
  • avgqu-sz:平均 I/O 队列长度。队列越长,说明请求在排队等待,磁盘处理不过来。
  • await:平均每次 I/O 请求从发出到完成的总耗时,包括在队列里等待的时间。这个值是判断“用户感知延迟”的关键指标。再细分还有r_await和w_await,分别看读和写的延迟。
  • svctm:平均每次 I/O 请求在硬件层面实际处理的时间。这个字段在新版 Linux 里已经基本没有参考意义,因为很多存储设备(比如 SSD、云盘、分布式存储)的处理时间内核根本无法准确统计。

注意:svctm在很多资料里还被拿来分析,但内核维护者早就说过,这个值在现代设备上不可靠。我一般直接忽略它,重点看await和%util。

  • %util:设备繁忙程度百分比。可以粗略理解为“设备有百分之多少的时间在干活”。很多人以为 100% 就是满负荷,其实不完全准确。这个后面专门讲。

3.2 CPU 部分的 %iowait 怎么看

iostat 输出的第一段是 CPU 状态,其中%iowait表示 CPU 等待 I/O 完成的时间占比。这个值如果很高(比如超过 30%),说明 CPU 大多数时间在干等磁盘返回数据,应用层会感受到明显的卡顿。

但要注意,%iowait高不代表磁盘就是坏的。它只说明 CPU 在等待 I/O,磁盘可能正常工作,只是负载重。也可能是因为进程数太少,CPU 没别的活干只好等着。我见过不少情况是:%iowait高达 40%,但实际上磁盘根本没有满负荷,纯粹是业务进程在同步等待锁或者网络资源,误把等待时间算进了 I/O。

4. 常用参数组合与场景化使用

iostat 的参数不多,但组合起来有不同的用途。我把我平时最常用的几套组合整理成了一个速查表,方便大家直接抄作业。

4.1 参数速查表

命令组合用途说明适用场景
iostat -x 1每秒刷新一次,显示详细扩展指标实时观察磁盘负载变化
iostat -x 1 5每秒一次,共采集 5 次抓取一段时间内的平均表现
iostat -d -k 1只看设备报告,以 KB 为单位快速确认哪块盘吞吐异常
iostat -p /dev/vda 1只看指定分区或磁盘多盘机器定位问题盘
iostat -m 1以 MB 为单位显示吞吐大吞吐场景下避免数字太长
iostat -x -t 1附带时间戳输出需要配合日志定位时间线

我的习惯是:先跑iostat -x 1 3,拿到 3 组数据,观察%util和await的走势;如果发现某块盘异常,再跑iostat -p /dev/sdb -x 1单盘盯一会儿。注意采样次数不要太多,3 次足够了,因为 iostat 只输出从启动以来的平均值和间隔内的增量值,次数再多也是重复。

4.2 动态刷新间隔的讲究

很多人用iostat 1就一直挂在那里刷屏,其实没必要。默认情况下 iostat 第一行输出的是“系统启动至今的平均值”,后面才是每个间隔内的数据。如果你只看第一行去判断问题,会被启动以来长时间的平均值严重误导。举个例子,一台机器跑了 30 天,平均%util是 20%,但最近 1 分钟磁盘已经打满 100%。你第一眼看到的是 20%,就会误判“磁盘还好啊”。

所以正确姿势是:至少看第二行之后的采样数据,或者用iostat -x 1 3这种明确采样次数的方式,把第一行平均值忽略掉。

5. 实操案例:一次典型的“磁盘打满”排查

我之前处理过一个问题,某公司的订单服务突然延迟飙高,数据库的慢查询日志里全是简单查询超时。我上去之后,没有先看慢查询,而是直接跑iostat -x 1 5。

5.1 初步定位

输出的关键信息大致是:

Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgqu-sz await r_await w_await %util nvme0n1 0.00 2048.00 0.00 2048.00 0.00 8192.00 12.00 11.50 0.00 11.50 100.00

分析一下:

  • w/s高达 2048,也就是每秒 2048 次写请求,这已经是一个非常高的 IOPS 值。
  • avgqu-sz是 12,意味着平均有 12 个请求在排队等待处理。
  • await是 11.5ms,对于 NVMe 盘来说偏高太多。正常 NVMe 盘的延迟应该在零点几毫秒到几毫秒之间。
  • %util是 100%,磁盘已经满负荷。

到这基本可以断定:存储层扛不住了,但“谁在写”还不知道。

5.2 定位写源头

接着用pidstat -d 1 5查看每个进程的 I/O 情况,很快就发现是一个日志收集进程在疯狂写文件,并且日志轮转配置失效,导致单个日志文件无限增长。顺序写变成了大量的小块追加写,再加上磁盘本身是共享存储,其他服务也受影响,最终整个存储池的队列被拉满。

5.3 解决的思路

直接把日志进程的写入方式改成异步批量写,同时修复日志轮转,把单文件大小限制住。改完再跑iostat -x 1 5,w/s从 2048 降到了 200 左右,await回落到 1ms 以内。

这里我想强调一点:排查 I/O 性能问题,不能只看 iostat,它是第一棒,但绝不是最后一棒。拿到“磁盘满”这个结论之后,一定要配合进程级工具找到真正的元凶,否则你可能会做出错误的扩容决策——比如以为磁盘不够用,直接加盘,结果问题根本不在容量上。

6. 三个最常见的误判:%util、await 和 io 瓶颈的真相

6.1 %util 100% 就是磁盘饱和?不一定

%util 的含义是“设备繁忙时间占总时间的百分比”,它的计算方式是基于设备是否“有未完成的请求”来判断的。问题在于:现代磁盘大多支持多队列并行处理,即使设备一直有请求在处理(%util=100%),也可能远没到硬件极限。比如 SSD 内部有多个并行通道,一块盘同时处理几十个请求都是常态,这时候 %util 一直显示 100%,但实际吞吐和延迟都表现正常。

反之,有些场景 %util 不高,但延迟已经暴涨。分布式存储或者云盘就是典型——网络抖动、存储节点热点会导致请求排队,但本地设备看起来并不忙。

所以我的经验是:%util 高要警惕,但不要只凭它下结论。还要结合 IOPS、吞吐量、延迟三个维度综合判断。真正衡量磁盘是否饱和,应该看“当前 IOPS/吞吐是否接近该设备的标称上限”。

6.2 await 高就是磁盘慢?小心被平均数骗了

await 是“从发出请求到完成”的平均时间,包含排队时间和处理时间。如果你的应用的 I/O 模式是随机小块读写,单次请求本身需要寻址,延迟天然比顺序读写高。这并不代表磁盘坏了,只是访问模式导致的。

更隐蔽的一个坑是:平均值掩盖了长尾延迟。有时候磁盘 99% 的请求都在 1ms 内完成,但剩下的 1% 请求要 100ms,平均值算出来是 2ms 左右,表面看很健康,可业务却出现了偶发超时。这种情况我建议配合iostat -x里的r_await和w_await分开看,或者用延迟分布工具去抓长尾请求,只看平均值会误事。

6.3 I/O 瓶颈不如 CPU 瓶颈好判断

CPU 瓶颈很容易判断:某个核的%user或者%sys飙到 100%,就是 CPU 不够了。但 I/O 瓶颈牵扯到设备类型、队列深度、总线带宽、远端存储网络等多个环节,光靠一个工具讲不清楚。我在实践中总结了一套判断流程,大家可以参考:

  1. 先看iostat -x,确认是读还是写,是 IOPS 高还是吞吐高。
  2. 对照设备规格确认是否逼近上限。
  3. 用pidstat -d或iotop定位具体进程。
  4. 用strace或者应用日志确认 I/O 请求的大小和频率。
  5. 结合业务高峰和监控曲线找规律。

这套流程走完,I/O 问题一般都能定位到八九不离十。

7. 容易被忽略的细节:采样时间、设备命名和输出格式

7.1 采样间隔不是随便定的

iostat 默认输出是自系统启动以来的平均值,所以如果你直接在机器上敲iostat不加参数,看到的数据参考价值有限。正确的做法是带间隔和次数,比如iostat -x 1 3。间隔的选择也有讲究:间隔太短(比如 0.1 秒)会占用额外资源,在本身就繁忙的机器上造成干扰;间隔太长(比如 60 秒)又可能把短时峰值抹平。生产环境我一般用 1 秒或 2 秒间隔,采 3 到 5 次就够看了。

7.2 设备名不同,含义不同

现在的服务器设备命名越来越复杂:/dev/sda一般是本地 SCSI/SATA 盘,/dev/nvme0n1是 NVMe SSD,/dev/vda是虚拟化环境下的虚拟磁盘,/dev/mapper/xxx是 LVM 逻辑卷。如果某台机器有多块盘,iostat 默认会全部列出来。我见过有人盯着/dev/sda猛看,却发现真正的数据库数据在/dev/sdb上,白忙活半天。

用iostat -p参数指定设备名,或者先跑lsblk确认设备和挂载点的对应关系,再针对性监控。另外,在云环境里,一块云盘背后的实际硬件可能有多块物理盘支撑,iostat 看到的只是你这一朵“云盘视图”,不代表物理硬件状态,这一点要心里有数。

7.3 第一行数据是“累计平均”

这个前面提过,但还是值得单独强调:iostat 输出的第一行是自启动以来的累计平均,之后才是按间隔采样的数据。判断当前瓶颈时,请忽略第一行,直接看后续采样值。尤其是机器跑了很久、历史负载又不高的时候,第一行的平均值会把当前峰值严重稀释。

8. iostat 的局限性与搭档工具

没有任何工具是万能的,iostat 也不例外。它在“系统级”的 I/O 监控上很强大,但深入一步就会力不从心。这里整理一下我的常用搭档组合。

8.1 进程级定位:pidstat 和 iotop

iostat 告诉你系统整体 I/O 情况,但如果想知道具体哪个进程在产生 I/O,就要用pidstat -d或者iotop。前者按进程显示读写速率,适合脚本化采集;后者类似 top 的交互界面,适合人工观察。对比起来,我写脚本排查时偏好 pidstat,手动登录机器时偏好 iotop。

8.2 延迟细节追踪:iostat 看不到的长尾

如果需要更精确的延迟分布,iostat给不了。/proc文件系统里的io信息可以辅助,但更直接的是用blktrace、fio这类专业工具。fio可以主动压测磁盘性能,blktrace可以追踪 I/O 请求在内核里的整个旅程。不过这两个工具学习成本稍高,一般只有深挖问题时才用得着。

8.3 配合监控平台长期采集

日常巡检我建议写一个脚本,定期把iostat -x的采样结果落到监控平台,保留历史数据。因为很多 I/O 问题是慢慢劣化的,比如磁盘坏道逐渐增多、SSD 写入放大越来越严重,这些都是量变到质变的过程。没有历史数据,你很难说清楚“这个延迟是不是最近才变高的”。

9. 一个小技巧:用 iostat 做磁盘选型和容量规划

除了排障,iostat 还能帮你做一件很有价值的事——用真实业务负载来评估磁盘选型。比如你计划给新服务配存储,不确定该用高性能 SSD 还是普通 HDD,与其听厂商宣传,不如在当前机器上模拟业务负载跑一遍。

方法是:先在测试环境用fio模拟业务的 I/O 模式(随机读写还是顺序读写,块大小多少,队列深度多少),再用iostat -x采集实际表现。比如业务是典型的 4KB 随机读,队列深度 32,那你就用 fio 按这个参数压测,然后对比 iostat 里的await和%util,看这台磁盘能否承受预期的 IOPS。

我个人经验:不要盲目看标称 IOPS,厂商给的数字通常是最优条件下的极限值,真实环境能达到标称的 30% 到 50% 就算不错。iostat 配合 fio 的数据,比厂商宣传页可靠得多。

10. 后续还能怎么扩展

iostat 只是 Linux 性能分析工具箱里的一个成员。如果你发现磁盘没问题,但服务还是慢,那就要去排查网络(nicstat)、内存(vmstat)、CPU 上下文切换(pidstat -w)了。性能问题的排查思路是“先全局后局部,先系统后进程”,iostat 承担的是全局第一棒,跑好这一棒,后续方向才不会偏。

我在实际使用中最深的一个体会是:工具再熟悉,也不如对业务场景的理解重要。同样的await=20ms,在 OLTP 数据库上可能是灾难,在批量分析任务里可能完全能接受。所以拿到 iostat 数据之后,一定要回到业务视角去判断“这个值对我来说到底算不算高”。

相关新闻

从空闲检测到外设断电:Windows自动锁屏节能方案

从空闲检测到外设断电:Windows自动锁屏节能方案

我的电脑最近半年一直在跑一个看起来很土的需求:连续30分钟没有任何操作,就自动锁定屏幕,同时把显示器、音箱这些外设的电源一起断掉,实现节能。这个需求听起来就是电源选项里改几个下拉框的事,真正做下来才发现&#…

2026/10/20 12:41:30 阅读更多 →
从判别到生成:读懂自回归、VAE、GAN与扩散模型的路线与选型

从判别到生成:读懂自回归、VAE、GAN与扩散模型的路线与选型

第13章的标题虽然是“深度生成模型”,但它真正想回答的问题只有一个:机器到底能不能“创造”?我第一次把这章内容放进整套学习资料时,团队里有人质疑:前面12章都在教模型怎么识别、怎么预测、怎么做序列翻译&#xff0…

2026/10/20 12:41:30 阅读更多 →
Embabel状态机:把Agent决策权从LLM拿回来

Embabel状态机:把Agent决策权从LLM拿回来

1. 从一次失控的Agent任务说起去年冬天,我接手了一个内部知识库问答系统的改造项目。需求听起来很朴素:让Agent根据用户提问,自动决定是查文档、调接口,还是直接回答。我一开始用的是纯LLM驱动的方案,把工具列表塞进系…

2026/10/20 12:41:30 阅读更多 →

最新新闻

基于拍卖机制的多智能体动态任务分配与Matlab仿真实践

基于拍卖机制的多智能体动态任务分配与Matlab仿真实践

做多智能体任务分配的时候,我最担心的不是算法收敛不收敛,而是现场环境一变,整个分配方案就得推翻重来。后来我在多个机器人协同仿真项目里尝试了基于拍卖机制的分散式任务分配,发现这套思路在动态环境下格外稳,而且用…

2026/10/20 20:06:20 阅读更多 →
交换机命令实战:VLAN划分、Trunk与链路聚合在局域网组网中的应用

交换机命令实战:VLAN划分、Trunk与链路聚合在局域网组网中的应用

1. 项目概述与组网前的准备1.1 这个项目到底在解决什么问题先说结论:这篇不是教你背命令的字典式教程,而是把交换机常用命令放回真实的局域网组网场景里,告诉你“什么时候该敲什么、敲完怎么验证、出了问题怎么排查”。局域网组网这件事&…

2026/10/20 20:06:20 阅读更多 →
Kafka消息重试机制详解:从参数配置到死信队列的完整设计指南

Kafka消息重试机制详解:从参数配置到死信队列的完整设计指南

凌晨两点被电话叫醒,这种事情做过实时数据的人应该都懂。那次是某个核心业务的消费组 lag 从几百一路飙到几十万,下游接口超时,消费线程全部卡在同一批消息的重试上,新消息进不来,旧消息处理不完,告警群瞬间…

2026/10/20 20:06:20 阅读更多 →
Vue+SpringBoot社区医疗系统毕设实战:从跑通环境到二次开发避坑指南

Vue+SpringBoot社区医疗系统毕设实战:从跑通环境到二次开发避坑指南

简介:这套基于Vue与SpringBoot的社区医疗服务系统设计资源,面向计算机相关专业毕业生、课程设计及期末大作业人群,可作全栈项目参考或二次开发基础。系统围绕社区居民医疗健康需求,涵盖预约挂号、健康档案、线上问诊、药品配送等典…

2026/10/20 20:06:20 阅读更多 →
CNN水果识别毕设全流程:从数据构建到模型评估与答辩

CNN水果识别毕设全流程:从数据构建到模型评估与答辩

1. 毕设选题之前,先想清楚"水果识别"到底在考察什么每年到了大三暑假,总有一批同学开始焦虑毕设题目的选择。我的建议一贯是:不要一开始就盯着"我要做一个多炫的系统",而是想清楚"这个题目能让我把哪些核…

2026/10/20 20:06:20 阅读更多 →
IIEM海面散射系数MATLAB实现:二重积分与数值陷阱解析

IIEM海面散射系数MATLAB实现:二重积分与数值陷阱解析

简介:提供一套开源的改进积分方程模型(IEM)Python实现,面向粗糙表面电磁散射研究,适用于遥感、雷达成像等需要二重积分数值计算的场景,为已有一定编程基础的科研与工程人员提供可直接运行的基础代码。整套资…

2026/10/20 20:05:13 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

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

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

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

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

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

2026/10/20 1:01:30 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/20 10:02:13 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/19 17:32:41 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

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

2026/10/20 10:02:08 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

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

2026/10/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

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

2026/10/20 9:50:45 阅读更多 →