maintenance_work_mem设置过大导致数据库启动崩溃与调优实战
1. 问题现象与原因初判1.1 故障现场数据库服务彻底起不来的样子先描述一下我遇到的实际场景。某单位的数据库服务器上运行着 hgdb-se4.3.2某天业务人员反馈应用连不上数据库查看数据库进程时发现程序根本不在运行。尝试通过常规命令启动服务结果是启动动作一闪而过服务状态又回到失败。此时去翻数据库运行日志可以看到类似下面的记录FATAL: could not reattach to shared memory DETAIL: memory not available HINT: Please check the memory limit setting. LOG: startup process (PID 12345) was terminated by signal 11: Segmentation fault LOG: aborting startup due to startup process failure如果只看后半段很多人第一反应是数据文件损坏甚至想去做恢复目录、重建数据文件之类的危险操作。但日志前面已经写得很清楚问题出在共享内存申请失败。顺着这个线索查下去才定位到是 maintenance_work_mem 参数被设置得太大导致数据库在启动恢复阶段直接申请内存超标整个进程被系统杀掉。这种现象看起来像“服务不能启动”本质上却不是数据库软件坏了也不是数据文件损坏而是内存申请被操作系统拒绝。1.2 maintenance_work_mem 到底在数据库里管什么maintenance_work_mem 这个参数在 PostgreSQL 系的数据库里控制的是维护性操作所能使用的最大内存。维护性操作包括但不限于 VACUUM、CREATE INDEX、ALTER TABLE 加字段或改类型、导入数据时创建索引、执行 ANALYZE以及崩溃恢复过程中的某些数据重建动作。它和我们更熟悉的 work_mem 很像都是数据库执行计划时分配的工作内存。但两者有一个关键区别work_mem 是为普通查询的排序、哈希连接等操作准备的每个会话每次操作都可能分配一份而 maintenance_work_mem 主要服务于后台维护操作尤其是 VACUUM 和建索引这类操作通常占用内存量更大执行时间更长。正因为它是“后台维护”用的很多人在调优时容易忽略它。配合 autovacuum 工作时maintenance_work_mem 会直接影响 VACUUM 扫描表时的内存处理能力。经典操作中建一个大型索引时如果该参数设置过小数据库会频繁地把排序临时数据落盘导致索引创建耗时非常长而设置过大就会带来今天要说的这类风险。1.3 为什么一个“工作内存”参数能阻碍启动由于 hgdb-se4.3.2 是 PostgreSQL 内核的分支版本它的启动过程也不是直接加载完配置就对外服务。数据库系统在启动时会读取预写日志WAL对上一次异常停机前没有完成的变更进行重放这个过程叫崩溃恢复。崩溃恢复期间数据库可能需要重建部分数据块、清理无效事务、甚至重新整理索引项。这些动作写入时同样会走维护性内存分配逻辑。崩溃恢复需要的资源比正常运行时更敏感。正常运行时内存不足最坏结果是某个查询报错、某个后台进程重试。启动恢复阶段内存不足直接结果就是启动进程被杀掉数据库无法进入可用状态。这就像你平时出门带一个小背包就够了但突然要在短时间内装下很多东西包还是那个包硬塞就会拉链崩开。日志里出现 signal 11本质就是数据库进程尝试申请超大块内存超出操作系统对进程的内存限制触发内存分配失败接着在异常分支里发生了段错误。2. 核心原理启动恢复的内存消耗为什么要特别小心2.1 数据库启动恢复时究竟做了什么要理解这个参数为什么能“卡住启动”得先看崩溃恢复的基本流程。数据库每次非正常停机比如服务器断电、进程被 kill -9、系统崩溃内存中的数据页和磁盘上的数据文件就会存在不一致。下次启动时数据库会从 WAL 里读取尚未落盘的事务记录把对应的数据块重新写一遍。这个“重写一遍”不是简单复制它可能包括按 WAL 记录定位到受影响的数据页并加载到内存对数据页执行对应的插入、删除、更新或页面组织操作如果涉及索引页还要按索引类型做结构调整某些情况下触发器性地重建索引、清理死元组。整个过程中数据库需要按维护操作的模式申请内存。如果 maintenance_work_mem 被设置成一个不合理的巨大值数据库启动进程在早期初始化时就会尝试预留对应规模的资源。多个恢复子任务同时存在时申请的内存量还会叠乘。所以问题不是“启动阶段到底用不用 maintenance_work_mem”而是“启动恢复过程中一旦用了就要按配置申请配置不合理直接引爆资源检查”。2.2 内存峰值怎么估算才不会翻车很多人喜欢把 maintenance_work_mem 调到 1GB、2GB 甚至更高理由是建索引快、VACUUM 快。这种做法在内存非常充裕且该数据库实例独占整机的场景下可能没事但一旦主机上还跑着其他应用或者实例里存在大量并发维护任务就会出问题。内存峰值不能只看单一参数必须做整体预算。一个常用评估方式如下假设一台服务器内存总量为 M数据库实例的计划占用大约由几项构成参数作用位置典型风险shared_buffers共享缓冲池所有会话共用设置过大会挤压操作系统页缓存空间maintenance_work_mem每个维护操作单独分配设置过大时多任务并发叠加容易超标work_mem每个排序/哈希操作单独分配高并发情况下乘以会话数风险很大max_connections最大连接数每个连接都会占用一定结构内存如果简单估算你可以用这个公式看个大概预估占用 ≈ shared_buffers max_connections × 会话基础内存 并发连接数 × work_mem 并发维护任务数 × maintenance_work_mem注意 maintenance_work_mem 是“每个操作”一份不是全局一份。比如两个 autovacuum 任务同时跑各自按 maintenance_work_mem 的值申请。恢复阶段如果出现并发索引重建或页面清理同样会按任务数量叠加。当时那台机器配置是 32GB 内存shared_buffers 设置成 8GBmaintenance_work_mem 被设成了 4GB同时还有多个应用占用内存。启动恢复时并发处理几个大表的清理和索引操作内存一下就冲破系统限制。正常运行时可能只看到某个维护操作慢一点启动阶段却直接判了死刑。2.3 为什么网上很多“建议值”经常坑人很多参数建议来自“内存大就多给一点”的直觉。给 maintenance_work_mem 设一个很大的值跑单条 CREATE INDEX 时确实可能更快因为排序可以放在内存里不需要写临时文件。但数据库的参数体系是联动的不能只盯着一个参数。常见的坑有这几种第一只考虑“这个参数本身”不考虑还有多少个并发任务会使用它。VACUUM 和自动清理任务是周期性触发的多个表同时进入维护状态时内存申请就成倍增长。第二只考虑“运行阶段”不考虑启动恢复阶段。数据库崩溃恢复时需要的内存并不比正常运行时少某些场景下甚至更极端因为它要在短时间内把大量 WAL 日志重放到数据文件上。第三只考虑“当前配置文件的参数值”忽略了参数可能被会话级、数据库级覆盖。如果你配置了数据库级或用户级的 ALTER DATABASE 或 ALTER USER 参数而这些参数又比全局配置更大启动过程中的某些分支也会按更大的值执行。我处理过的案例里配置文件上写的是 1GB但某一次维护时执行了 ALTER DATABASE xxx SET maintenance_work_mem 4GB这种隐蔽覆盖项在启动异常时极具迷惑性。3. 实操排查与修复从起不来服务到恢复运行3.1 确定真正的配置入口和生效文件出现这类故障第一步不是盲目修改参数而是找到数据库实例实际读取的配置文件。不同部署方式下配置文件的路径可能不一样但通常可以通过下面的方式确认# 通过进程命令行查看数据目录 ps -ef | grep hgdb # 输出中通常会看到 -D /data/hgdbdata 之类的内容然后用数据目录拼接配置文件名ls -l /data/hgdbdata/postgresql.conf如果服务完全起不来ps 命令可能看不到进程。这时可以查默认安装路径或者检查启动脚本、服务文件里写明的数据目录位置。最常见的路径无外乎 /var/lib/hgdb、/data/hgdb、/usr/local/hgdb/data 这几个。拿到配置文件后不要急着在文件里大改。先把原始配置备份一份哪怕只是复制一份带时间戳的备份文件cp /data/hgdbdata/postgresql.conf /data/hgdbdata/postgresql.conf.bak_$(date %Y%m%d)这个习惯非常重要。如果后面你还调整了其他参数或者需要回退备份能帮你省掉大量时间。3.2 通过最小配置让数据库先启动起来确认 maintenance_work_mem 配置过大之后目标很明确先把它降到安全范围让数据库恢复启动能力再讨论合理值。可以直接编辑配置文件把该参数改成稳妥的初始值sed -i s/^maintenance_work_mem.*/maintenance_work_mem 1GB/ /data/hgdbdata/postgresql.conf如果文件里的写法带着多余空格或者注释状态建议先用 grep 检查grep -rn maintenance_work_mem /data/hgdbdata/postgresql.conf这里说的 1GB 只是“先恢复启动”的过渡值不是最终推荐。对于大多数业务系统1GB 已经足够满足日常 VACUUM 和建索引的内存需求而且不容易在恢复阶段直接爆炸。更稳妥的做法是先设置成 512MB 甚至 256MB确认能起来后再往回调。如果数据库连单用户模式都进不去或者启动脚本会执行检查导致无法存档修复还可以用命令行指定参数的方式启动一次。PostgreSQL 系数据库支持在启动命令里临时覆盖参数值如下所示pg_ctl -D /data/hgdbdata -o -c maintenance_work_mem256MB start这样启动时数据库会用命令行参数覆盖配置文件里的值。但要注意这种方式只影响本次启动后面还需要把配置文件改正确否则下次启动还会炸。3.3 验证服务状态并确认恢复过程完整走完启动命令执行后不能只看服务状态变成“运行中”就算完。要重点观察日志确认没有新的报错tail -f /data/hgdbdata/log/*.log或者直接查数据库进程ps -ef | grep hgdb | grep -v grep如果进程稳定驻留再用 psql 连接验证psql -h 127.0.0.1 -U postgres -d postgres -c select version();注意启动恢复期间最好不要立刻执行大量查询或强制进行全表扫描否则会给恢复过程额外增加内存和 I/O 压力。让数据库安静运行几分钟确认日志里不再出现崩溃恢复相关错误再继续下一步调优。3.4 重新评估配置别再用拍脑袋方式设参数数据库恢复启动后根据这台机器的实际资源重新计算 maintenance_work_mem。可以按下面的思路来总内存 32GBshared_buffers 建议保持内存的 1/4 到 1/3也就是 8GB 左右不能再往上顶系统预留内存至少保留 4GB其他应用内存占用按实际情况扣除maintenance_work_mem 设置在 512MB 到 1GB 之间比较合理。更重要的是要检查工作中是否真的需要特别大的维护内存。大部分业务系统跑 VACUUM 时不追求极致的单操作速度更在意稳定性和并发能力。把 maintenance_work_mem 调得过大你的元组清理和索引构建确实可能快一点但带来的风险远大于收益。4. 常见问题直查与排障经验4.1 类似场景下的快速问题对查表表面现象可能原因第一步处理启动进程报 shared memory 错误maintenance_work_mem 或 shared_buffers 过大先降 maintenance_work_mem 到 256MB再逐步恢复段验证启动恢复过程中进程被 signal 11 杀死内存申请超限引发段错误查日志中是否有 memory not available确认是资源问题再动配置文件进程启动了但几十秒后又自动退出启动恢复尚未完成内存再次超限用命令行临时参数减小 maintenance_work_mem 重启启动时提示无法分配共享内存系统内核参数或 cgroup 限制检查操作系统内存限制、容器内存限制VACUUM 运行特别慢临时文件暴涨maintenance_work_mem 设置过低结合并发任务数逐步抬高不要一次性翻倍4.2 排障时要反复确认的三个隐藏位置经历这次故障后我再看类似问题时会提前检查三个容易被忽略的位置。第一是配置文件末尾是否有额外的 include 指令。PostgreSQL 系配置支持 include 其他文件比如 include_if_exists conf.d/maintenance.conf。如果主配置文件里的 maintenance_work_mem 看着没问题但 include 的文件里覆盖了它你对主文件的一切修改都是白费。第二是数据库级和用户级的参数覆盖。通过 ALTER DATABASE 或 ALTER USER 设置的值会覆盖 postgresql.conf 里的全局值。当你修改全局配置后最好用下面这条 SQL 查一遍当前实际生效值SELECT name, setting, unit, source FROM pg_settings WHERE name IN (maintenance_work_mem, work_mem, shared_buffers);如果 source 列不是 default 也不是 config file而显示 session、database 或 user就说明有更高优先级的覆盖项。第三是服务启动脚本里是否使用了额外的 -c 参数。部分部署方式会把参数写在启动服务脚本中比如 ExecStart 行里追加 -c maintenance_work_mem4GB。这种情况下改配置文件根本不生效必须同步修改启动脚本。4.3 修复后的应激测试怎么做数据库刚恢复不要急着宣布“故障解决”。我建议按下面的顺序做一轮快速验证手动执行一次 VACUUM观察进程内存和日志情况手动创建一个小索引确认排序不再大量落盘重启一次数据库确认启动阶段能稳定走出崩溃恢复观察一天内自动清理任务的日志确认没有因为内存不足而反复失败。这里特别提醒重启一次数据库非常关键。因为部分故障只在“启动恢复”这个特定路径上出现正常运行时的内存表现有欺骗性。如果重启后依然稳定才能说你真的解决了问题。5. 调优边界维护内存与周边参数的配合逻辑5.1 参数联动才是调优的本质故障修复后还要从全局角度审视参数组合。maintenance_work_mem 不是孤立存在的它和 work_mem、shared_buffers、max_connections 一起构成数据库的内存使用骨架。我常用的配置逻辑是参数设置原则举例32GB 内存shared_buffers总内存的 25% 左右最高不建议超过 33%8GBmaintenance_work_mem总内存的 1% 到 3%结合并发维护任务数512MB 到 1GBwork_mem每个查询排序分配量谨慎设置64MB 起步高并发时还要再降max_connections按业务峰值连接数估不能只看日常连接数按实际规模定这样分配的原因是并发连接数高时work_mem 的风险远比 maintenance_work_mem 大因为每个连接到排序操作都可能触发分配。但 maintenance_work_mem 在恢复阶段同样会集中释放所以同样不能忽视。5.2 “不敢调大参数”和“乱调大参数”之间的平衡很多人看到这次故障后可能走向另一个极端凡是 maintenance_work_mem 都设置得很小比如 32MB。这样确实安全但建索引、跑 VACUUM 时性能会很差大量临时文件落盘可能导致 I/O 暴涨进而拖垮业务。合理的做法是结合业务操作确定参数值。如果业务上有大量批量导入需求导入过程会重建索引维护内存需要相对大些建议从 1GB 起步。如果只是日常 OLTP 业务表体积不大VACUUM 负担轻512MB 足够。调整参数时遵循“小步试错”原则每次调整幅度不要超过当前值的 50%改完观察一天再动下一项。这样就算有问题也能快速定位是哪个参数导致的。5.3 监控缺一不可看日志比猜故障更靠谱无论参数调整得多么谨慎没有监控支撑都是盲调。至少应该把下面的信息纳入日常巡检数据库错误日志中的内存不足错误系统层的内存使用率、Swap 使用情况autovacuum 任务的执行时长和临时文件产生量pg_stat_activity 中活跃查询的内存消耗趋势。这次故障如果早有日志监控就能在数据库进程反复被杀时第一时间看到启动恢复失败的具体原因而不是在“数据损坏”的错误方向上一通排查。6. 最后的经验沉淀这个故障处理完之后我最大的感触是数据库参数没有绝对正确的值只有“适配当前环境的值”。maintenance_work_mem 设得太小维护操作慢设得太大启动都可能成为问题。关键是要理解参数背后的分配逻辑再结合机器内存、业务特征、并发模型做综合判断。后来每调整一个参数我都会顺手查一下它在 pg_settings 里的 source 字段确认改动确实生效并时刻留意配置文件里是否存在覆盖项。数据库重启的验证步骤也一定要做因为很多问题只有在启动路径上才会暴露。如果你也在维护 PostgreSQL 系的数据库建议这次故障后重新审视一下自己的 maintenance_work_mem 设置。不要等它成了启动拦路虎才想起来它的存在。

