Zabbix 4.0 数据收集全链路解析:从 Agent 配置到大规模优化实践
1. 从零理解 Zabbix 数据收集的核心链路1.1 数据收集到底在收什么很多人刚接触 Zabbix 的时候脑子里就一个画面装个 Agent加个主机然后图表就出来了。但真到了生产环境你会发现数据收集这条链路远比想象中复杂。Zabbix 4.0 的数据收集本质上是在解决三个问题谁来采、怎么传、存到哪。“谁来采”指的是采集端可以是 Zabbix Agent、SNMP 接口、JMX 网关、IPMI 接口也可以是自定义脚本通过 trapper 模式主动推。“怎么传”涉及主动模式和被动模式的选型以及 Proxy 的部署位置。“存到哪”则是 Server 端收到数据后写入数据库的整个过程包括预处理、触发器计算、历史数据落盘。这三个环节里任何一个出问题你看到的都是“数据断了”或者“图表有空洞”。但排查方向完全不同。我见过太多人一看到没数据就重启 Agent结果折腾半天发现是 Server 的 poller 进程不够用。所以这一章的核心不是教你怎么点按钮而是帮你建立一条完整的排查链路。1.2 主动模式与被动模式的本质区别Zabbix Agent 有两种工作模式这个知识点看起来简单但实际选型的时候很多人是懵的。被动模式是 Server 主动来找 Agent 要数据。Server 端的 poller 进程发起连接Agent 响应并返回监控项的值。这种模式的问题在于当监控主机数量上去之后Server 端的 poller 进程会成为瓶颈。默认 Zabbix 4.0 的StartPollers是 5意味着同时只能处理 5 个被动检查请求。你有 500 台主机每台 100 个监控项那就是 50000 个请求要排队。主动模式反过来Agent 自己去找 Server 要任务列表然后主动把数据推上去。Server 端由 trapper 进程接收数据。这种模式下Agent 的采集压力分散到了每台被监控主机上Server 端只需要处理接收和入库。大规模环境下主动模式几乎是必选项。我个人的经验是主机数量超过 200 台就应该认真考虑全面转向主动模式。低于这个数被动模式的管理成本更低因为不需要在 Agent 配置里维护 Server 地址和 Hostname 的对应关系。1.3 数据收集的完整生命周期一条监控数据从产生到最终展示要经过这些环节Agent 根据ServerActive或Server配置发起采集采集结果通过 Zabbix 协议传输到 Server 或 ProxyServer 的 preprocessing 环节对原始值做处理单位转换、正则提取、JSON 解析等值被写入 history 表或 trends 表触发器表达式基于新值重新计算前端从数据库读取并渲染图表这个链路里第 3 步的预处理是 Zabbix 4.0 的一个亮点功能也是很多人忽略的。比如你采集到一个 JSON 字符串{cpu: 45.2, mem: 78.1}可以在预处理阶段直接用 JSONPath 提取出$.cpu而不需要写额外的脚本。这个能力在采集云平台 API 数据时特别有用。2. Zabbix Agent 关键配置项深度拆解2.1 Server 与 ServerActive 的配置逻辑这两个参数是 Agent 配置里最容易搞混的。# 被动模式允许哪些 Server 来连接我 Server192.168.1.100 # 主动模式我主动把数据推给谁 ServerActive192.168.1.100 # 主动模式下必须配置且必须与前端主机名完全一致 Hostnameweb-server-01Server参数控制的是白名单只有列表里的 IP 才能连接 Agent 的 10050 端口。ServerActive控制的是 Agent 主动上报的目标地址。如果你只用被动模式ServerActive可以不配如果只用主动模式Server可以不配但建议保留作为调试用途。Hostname这个参数是主动模式的命门。Agent 上报数据时会带上这个 HostnameServer 端拿它去匹配前端配置的主机名。大小写敏感连空格都不能差。我踩过的坑是前端主机名写的是Web-Server-01Agent 配置写的是web-server-01结果数据全部被 Server 丢弃日志里只看到no host found之类的提示排查了半小时才发现是大小写问题。2.2 超时与并发参数调优# 被动模式下Agent 等待 Server 请求的超时时间 Timeout3 # 主动模式下Agent 从 Server 获取任务列表的间隔 RefreshActiveChecks120 # 主动模式下缓冲区大小Zabbix 4.0 新增 BufferSize100 # 主动模式下缓冲区发送间隔 BufferSend5Timeout默认是 3 秒这个值在采集一些响应慢的脚本时经常不够。比如你写了个自定义脚本去查数据库查询本身要 5 秒那 Agent 会在 3 秒时超时并返回错误。这时候需要把Timeout调大但注意Server 端的 Timeout 也要同步调整否则 Server 端先超时了Agent 返回再快也没用。BufferSize和BufferSend是 Zabbix 4.0 引入的主动模式缓冲机制。Agent 采集到的数据先放在内存缓冲区攒够BufferSize条或者到了BufferSend秒就批量发送。这个设计减少了网络交互次数但代价是数据延迟增加。如果你对实时性要求高把BufferSend调小到 1 秒如果网络带宽紧张把BufferSize调大。2.3 自定义监控项的用户参数配置UserParameter是 Agent 端最灵活的扩展方式# 语法UserParameterkey,command UserParametermysql.status[*],/etc/zabbix/scripts/check_mysql.sh $1 UserParameternginx.active,/etc/zabbix/scripts/check_nginx.sh active这里有几个实操要点脚本必须有可执行权限且属主是 zabbix 用户或者 root脚本的输出必须是单个值多行输出会被截断脚本的退出码必须是 0非 0 会被标记为 unsupported带参数的 key 用[*]通配调用时传参我见过一个典型问题脚本手动执行没问题但 Zabbix 采集就是 unsupported。原因是脚本里用了$HOME或者$PATH这类环境变量而 Zabbix Agent 运行时的环境变量和登录 shell 完全不同。解决办法是在脚本开头显式设置PATH或者用绝对路径调用所有命令。3. 分布式架构下的 Proxy 部署与数据流转3.1 什么场景下必须上 ProxyProxy 的核心价值是就近采集、断网缓存、减轻 Server 压力。以下场景建议部署 Proxy场景是否建议 Proxy原因跨机房监控必须避免跨专线采集减少延迟和带宽消耗主机数超过 500建议分散 Server 的 poller 压力网络不稳定必须Proxy 本地缓存数据恢复后补传单一局域网小规模不需要增加运维复杂度收益不明显Zabbix 4.0 的 Proxy 支持主动和被动两种模式。主动模式下Proxy 自己向 Server 请求配置并上报数据适合 Proxy 在 NAT 后面的场景。被动模式下Server 主动连接 Proxy 获取数据适合 Server 能直接访问 Proxy 的场景。3.2 Proxy 配置的关键参数# Proxy 模式0 主动1 被动 ProxyMode0 # Server 地址 Server192.168.1.100 # Proxy 名称必须与前端配置完全一致 Hostnameproxy-beijing-01 # 本地缓存大小断网时能存多少数据 ProxyLocalBuffer720 # 离线数据保留时间小时 ProxyOfflineBuffer24 # 配置同步间隔 ConfigFrequency60 # 数据发送间隔 DataSenderFrequency5ProxyOfflineBuffer这个参数很关键。假设你的 Proxy 和 Server 之间网络断了Proxy 会把数据存在本地 SQLite 数据库里。ProxyOfflineBuffer24表示保留 24 小时的数据。如果断网超过 24 小时最早的数据会被丢弃。所以这个值要根据你的网络恢复能力来设一般建议至少 24 小时网络差的场景设 72 小时。ConfigFrequency控制 Proxy 多久从 Server 同步一次配置。默认 60 秒意味着你在前端加了个新监控项最多 60 秒后 Proxy 才会开始采集。调试阶段可以临时调小到 10 秒生产环境保持 60 秒即可。3.3 数据从 Proxy 到 Server 的完整路径主动模式下数据流转是这样的Proxy 的 poller 进程采集数据写入本地数据库Proxy 的 data sender 进程从本地数据库读取数据通过 Zabbix 协议发送到 Server 的 trapper 端口10051Server 的 trapper 进程接收并写入 Server 数据库Server 的 preprocessing 环节处理数据触发器和前端读取这个链路里Proxy 的 data sender 进程数StartDataSenders和Server 的 trapper 进程数StartTrappers是两个容易成为瓶颈的参数。默认值分别是 5 和 5。如果你有 10 个 Proxy 同时上报每个 Proxy 5 个 sender那就是 50 个并发连接打到 Server 的 5 个 trapper 上排队是必然的。建议 Server 端StartTrappers至少设置为 Proxy 数量乘以 2。4. 数据预处理与入库机制4.1 预处理功能的实战应用Zabbix 4.0 的预处理功能支持多种处理方式我挑几个最常用的讲。正则提取从原始文本中提取需要的部分。原始值CPU usage: 45.2% 预处理正则表达式 ([0-9.])% 结果45.2JSONPath从 JSON 响应中提取字段。原始值{status:ok,data:{cpu:45.2,mem:78.1}} 预处理JSONPath $.data.cpu 结果45.2自定义倍数单位换算。原始值1024 预处理自定义倍数 0.001 结果1.024丢弃未变化的值这个功能在采集一些变化不频繁的指标时特别有用。比如你采集服务器的内核版本这个值几乎不变每次都存一条历史记录纯属浪费。开启“丢弃未变化的值”后只有值发生变化时才写入数据库。4.2 历史数据与趋势数据的存储策略Zabbix 把数据分成两类存储history 表存储原始采集值保留时间由监控项的“历史数据保留时长”控制trends 表存储每小时的最小值、最大值、平均值保留时间由“趋势数据保留时长”控制这个设计的意义在于你不需要保留一年的原始数据来画一年的趋势图。trends 表每小时只有一条记录一年也就 8760 条查询速度极快。而 history 表如果每秒一条一年就是 3153 万条查询会非常慢。我的建议是监控项类型历史保留趋势保留CPU/内存/磁盘7 天365 天网络流量3 天180 天进程状态30 天365 天日志类1 天不保留历史数据保留时间不要设太长否则数据库会膨胀得很快。需要长期看的趋势靠 trends 表就够了。4.3 数据库写入性能优化Zabbix Server 写入数据库的性能直接影响数据收集的实时性。几个关键优化点使用分区表MySQL 下对 history 和 trends 表按时间分区可以大幅提升查询和删除效率。Zabbix 官方提供了分区脚本建议在数据量超过 1000 万条时启用。调整 InnoDB 参数innodb_buffer_pool_size 4G innodb_log_file_size 1G innodb_flush_log_at_trx_commit 2 innodb_file_per_table 1innodb_flush_log_at_trx_commit2表示每秒刷一次日志而不是每次事务都刷。这会把数据安全性从“绝不丢失”降到“最多丢 1 秒”但写入性能提升明显。监控数据丢 1 秒通常是可以接受的。关闭不必要的 Housekeeper 任务Housekeeper 负责清理过期数据但它会消耗大量数据库资源。如果用了分区表可以直接关闭 Housekeeper通过删除分区来清理数据效率高得多。5. 常见故障排查与避坑指南5.1 数据收集中断的排查思路数据断了按这个顺序查Agent 是否存活systemctl status zabbix-agent或ps -ef | grep zabbix_agentd网络是否通telnet server-ip 10050被动或telnet server-ip 10051主动Agent 日志有没有报错tail -f /var/log/zabbix/zabbix_agentd.logServer 端有没有收到数据查看 Server 日志/var/log/zabbix/zabbix_server.log前端监控项是否 unsupported在“最新数据”里看监控项状态数据库是否正常mysql -e show processlist看有没有慢查询堆积这个顺序是从近到远、从简单到复杂。大部分问题在前三步就能定位。5.2 常见问题速查表现象可能原因解决方法监控项 unsupported脚本权限不对chmod xchown zabbix数据有延迟BufferSend 太大调小 BufferSend数据有空洞Timeout 太小调大 Agent 和 Server 的 Timeout主动模式无数据Hostname 不匹配检查前端主机名和 Agent 配置Proxy 数据不更新ConfigFrequency 太大临时调小检查 Proxy 日志数据库写入慢Housekeeper 占用资源启用分区表关闭 Housekeeper触发器不触发预处理改变了值检查预处理步骤是否正确图表显示断点历史数据被清理调整历史数据保留时长5.3 几个我踩过的坑坑一Agent 的 Hostname 和前端不一致。这个前面提过但值得再强调。主动模式下Agent 上报数据时带的是Hostname参数的值Server 拿这个值去匹配前端主机。如果前端主机名是web01Agent 配置是web01.example.com数据就会被丢弃。解决办法是统一用短名或者统一用 FQDN别混着来。坑二自定义脚本的输出带了换行符。Zabbix 期望的是单行输出如果你的脚本echo了多行Agent 会把第一行当作值后面的忽略。更隐蔽的是有些脚本输出末尾带了\r\n在 Linux 下看起来正常但 Zabbix 解析时可能出问题。建议脚本最后用printf而不是echo或者用tr -d \n处理一下。坑三Proxy 的本地缓存满了。ProxyLocalBuffer默认是 720 小时听起来很大但如果你的 Proxy 采集了几千个监控项本地 SQLite 数据库会迅速膨胀。一旦磁盘满了Proxy 会停止工作。建议给 Proxy 的/var/lib/zabbix目录单独挂盘并设置监控告警。坑四Server 的 StartPollers 不够用。被动模式下所有采集请求都要排队等 poller 进程。默认 5 个 poller每个 poller 处理一个请求平均 0.1 秒那每秒最多处理 50 个请求。你有 1000 个监控项轮询周期 60 秒那每秒需要处理约 17 个请求看起来够用。但如果某个监控项超时了 3 秒这个 poller 就被占用了 3 秒吞吐量直接掉下来。所以StartPollers要根据实际情况调我一般设成 CPU 核数的 2 到 4 倍。6. 大规模环境下的数据收集优化实践6.1 监控项采集频率的合理规划不是所有监控项都需要 30 秒采集一次。合理的频率规划能大幅降低系统负载监控项类型建议频率理由CPU/内存/磁盘60 秒变化快但 60 秒足够反映趋势网络流量60 秒同上进程状态120 秒进程不会秒级变化磁盘空间300 秒变化慢5 分钟足够配置文件校验3600 秒几乎不变证书过期检查86400 秒每天一次足够把采集频率从 30 秒改成 60 秒数据量直接减半而你对系统状态的感知几乎没有损失。这个优化在大规模环境下效果非常明显。6.2 使用批量采集减少连接开销Zabbix Agent 支持在一个连接里返回多个监控项的值这个特性叫“批量采集”。被动模式下Server 的 poller 会尽量把同一台主机的多个请求合并成一个连接。主动模式下Agent 一次获取一批任务采集完后批量上报。要充分利用这个特性建议同一台主机的监控项尽量用同一个 Agent 采集不要一部分用 Agent 一部分用 SNMP主动模式下RefreshActiveChecks不要设得太小否则 Agent 频繁向 Server 请求任务列表使用UserParameter时尽量把相关指标写在一个脚本里一次采集多个值6.3 数据收集的容量规划最后说一下容量规划。假设你有 500 台主机每台 100 个监控项采集频率 60 秒每秒数据量500 × 100 ÷ 60 ≈ 833 条/秒每天数据量833 × 86400 ≈ 7200 万条历史保留 7 天约 5 亿条记录每条记录按 100 字节算约 50 GB这还只是 history 表加上 trends 表、索引、触发器数据数据库总大小轻松超过 100 GB。所以在大规模环境下数据库的 IOPS 和磁盘空间必须提前规划。我一般建议用 SSD并且 history 表和 trends 表分开存储在不同的磁盘上。如果数据量再大一个数量级就要考虑用 TimescaleDB 或者 ClickHouse 作为后端存储。Zabbix 4.0 原生支持这两种后端配置起来也不复杂但那是另一个话题了。提示在做容量规划时一定要把预处理和触发器计算的开销算进去。一个复杂的触发器表达式可能比采集本身还耗资源。

