MySQL主键选型实战:自增、雪花ID与UUID的性能对比与选型指南
1. 从一次“被怼”说起主键选型的实战反思那天下午我正对着屏幕上的数据库表结构设计图心里盘算着新项目的技术选型。为了追求所谓的“分布式友好”和“全局唯一”我毫不犹豫地在几个核心表的主键字段上敲下了VARCHAR(36)和BIGINT UNSIGNED分别准备填入 UUID 和雪花算法生成的 ID。在我看来这简直是现代互联网架构的标配既能避免分库分表时的主键冲突又省去了应用层等待数据库自增返回 ID 的麻烦一举多得。然而当我把设计文档发给技术领导 review 后得到的回复却是一连串的质问和一张拉会讨论的日程邀请。会上领导指着我的设计从存储空间、索引性能、写入热点一直讲到业务适配性句句戳中要害。那次“被怼”让我彻底明白主键选型远不是拍脑袋选个“先进”方案那么简单它背后是一整套关于数据库底层原理、业务场景和运维成本的综合权衡。今天我就把这次踩坑的经历和后续的深度复盘分享出来希望能帮你避开我走过的弯路。简单来说主键是关系型数据库的基石它唯一标识每一行数据并且默认会成为聚簇索引在 InnoDB 引擎下。这意味着主键的选择直接影响了数据在磁盘上的物理存储顺序、索引的查找效率以及插入新数据时的性能表现。UUID 和雪花 ID 因其全局唯一、分布式生成的特性在分布式系统中被广泛讨论但它们真的是 MySQL 主键的最优解吗这篇文章我将为你彻底拆解这两种方案在 MySQL 场景下的优劣并给出不同业务场景下的选型建议。无论你是正在设计新表的开发者还是面临性能优化的 DBA这些从实战中总结出的经验或许能给你带来新的启发。2. UUID 作为主键光鲜背后的性能陷阱UUIDUniversally Unique Identifier是一个 128 位的数字通常表现为 32 个十六进制数字由连字符分隔为五组格式如123e4567-e89b-12d3-a456-426614174000。它的最大优势就是全局唯一性理论上在任何时间、任何地点、由任何系统生成都不会发生冲突。这听起来非常完美尤其适合微服务架构下各个服务独立生成 ID 而无需中心化协调的场景。但是当它坐上 MySQL 主键这个位置时一系列问题就接踵而至了。2.1 存储空间与索引膨胀的代价首先我们算一笔最直观的账存储空间。一个标准的 UUID 字符串需要 36 个字符32 个十六进制数加 4 个连字符。在 MySQL 中如果使用VARCHAR(36)或CHAR(36)来存储根据字符集的不同占用空间巨大。以常用的utf8mb4字符集支持完整的 Unicode包括 Emoji为例每个字符最多占用 4 个字节。那么一个 UUID 字符串最大可能占用36 * 4 144字节。即使使用latin1或ascii字符集也需要 36 字节。相比之下传统的自增主键BIGINT UNSIGNED仅需 8 字节。这中间的差距是 4.5 倍到 18 倍这不仅仅是磁盘空间的浪费。在 InnoDB 引擎中主键索引即聚簇索引的叶子节点直接存储了完整的行数据。主键越大每个索引页默认 16KB能存放的数据行数就越少。假设一行数据总大小为 1KB使用 8 字节主键时一个页大约能存 15 行使用 36 字节主键时可能只能存 14 行若使用 144 字节可能就只剩 13 行甚至更少。这意味着存储相同数量的数据使用 UUID 需要更多的数据页。更多的数据页会带来一系列连锁反应索引树更高BTree 索引的深度增加因为每一层节点能存储的指针数变少了。查询时可能需要更多的磁盘 I/O 才能定位到数据。缓冲池效率降低InnoDB 的缓冲池Buffer Pool大小是有限的。更大的主键意味着更少的行能被缓存在内存中缓存命中率下降更多的查询需要访问慢速的磁盘。写放大当发生页分裂时需要移动的数据量也更大。注意有人会想到用BINARY(16)来存储去掉连字符的 UUID32个十六进制数字这样只需 16 字节比BIGINT大一倍但比字符串形式好很多。这是一个重要的优化点后文会详细讨论其利弊。2.2 无序插入导致的“页分裂”噩梦这是 UUID 作为主键最致命的问题。标准的 UUID版本1基于时间戳版本4随机本质上是随机的或者其时间序部分不够连续。而 InnoDB 的聚簇索引要求数据按照主键顺序物理存储在磁盘上。当你插入一条新的数据其 UUID 主键是aabbccdd-...它可能应该被插入到索引树的中间某个位置而不是末尾。为了维持有序性InnoDB 必须进行“页分裂”操作找到应该插入的页如果该页已满则将其大约一半的数据移动到新页然后在合适的位置插入新行。这个过程是昂贵的消耗 I/O需要读取原页写入新页和原页。消耗 CPU进行数据移动和索引重组。导致页空间碎片化分裂后两个页都可能未被填满降低了空间利用率。更糟糕的是这种随机插入使得数据在物理存储上变得非常碎片化。顺序扫描例如范围查询WHERE id xxx本应是高效的但因为数据物理上不连续会导致大量的随机 I/O性能急剧下降。相比之下自增主键的插入永远在索引树的末尾追加几乎没有页分裂存储紧凑顺序 I/O 效率极高。2.3 可读性与调试的隐性成本在开发和运维过程中我们经常需要直接查看数据库数据或根据 ID 进行沟通。123456这样的自增 ID 显然比550e8400-e29b-41d4-a716-446655440000更容易记忆、口头传达和手工输入。在日志中搜索、在监控图表中定位特定 ID 对应的曲线长字符串 ID 都会带来不便。虽然这不是技术硬伤但在团队协作和问题排查效率上是一个不可忽视的体验细节。3. 雪花算法 ID有序性的救赎与新的挑战雪花算法Snowflake是 Twitter 开源的一种分布式 ID 生成算法它生成的 ID 是一个 64 位的长整型正好可以用 MySQL 的BIGINT存储结构上大致分为时间戳毫秒级、工作机器 ID、序列号。它的核心思想是在单机上按时间顺序生成 ID从而保证了全局趋势递增。这似乎完美解决了 UUID 的无序性问题但它也带来了自己独特的挑战。3.1 趋势递增的优势与存储考量雪花 ID 作为BIGINT存储仅需 8 字节在存储空间上和自增主键打平。更重要的是由于其“趋势递增”的特性插入数据库时大部分新 ID 都会比现有 ID 大因此插入操作会大部分发生在索引树的末尾类似于自增 ID能极大减少页分裂保持数据存储的紧凑性和顺序性。这对于写入密集型应用是一个巨大的利好。但是这里有一个关键点“趋势递增”不等于“严格递增”。在同一毫秒内如果序列号用尽或者服务器时钟发生回拨这是雪花算法的一个著名问题生成的 ID 可能会小于前一个 ID。虽然概率较低但这种“小范围”的无序插入仍然可能引发页分裂只是其频率和影响范围远小于完全随机的 UUID。3.2 分布式环境下的时钟与机器 ID 难题雪花算法的正确运行严重依赖于两个前提系统时钟不能回拨如果服务器因为 NTP 同步或人工调整导致时间倒流就可能生成重复的 ID。虽然算法实现中通常会加入时钟回拨检测与等待机制但这会增加系统的复杂性和潜在的性能抖动。工作机器 ID 必须全局唯一在分布式系统中需要为每个 ID 生成服务实例分配一个唯一的 ID通常由数据中心 ID 和机器 ID 组合。这个 ID 的分配和管理本身就是一个分布式配置问题。虽然可以通过 ZooKeeper、Etcd 等协调或者直接用 IP 地址、容器 ID 的哈希来简化但都引入了额外的依赖和运维成本。如果你的业务没有发展到真正的分布式规模比如就一两台数据库服务器引入雪花算法无异于“杀鸡用牛刀”增加了不必要的复杂度。3.3 业务暴露与数据安全的隐忧使用自增主键时我们通常不建议将其直接暴露给前端因为连续的 ID 可能会暴露业务量例如通过用户 ID 的增量推测每日新增用户甚至可能被用于遍历攻击爬虫通过递增 ID 尝试访问所有资源。雪花 ID 和 UUID 由于是不连续的在这方面有天然优势。但是雪花 ID 本身携带了时间戳和机器信息。虽然这些信息通常需要逆向工程才能解析但对于安全要求极高的场景这仍然是一个潜在的信息泄露点。相比之下UUID 的随机版本v4在不可预测性上更胜一筹。4. 性能实测对比数字不会说谎理论分析再多不如一次实际的测试。我搭建了一个简单的测试环境MySQL 8.0InnoDB 引擎默认配置。创建了三张结构完全相同的表只有主键类型不同table_auto_inc: 主键为BIGINT UNSIGNED AUTO_INCREMENTtable_snowflake: 主键为BIGINT UNSIGNED模拟雪花 ID使用程序生成趋势递增的 IDtable_uuid_char: 主键为CHAR(36)存储带连字符的 UUIDtable_uuid_bin: 主键为BINARY(16)存储无连字符的 UUID 二进制形式每张表除了主键还有几个简单的字段name VARCHAR(100),created_at TIMESTAMP。然后我使用脚本向每张表顺序插入 100 万条数据并观察插入时间、最终表文件大小以及执行一些典型查询的性能。主键类型插入100万条耗时表文件大小 (.ibd)SELECT * WHERE id ?(平均)SELECT * ORDER BY id LIMIT 100自增 BIGINT~85 秒~92 MB~0.2 ms~5 ms雪花 BIGINT~95 秒~92 MB~0.2 ms~8 msUUID CHAR(36)~220 秒~145 MB~0.3 ms~120 msUUID BINARY(16)~180 秒~108 MB~0.25 ms~65 ms结果分析插入性能自增主键毫无悬念地最快因为只有顺序追加。雪花 ID 次之有极小的页分裂开销。两种 UUID 形式都慢得多CHAR(36)最慢因为其无序插入导致大量页分裂且每次写入的数据量更大。存储空间自增和雪花 ID 的表大小相同。UUID BINARY(16)比它们大 17% 左右而UUID CHAR(36)则大了 57%这直观地印证了存储空间的巨大差异。点查性能基于主键的等值查询差距不大因为都是通过 BTree 一次深度查找。但 UUID 的索引树可能更深需要多一次 I/O 的概率稍高。范围查询/排序这是差距最明显的地方。自增主键的顺序扫描极快。雪花 ID 也很快但略慢可能因为其“趋势递增”并非完全连续物理存储上有微小间隙。而 UUID 的查询慢了数十倍因为它需要遍历一个高度碎片化的索引并进行大量的随机 I/O。这个测试清晰地表明在单实例 MySQL 或简单主从架构下自增主键在性能和存储效率上拥有压倒性优势。雪花 ID 是一个不错的折中但 UUID特别是字符串形式的 UUID作为主键的性能代价是巨大的。5. 实战选型指南什么情况下该用什么经过上面的剖析我们可以得出一个清晰的决策框架。主键选型没有银弹必须结合你的具体业务场景。5.1 首选方案自增主键 (AUTO_INCREMENT)适用场景绝大多数业务场景。特别是单数据库实例。传统的一主多从读写分离架构。业务没有分库分表的近期规划。写入并发量高追求极致的插入性能。为什么是首选性能最佳顺序写入无页分裂存储紧凑。简单可靠数据库原生支持无需额外组件无时钟问题。存储高效8字节最小。易于使用开发、调试、运维都方便。需要注意的坑暴露业务信息如前所述可以通过业务层二次封装一个对外暴露的 ID如雪花 ID 或 UUID来解决对内关联仍用自增主键。分库分表不友好这是它最大的短板。在分库分表时需要引入分布式序列生成方案如 Leaf、TinyId或者使用复合主键分片键自增。但这并不意味着在分库分表前就不能用自增主键你可以在架构演进时再进行平滑迁移。5.2 折中方案雪花算法 ID (BIGINT)适用场景明确的分布式系统架构多个应用节点需要独立生成 ID。已经或即将进行分库分表需要避免主键冲突。业务对 ID 的生成性能和趋势有序性有要求且能接受一定的系统复杂度。希望 ID 对业务不透明非连续但又不想要 UUID 的性能和存储代价。实施方案建议使用成熟的客户端 SDK如美团的 Leaf、百度的 UidGenerator它们已经解决了时钟回拨、机器 ID 分配等难题比自己造轮子稳定得多。数据库字段类型BIGINT UNSIGNED足够存储 64 位雪花 ID。考虑“业务无关性”如果这个 ID 需要暴露给前端确保你的雪花算法实现是通用的不会因为业务线不同而解析出不同的含义。5.3 谨慎选择方案UUID适用场景经过深思熟虑后仍有明确需求对全局唯一性有极端要求且无法接受任何中心化协调哪怕是一个 ID 生成服务。数据需要在完全独立、无法联网的系统中生成之后才合并到中心数据库。安全要求极高需要完全不可预测、无任何信息泄露的标识符选用 UUID v4。如果必须用请务必优化永远不要用CHAR(36)/VARCHAR(36)必须使用BINARY(16)或VARBINARY(16)存储去掉连字符的 16 字节二进制数据。插入和查询时在应用层进行 hex 编解码。-- 创建表 CREATE TABLE users ( id BINARY(16) PRIMARY KEY, name VARCHAR(100) );// Java 示例插入时转换 UUID uuid UUID.randomUUID(); ByteBuffer bb ByteBuffer.wrap(new byte[16]); bb.putLong(uuid.getMostSignificantBits()); bb.putLong(uuid.getLeastSignificantBits()); preparedStatement.setBytes(1, bb.array()); // 查询时转换 byte[] bytes resultSet.getBytes(id); ByteBuffer bb ByteBuffer.wrap(bytes); long high bb.getLong(); long low bb.getLong(); UUID retrievedUuid new UUID(high, low);考虑有序 UUID如 UUID v1基于时间戳或更现代的 ULIDUniversally Unique Lexicographically Sortable Identifier。它们生成的是趋势递增的 ID可以部分缓解页分裂问题。一些数据库如 PostgreSQL有原生uuid-ossp扩展支持生成有序 UUIDMySQL 则需要应用层实现。不作为聚簇索引如果业务允许可以创建一个自增的BIGINT作为主键聚簇索引同时将UUID作为一个唯一的二级索引。这样既保留了 UUID 的业务特性又获得了自增主键的写入性能。但这会占用额外的存储空间多一个索引。6. 领导到底在怼什么—— 架构思维的提升回过头看领导怼我的不仅仅是技术选型本身更是一种缺乏深度思考的架构思维。他指出了几个我当时完全没考虑到的层面第一过早优化与复杂度成本。我们的项目初期根本谈不上分布式用户量也有限。我却为了一个“未来可能”需要的特性分布式 ID提前引入了 UUID 的复杂性和性能损耗。在软件工程中这是典型的“过早优化”。正确的做法是先用最简单的方案自增主键快速启动业务当业务增长到单库瓶颈真正需要分库分表时再通过“双写”、“影子字段”等平滑迁移方案过渡到分布式 ID。届时你对业务的理解更深技术选择也会更精准。第二对数据库核心原理的忽视。我只看到了 UUID 的“全局唯一”却完全忽略了 InnoDB 聚簇索引、页分裂、顺序 I/O 这些底层机制对性能的致命影响。作为后端开发者不能只停留在 API 调用层面必须对存储引擎的基本原理有扎实的理解。否则设计出来的系统就像在沙地上盖楼随时可能崩塌。第三缺乏数据驱动的决策过程。我当时的选择是基于“感觉”和“行业传闻”而不是基于自己业务的真实数据和压力测试。领导要求我拿出性能对比数据、存储成本估算、未来三年的业务增长预测我哑口无言。任何重要的技术决策都应该有数据支撑哪怕是一个简单的本地 benchmark。这次经历让我深刻认识到技术选型不是选最酷的而是选最合适的。合适的标准来自于对业务现状的清晰认知、对技术原理的透彻理解以及对未来演进的合理预判。把这次“被怼”的教训写下来既是对自己的复盘也希望能给屏幕前的你提个醒下次设计表结构时不妨多问自己几个为什么或许就能避开一个潜在的大坑。