相关新闻

Tab Out徽章颜色心理学:后台Service Worker如何用绿黄红三色信号灯管理你的标签页数量

Tab Out徽章颜色心理学:后台Service Worker如何用绿黄红三色信号灯管理你的标签页数量

【免费下载链接】tab-out Keep tabs on your tabs. Turn your "New tabs" page into a mission control, so you can close them easily. Built for people who open too many tabs and never close them. 项目地址: https://gitcode.com/gh_mirrors/ta/…

2026/10/11 20:43:29 阅读更多 →
MATLAB贝叶斯优化实战:从高斯过程到超参数调参避坑指南

MATLAB贝叶斯优化实战:从高斯过程到超参数调参避坑指南

简介:这是一份面向MATLAB使用者和机器学习初学者的贝叶斯优化实现案例,专门应对高维黑盒函数、超参数调优和工程仿真寻优等难以借助解析方法求解的场景。压缩包里共两个文件,都是点m脚本,压缩后大小仅为六百四十字节左右&#xff…

2026/10/11 20:43:29 阅读更多 →
YOLOv5驱动的DNF自动脚本:目标识别、训练实战与踩坑全解析

YOLOv5驱动的DNF自动脚本:目标识别、训练实战与踩坑全解析

简介:基于YOLOv5目标检测的DNF自动脚本,是一套面向游戏脚本开发与计算机视觉学习者的实战代码,主要解决DNF界面中的地图场景、怪物位置、技能冷却状态识别,以及键鼠自动响应与操作问题,可用于游戏刷图、任务挂机等场景…