相关新闻

算法概览图:用拓扑建模实现原理级动画可视化

算法概览图:用拓扑建模实现原理级动画可视化

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

2026/10/14 1:08:04 阅读更多 →
基于ESP8285与MQTT的远程电机控制器开发全攻略

基于ESP8285与MQTT的远程电机控制器开发全攻略

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

2026/10/14 1:08:04 阅读更多 →
工业控制系统国产化落地指南:全栈平台迁移路径与工程避坑实践

工业控制系统国产化落地指南:全栈平台迁移路径与工程避坑实践

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

2026/10/14 1:08:04 阅读更多 →

最新新闻

软件开发项目履约保证金的风险与应对策略

软件开发项目履约保证金的风险与应对策略

1. 项目背景与行业现状在软件开发行业,履约保证金已经成为许多项目招标过程中的标配要求。最近接洽的一个企业级ERP系统开发项目,甲方明确要求参与竞标的开发团队需缴纳合同金额20%的履约保证金。这让我不得不重新审视这个行业普遍存在却鲜少被深入讨论的…

2026/10/14 1:51:15 阅读更多 →
Ponytail技术解析:从3D角色头发物理模拟到工程实践

Ponytail技术解析:从3D角色头发物理模拟到工程实践

我无法根据当前输入生成符合要求的博文。原因如下:项目标题“ponytail”本身是一个常见英文单词,意为“马尾辫”,属于日常服饰/造型/美妆/时尚领域术语;但项目正文为空、关键词为空、摘要描述为空;所谓“相关热搜词”与…