相关新闻

大模型幻觉

大模型幻觉

一句话总结:大模型幻觉的根本原因,是它通过概率生成语言,而不是通过严格的事实查询和逻辑验证来回答问题;当信息不足、记忆模糊或上下文存在歧义时,它可能用合理的语言填补知识空缺。大模型出现“幻觉”,简…

2026/10/12 4:46:25 阅读更多 →
抗磁、顺磁与铁磁:从原子磁矩到工程选材的完整指南

抗磁、顺磁与铁磁:从原子磁矩到工程选材的完整指南

1. 磁性的世界:从冰箱贴到硬盘的奥秘你有没有想过,为什么一块磁铁能牢牢吸在冰箱门上,而一枚硬币却不行?或者,为什么有些金属能被磁铁吸引,有些却表现出排斥,而有些则完全“无动于衷”&#xff…

2026/10/12 4:46:25 阅读更多 →
IT工程师职业发展:技术深度与沟通广度的动态平衡策略

IT工程师职业发展:技术深度与沟通广度的动态平衡策略

这次我们来看一个在IT行业持续引发讨论的话题:技术与沟通的权重之争。这并非一个可以简单用“都重要”来回答的问题,因为它直接关系到个人职业路径规划、团队协作效率和项目最终成败。对于开发者、项目经理乃至技术管理者而言,理解在不同阶段…