2026/10/11 20:43:29 阅读更多 →

最新新闻

蟑螂检测数据集VOC/YOLO双格式解析与YOLOv8训练实践

蟑螂检测数据集VOC/YOLO双格式解析与YOLOv8训练实践

简介:面向计算机视觉目标检测需求,这份数据包适合需要训练蟑螂识别模型的开发者、算法学习者以及智能家居或卫生防治项目人员。资源包含三百七十四张真实环境蟑螂图片,配套相同数量的XML标注文件与TXT标注文件,XML遵循Pascal VOC格…

2026/10/11 21:36:30 阅读更多 →
--cached 只删 Git 记录,不删本地 node_modules。之后 Git 不会再追踪它。删除线上仓库的node_modules

--cached 只删 Git 记录,不删本地 node_modules。之后 Git 不会再追踪它。删除线上仓库的node_modules

项目根目录新建或编辑 .gitignore,加入:/gitignorenode_modules/终端执行,把 node_modules 从 Git 索引中移除(保留本地文件):git rm -r --cached node_modules提交并推送:git add . git commit…

2026/10/11 21:36:30 阅读更多 →
WFU_PickAtlas脑区ROI生成与批处理避坑指南

WFU_PickAtlas脑区ROI生成与批处理避坑指南

简介:这是一份面向脑成像研究者的MATLAB工具箱安装包,用于基于Talairach Daemon数据库生成感兴趣区(ROI)掩膜,可便捷调用Brodmann分区、脑叶、半球、解剖标签与组织类型等多种脑模板,满足不同分析场景的模板…