2026/10/14 1:51:15 阅读更多 →
Elasticsearch演讲PPT全攻略:内容架构、页面拆解与现场避坑

Elasticsearch演讲PPT全攻略:内容架构、页面拆解与现场避坑

简介:Elasticsearch演讲PPT是一份面向ES初学者、后端开发者和技术分享讲师的演示文稿,旨在用一节课时间讲清Elasticsearch的核心概念、数据模型与高性能原理,帮助读者快速建立对分布式全文检索与分析引擎的整体认知。PPT共1个pptx文件&#x…

2026/10/14 1:51:15 阅读更多 →
张北鲜羊奶采购指南:渠道、鉴别与巴氏杀菌储存全攻略

张北鲜羊奶采购指南:渠道、鉴别与巴氏杀菌储存全攻略

1. 写在前面:为啥一定要跑张北买羊奶头一回开车去张北收羊奶,是跟着一个跑生鲜配送的老哥搭伴走的。凌晨四点多从张家口市区出发,五点半到草原边上,天色刚泛白,牧民家的棚圈里已经有动静了。那趟跑下来我才算真正明白&…

2026/10/14 1:51:15 阅读更多 →
总线协议为何如此多?从I²C、SPI到PCIe的分层架构解析

总线协议为何如此多?从I²C、SPI到PCIe的分层架构解析

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

2026/10/14 1:51:15 阅读更多 →
Web课程设计报告:可运行、可复现、可答辩的前端工程交付指南

Web课程设计报告:可运行、可复现、可答辩的前端工程交付指南

简介:本资源是一份完整的Web开发期末课程设计实践报告与配套源码,面向计算机相关专业本科生及Web初学者,解决课程设计从选题、技术选型到系统实现的全流程学习需求。压缩包共181个文件,涵盖前端(22个CSS、18个JS、12个…

2026/10/14 1:50:15 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/13 16:22:04 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 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/13 22:01:27 阅读更多 →
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/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练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/12 18:11:17 阅读更多 →