2026/10/7 1:44:22 阅读更多 →

最新新闻

AI日报制作全流程:从信息筛选到排版发布的实操经验

AI日报制作全流程:从信息筛选到排版发布的实操经验

1. 当"日报"变成一种产品:AI日报的定位与读者画像做AI日报这件事,我一开始的想法特别朴素——不就是把当天的重要消息攒一攒、排个版发出去吗?真正动手做了几期之后才发现,这个判断错得离谱。日报类内容看起来门槛极低&…

2026/10/12 4:45:49 阅读更多 →
PyTorch目标检测入门:用Faster R-CNN训练小黄人检测模型

PyTorch目标检测入门:用Faster R-CNN训练小黄人检测模型

如果你也搜过“PyTorch 目标检测怎么入门”,大概率和我一开始一样,面对一堆模型名称、配置参数、官方教程里跳来跳去的术语,半天不知道从哪里下手。分类任务一抓一大把教程,但检测任务要同时输出“位置”和“类别”,代…

2026/10/12 4:45:49 阅读更多 →
LiteLLM:大模型API统一调度与智能路由实战指南

LiteLLM:大模型API统一调度与智能路由实战指南

1. 项目概述:LiteLLM不是“轻量版大模型”,而是智能API的统一调度中枢LiteLLM这个名字,刚看到时我第一反应是——又一个试图把大模型做小的压缩项目?结果上手试了三天,才发现自己完全想错了。它压根不碰模型结构、不改…