2026/10/11 21:36:30 阅读更多 →
葡萄酒质量分析Python实战:从数据探索到模型评估的完整方案

葡萄酒质量分析Python实战:从数据探索到模型评估的完整方案

简介:基于Python实现的葡萄酒质量分析项目,面向计算机专业学生,适用于数据挖掘课程设计、期末大作业以及实战练习。资源包含完整源码和全部数据,共十六个文件,其中十个csv数据文件涵盖酒精含量、酸度、密度等影响葡萄酒…

2026/10/11 21:36:30 阅读更多 →
智慧空开如何重塑末端配电?ASCB2物联网断路器实战解析

智慧空开如何重塑末端配电?ASCB2物联网断路器实战解析

半夜两点,值班室的电话响了。某园区一栋办公楼的四层整体断电,电话那头很着急。运维人员从家里赶到现场,打开强电井,发现一个出线回路的空开跳了,简单检查没有明显短路,顺手合上闸,照明、插座、…

2026/10/11 21:36:30 阅读更多 →
YOLOv5绝缘子缺陷检测实战:数据处理、模型训练与避坑指南

YOLOv5绝缘子缺陷检测实战:数据处理、模型训练与避坑指南

简介:面向高校毕设学生和深度学习初学者的YOLOv5电线绝缘子缺陷检测完整项目,基于yolov5框架实现绝缘子缺陷目标检测,涵盖数据集、训练代码、预训练模型与教程讲解,适用于毕业设计、课程设计及期末大作业等场景。压缩包内含500个文…

2026/10/11 21:35:29 阅读更多 →

日新闻

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

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

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

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

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

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

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

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

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

2026/10/11 0:00:27 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/11 0:00:27 阅读更多 →

月新闻

我发现了一个新思路:用 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/11 10:45:37 阅读更多 →
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/11 14:36:53 阅读更多 →
黑夜航拍船只数据集训练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/11 14:36:54 阅读更多 →