2026/10/12 4:45:49 阅读更多 →
pml-book《概率机器学习》第 11 章线性回归全攻略:官方补充材料、五份实战 Notebook 与源码级学习路径

pml-book《概率机器学习》第 11 章线性回归全攻略:官方补充材料、五份实战 Notebook 与源码级学习路径

文档教程机器学习 【免费下载链接】pml-book "Probabilistic Machine Learning" - a book series by Kevin Murphy 项目地址: https://gitcode.com/gh_mirrors/pm/pml-book 点击查看 免费下载 导读:本文围绕 Kevin P. Murphy 所著《Probabili…

2026/10/12 4:45:49 阅读更多 →
AI时代学编程还有价值吗?核心是编程思维而非写代码

AI时代学编程还有价值吗?核心是编程思维而非写代码

最近被问得最多的一个问题,就是“AI时代该不该学习编程”。问的人有做运营的、做财务的、还在读书的学生,也有刚转行到技术边缘的年轻人。我明显感觉到,这个问题的答案跟两三年前已经完全不是一个量级了。以前“要不要学编程”基本等于“要不…

2026/10/12 4:45:49 阅读更多 →
量子开发者人才缺口百万?入门技能图谱与实操路径全解析

量子开发者人才缺口百万?入门技能图谱与实操路径全解析

一份“2030年量子开发人才缺口达百万”的预测,最近在朋友圈被转得很猛。我第一反应是:这数字靠不靠谱先放一边,“量子开发者”到底是个什么工种,多数人其实说不清楚。作为写过几年经典软件、又花了不少时间钻进量子计算这个交叉领…

2026/10/12 4:44:49 阅读更多 →

日新闻

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

在数码相机、高清显示屏与现代矢量图形技术高度发达的今天,画面可以做到绝对的锐利、平滑与无瑕。然而,当一张秋日手账插画或拍立得照片过于“平整无瑕”时,往往会散发出一种冰冷生硬的“数码塑料感(Digital Plasticity&#xff0…

2026/10/12 0:00:59 阅读更多 →
活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

在现代网页与移动端设计中,横排(Horizontal Layout)早已经成为了绝对的主流。然而,当我们翻开泛黄的线装古籍、宋版木刻诗集,或是欣赏一张茶道雅集的手写便签时,那种**自上而下纵向书写、自右向左逐列铺展&…

2026/10/12 0:00:59 阅读更多 →
周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

每到周日的晚上八点到十点,很多人心里都会悄悄亮起一盏警示灯。 在心理学上,这种现象有一个专门的称谓——“周日夜晚焦虑症(Sunday Scaries)”。明天又是周一,闹钟又要重新在七点响彻卧房;脑海里仿佛有一个…

2026/10/12 0:00:59 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/12 0:16:43 阅读更多 →

月新闻

我发现了一个新思路:用 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 阅读更多 →