MongoDB深分页性能优化:告别skip,拥抱游标分页与索引设计
有一类性能问题我几乎每隔几个月就要跟人解释一遍MongoDB 分页到底怎么搞才能既支持大数据量又不被skip()拖垮。早些年我们一个后台管理系统的列表页数据量到了一千多万条之后翻到第 500 页的接口直接花了 20 多秒DBA 一看慢日志全是被这条分页查询打出来的。后来我把方案从skip() limit()改成基于排序键的游标分页同样的数据量翻到后面的页面稳定在 100 毫秒以内。这篇文章就把当时踩过的坑、对比过的方案和最终落地的替换思路完整写出来希望能帮遇到同样问题的朋友少走弯路。1. 为什么skip()在浅分页时看不出问题一到深分页就崩1.1 一条skip()查询的真实执行路径很多人以为find().skip(10000).limit(20)只是“跳过前 1 万条再取 20 条”性能应该不错。但 MongoDB 本身没有“直接从第 10001 条开始取”的能力它的执行逻辑是先把满足条件的文档找出来按排序条件排好序然后从头开始一条一条数数到第 10001 条才开始返回。这个过程中的关键代价是“找出来”和“排好序”。如果查询条件没有命中索引MongoDB 就必须对集合做全表扫描如果排序字段没有索引那它在返回结果之前还得把符合条件的文档全部加载进内存做排序。数据量小的时候这些操作都在毫秒级完成但当集合有几百万甚至上千万条记录时skip()的偏移量越大白白丢弃的文档就越多。用一个生活化的例子来理解你有一本 1000 页的电话簿想找第 500 页的人名正常人会直接翻到中间但skip()的做法是从第 1 页开始往下翻每翻一页都确认一次“这是不是第 500 页”翻错了一页还要重新来耗时自然随页码增长而线性增加。1.2 从explain()结果里看真相不要猜直接让 MongoDB 把执行计划告诉我们。在 mongo shell 里跑一下db.orders.find({ status: shipped }) .sort({ createdAt: -1 }) .skip(100000) .limit(20) .explain(executionStats);重点关注这几项stage如果出现COLLSCAN说明查询条件没有走索引你最该优化的不是分页而是过滤条件。如果排序字段没有索引还可能出现SORTstage。totalDocsExamined这个数字最能说明问题。skip(100000).limit(20)可能让 MongoDB 扫描并丢弃 10 万条文档然后才返回 20 条。docsReturned最后返回的文档数。executionTimeMillis整体耗时。在我当年那个慢查询上totalDocsExamined和totalKeysExamined都是七位数而docsReturned只有 20。也就是说数据库为了让你翻到第 5000 页把前面几十万条文档反复扫了一遍又一遍。这种浪费是架构层面的指望调一个参数就能解决是不可能的。1.3 大数据量下的真实软件测试偏移量越大耗时越离谱我专门造了一批测试数据来对比集合里放了 1000 万条订单记录过滤条件是status shipped排序字段是createdAt。翻页位置和耗时的大致关系如下翻页位置skip 偏移量排序内存总耗时约第 1 页0小20ms第 100 页2000中等120ms第 1000 页20000较大900ms第 5000 页100000很大6.8s第 10000 页200000极大15s这里还没算上并发。如果系统同时有几十个用户在不同位置翻页每次翻页都要去重新扫描那个偏移量之前的所有数据数据库 I/O 会被瞬间打满。所以结论很直接skip()适合“数据量少 翻页浅”的场景不适合“数据量大 深分页”的场景。如果你的系统已经出现了翻页越来越慢或者是导出任务在取第 10 万条数据时超时那就该换方案了。2. 替代方案的核心思路把“翻页”改成“定位”2.1 先想清楚你要哪种分页模型在做优化之前必须分清业务需要的是“页数分页”还是“游标分页”。页数分页用户能看到“第 1 页、第 2 页……第 100 页”的页码可以随意跳转。这类需求常见于后台管理系统。它的特点是必须知道总页数所以一定要先count总数。游标分页用户只能“上一页 / 下一页”地顺序翻不能直接跳到任意页面。这种模式常见于信息流、消息记录、日志列表。它的特点是无需计算总页数天然适合用排序键做范围查询。skip()能同时满足两种模式但它的大问题就出在“翻页越深越贵”。如果你做的是信息流这类顺序浏览场景完全没必要用页数分页来折磨数据库。改成游标分页后每次查询都是从上一页的最后一条记录继续往后找查询代价跟翻多少页没关系只跟“找 20 条新数据”有关这才是真正的线性收益。2.2 计算总页数也有讲究别把countDocuments()用错只要是页数分页就必须知道总文档数。MongoDB 里建议用countDocuments()来精确计算它底层是聚合框架能正确处理过滤条件。一个容易踩的坑是collection.estimatedDocumentCount()很快但它不基于过滤条件只是从集合的元信息里获取一个估算值。如果业务上可以接受“页数显示个大概”用它可以省不少事但如果有严格的过滤条件必须用const total await db.collection(orders) .countDocuments({ status: shipped });这里第二个坑是countDocuments的过滤条件要和查询列表的过滤条件保持一致。不然总数是 100 万列表只查得到 10 万条翻到 11 页直接空白用户会以为系统坏了。在数据量特别大的情况下如果countDocuments本身要跑好几秒我会建议业务方接受估算总数或者干脆把“总页数”这个万恶之源从 UI 上拿掉只保留“上一页 / 下一页 / 回到首页”。为用户体验牺牲一点精确度换来的是数据库的长期稳定。2.3 keyset 分页Seek 分页的模型谈到替代skip()最经典的就是 keyset 分页也叫 Seek 分页。它的核心思想不是“跳过前面 N 条”而是“记住最后一条记录的位置直接从那个位置后面继续找”。举个例子按createdAt倒序排列的订单列表第一页查到了最后一条记录的createdAt 某个时间戳 a第二页的查询条件就变成db.orders.find({ status: shipped, createdAt: { $lt: a } }) .sort({ createdAt: -1 }) .limit(20);这样 MongoDB 只需要通过索引找到比a更早的 20 条记录完全不需要跳过任何数据。“跳过”这个动作彻底消失了。keyset 分页有两个硬性前提排序字段必须有索引。排序字段的值最好是唯一的或者至少是“近似唯一”否则可能出现同一页里重复数据。最常见的做法是用_id或createdAt如果你发现排序字段存在大量重复比如很多人排序用status但 status 只有 2 个值那就必须引入第二个排序字段来做二次排序比如{ status: 1, createdAt: -1 }甚至可以再加_id保证排序结果完全稳定。2.4 一个可直接落地的 keyset 模板下面是一个基于 Node.js MongoDB 驱动的参考实现重点看逻辑不用在意具体库的语法差异。// 第一页没有 lastId直接从最开始查 const pageSize 20; const filter { status: shipped }; const cursor db.collection(orders).find(filter) .sort({ _id: -1 }) .limit(pageSize); const list await cursor.toArray(); const lastId list.length ? list[list.length - 1]._id : null; // 第二页、第三页带上 lastId const filterWithSeek { status: shipped, _id: { $lt: lastId } }; const nextPage await db.collection(orders) .find(filterWithSeek) .sort({ _id: -1 }) .limit(pageSize) .toArray();如果排序字段不是_id而是业务时间字段就得把它们合成一个条件// 排序字段{ createdAt: -1, _id: -1 } // 假设上一页最后一条createdAt lastCreatedAt, _id lastId const filter { status: shipped, $or: [ { createdAt: { $lt: lastCreatedAt } }, { createdAt: lastCreatedAt, _id: { $lt: lastId } } ] };这段代码的精髓在于先按createdAt找更早的数据如果createdAt相等再用_id下的主键次序把同一条时间线上的数据排清楚。这样分页是严格稳定的无论怎么刷新页面都不会串页。3. 过滤、排序、分页一起出现时索引该怎么做3.1 从“过滤字段”到“排序字段”的索引顺序很多人建索引时只知道要给排序字段建索引但没想过过滤字段和排序字段合在一起时的顺序。MongoDB 索引设计有一条核心经验先放等值过滤字段再放排序字段最后放分页键。比如查询条件是status shipped排序字段是createdAt: -1索引就建db.orders.createIndex({ status: 1, createdAt: -1 });这样 MongoDB 可以先用status定位到“已发货”的所有记录再在这些记录上以createdAt倒序索引顺序扫描不用额外排序更不用skip。如果业务上还需要用_id来做分页稳定性索引可以扩展成db.orders.createIndex({ status: 1, createdAt: -1, _id: -1 });这个索引的好处是整个查询、排序、分页的数据都能从索引里拿到数据位置不需要回表读取每一篇文档来比较。我们程序的耗时从秒级降到几十毫秒主要就是靠这个索引。3.2 多字段排序时keyset 分页怎么写条件当一个列表需要按多个字段排序时例如sort({ price: 1, createdAt: -1 })keyset 分页就需要记录一组排序键而不能只记一个_id。假设第一页最后一条文档是{ price: 100, createdAt: 2021-01-01, _id: 123 }第二页的查询条件为db.products.find({ $or: [ { price: { $gt: 100 } }, { price: 100, createdAt: { $lt: 2021-01-01 } }, { price: 100, createdAt: 2021-01-01, _id: { $gt: 123 } } ] }) .sort({ price: 1, createdAt: -1, _id: 1 }) .limit(20);排序方向不同比较运算符也不同。这里是price升序所以用$gtcreatedAt降序所以用$lt到最后用_id兜底来保证排重。每次翻页时把上一页的这三个字段序列化到页参数里下一轮查询直接用。这段逻辑看起来复杂但它换来的是“翻到第几百页都不卡”。实际项目中我还遇到过更复杂的情况用户可以在线切换排序字段比如先按时间排、再按价格排。这种动态排序对 keyset 完全无能为力因为索引是固定的不可能实时为每一种排序建索引。所以我会在架构上做限制允许乱跳页的列表最多只支持两三种排序方式每种排序对应一个固定索引前端切换排序时明确提示用户“翻页到当前第 2 层继续翻页将重新计数”。3.3 当业务上“必须跳页”时怎么补救如果后台系统就是需要一个“跳到第 N 页”的入口那 keyset 分页没法直接满足。这时候有两个补救方向限制最大可跳转页数。在产品侧规定只允许查看前 100 页之后的列表只能通过“按时间范围筛选”来缩小查询范围。这种做法很多后台系统都在用运营想看旧数据时可以通过自定义起始时间来定位。利用“区间快照”做页数映射。定期把当期数据的分页快照存下来比如把_id范围映射到页号。翻页时先查快照再按_id做 keyset 查询。这个方案实现成本高但能兼顾跳页和性能。另外如果业务只是要“导出全量数据”那根本不需要分页 API直接用游标迭代整个集合就好了const cursor db.collection(orders).find({ status: shipped }).sort({ _id: 1 }); cursor.forEach(doc { /* 写入导出文件 */ });用游标一条一条处理好过在内存里拼一个巨大的数组这点很重要。4. 数据量到了“连 keyset 都不太对劲”的极端场景4.1 先评估你的“大数据量”到底有多大很多文章说“几十万条数据就不要用 skip”其实有点绝对了。如果集合里就几万条记录翻到第 20 页也不会有明显问题如果集合有 5000 万条即便用 keyset 也要注意每次范围查询的过滤字段能不能高效命中索引。我会把分页优化的优先级画成一条渐进的路线数据量 1 万skip()完全可用别浪费时间。1 万 到 100 万优先用游标分页 复合索引性能提升明显。100 万 到 1000 万考虑按时间分区、按业务维度拆分集合再分页。上亿级此时分页已经不是主要问题。全局的数据检索靠搜索引擎少折腾 MongoDB 的深分页。4.2 按时间分桶把“百万页”变成“多个小列表”如果数据是持续增长的日志、订单流水它天然有时间属性。一个很有效的方法是“分桶”按天 / 按月建集合比如orders_2024_01、orders_2024_02。分页时先定位到所在的桶再在桶内做 keyset 分页。跨桶查询时按时间先后合并各桶结果。这个方案让单个集合始终不会太大。哪怕整体数据量是 1 亿拆成 24 个桶后每个桶不到 500 万条配合索引分页速度和刚上线时没什么区别。缺点是跨桶分页的“跳页”变得更难了。但应对大数据量的业务本来就要接受顺序浏览这是某种意义上的取舍。4.3 不要为了“加快分页”而放弃聚合框架MongoDB 聚合框架的$skip和$limit走的是同一套底层逻辑聚合里的$skip照样会带来深分页性能问题。很多人以为改成聚合就“优化”了结果只是把慢查询从 find 换成了 db.collection.aggregate耗时不变。聚合框架真正用来做大数据量分析的场景是先$match把范围缩小再$group做汇总最后$sort取一个较小区间的 Top N。如果你是在聚合里对整个集合$sort $skip那就和find().sort().skip()一样低效必须用前面的索引和 keyset 思路重构。4.4 作为最后手段的“前 N 页缓存”如果产品无法接受任何限制并且业务方就是要在百万级数据上无限跳页那唯一现实的做法是把热度高的前 N 页比如前 5000 页的结果缓存起来。当用户访问的页码对应缓存命中时直接返回超出缓存范围时强制走有序查询并提示“该页数据量大加载可能需要几秒”。这个方案不解决根本问题但它能贴合真实用户行为。因为绝大多数用户只会翻前几十页没有人真的会翻到第 100 万页。5. 实践中的避坑清单这些都是真实环境里踩过的坑5.1 排序字段有大量重复值时第一页和第二页会重复数据有一次我们用sort({ status: 1 })做 keyset 分页因为status只有“待处理”“已完成”“已取消”三类导致大量文档拥有相同的status值。上一页取到的status作为下一页$gt条件结果是第二页直接跳过了上一页同值的剩下数据第三页又开始重复展示。解决办法就是引入“次级排序键”。凡是非唯一排序字段一律在后面追加_id让排序值变成唯一组合。这也是为什么几乎每张表的 keyset 分页都建议用{ 业务字段, _id }索引。5.2_id不是自增数字别拿它当页码很多从关系型数据库转过来的开发者习惯用主键 ID 做范围分页然后默认_id是连续的 1、2、3。但 MongoDB 默认_id是 ObjectId它大致按时间递增却并不保证连续而且如果你手动指定过_id可能完全跳跃。用_id做分页键没问题但不要拿它去算“第几页”只能作为“上一页位置”的游标。另外ObjectId 的前 4 个字节本身就是时间戳按_id倒序来分页天然等价于“按插入时间倒序”。如果你恰好要在“刚插入的数据排前面”的场景直接用_id就够了不用额外维护createdAt。5.3 删除数据后页数总数和列表不一致这个问题不只在 MongoDB 里出现所有数据库都有用户在翻页的过程中后台可能有数据被删除导致跳页时出现“页数对不上”、“某一页只有 5 条”。对策是列表页只显示“上一页 / 下一页”不显示总页数必须显示页数时以进入列表那一刻的快照总数为准。拿到用户请求的页码后重新按最新条件 count 一遍如果小于目标页码就回退到最后一页。5.4 游标分页的“游标”不要暴露原始 ID如果你把上一页最后一条的_id直接放进 URL 传给前端用户可以很容易绕过你设计的过滤条件比如传一个不属于他权限范围的 ID。更稳妥的做法是给游标做一个编码例如把{ lastId, createdAt, token }序列化后加签名服务端每次解析后校验签名同时再校验该记录是否仍满足业务过滤条件。这种细节在大数据量场景下被忽视等出了问题往往就是安全漏洞。5.5 别忘记“单个文档巨大”这个隐形坑分页优化的重点虽然常被放在skip()和索引上但列表接口还有一种常见慢来源返回了太多大字段。一条订单记录带一个大附件备注字段200 字节可能变成 2MB。翻页时虽然扫描的文档数少了但网络传输照样卡。在处理大数据量分页时建议尽量做字段投影db.orders.find(filter, { title: 1, createdAt: 1, _id: 1 }) .sort({ createdAt: -1, _id: -1 }) .limit(20);如果必须展示大字段也要在点赞、备注这种“详情”接口再单独查询。列表页只负责轻量展示。6. 选型决策清单下次做分页前先对着表看一眼我把上面的思路整理成一张决策参考表直接对着业务场景选方案就行。业务场景推荐方案核心动作数据量几万条后台管理skip()够用不折腾后台管理数据量大但允许看前 N 页限制页码 keyset限制跳转其余用游标信息流、消息记录顺序浏览keyset 分页记住上一页最后一条排序键数据量千万级时间属性强按时间分桶 keyset拆集合桶内分页需要自由跳页数据量极大前 N 页缓存 索引兜底缓存热点接受部分慢查询导出全部数据游标遍历别用分页接口真实项目中我很少看到“一种方案走天下”的分页系统。合理的做法是根据不同的接口和数据特征选择不同策略热门接口热烈用 keyset冷门后台用有限skip()导出用游标。这样既保证用户体验又不会为了极端场景付出过高维护成本。最后再分享一个实战小技巧不管用哪种方案在索引建好、代码上线前一定先用explain(executionStats)实测一下totalDocsExamined和totalKeysExamined。这两个值越接近返回条数说明你的查询越是“指哪打哪”。如果totalDocsExamined比docsReturned高出三个数量级哪怕接口响应时间暂时能接受迟早都会在数据量翻倍后变成灾难。把分页优化的功夫花在降低扫描量上比单纯调超时时间可靠得多。

相关新闻

游戏引擎对象与资源管理:生命周期、异步加载与内存优化

游戏引擎对象与资源管理:生命周期、异步加载与内存优化

做到第四篇,终于要碰引擎里最脏最累的两块地方:游戏对象和资源管理。说它们脏,是因为这两套东西不像渲染或者物理那样有清晰的理论边界,更像是一堆约定、引用和回调堆出来的软逻辑;说它们累,是因为线上事故…

2026/10/13 22:40:08 阅读更多 →
Python实现HTTPS通信:TLS/SSL原理、证书验证与实战指南

Python实现HTTPS通信:TLS/SSL原理、证书验证与实战指南

1. HTTPS通信的核心机制与Python实现思路1.1 一次HTTPS请求在底层到底做了什么先别急着写代码。要真正把HTTPS通信玩明白,需要先理解HTTPS和HTTP的区别。HTTP是明文传输,数据在网络上相当于“裸奔”,任何能触碰到链路的节点都能看到请求内容。…

2026/10/13 22:39:08 阅读更多 →
Split Bregman算法MATLAB实现:TV去噪与压缩感知

Split Bregman算法MATLAB实现:TV去噪与压缩感知

简介:Split Bregman 算法是一种常用于图像去噪、复原与压缩感知的优化方法,这份MATLAB源码资源完整呈现其核心实现,适合有一定MATLAB与优化基础的研究人员、工程师及研究生参考学习。压缩包共11个文件,以m源码文件为主&#xff0c…

2026/10/13 22:39:08 阅读更多 →

最新新闻

GNN图神经网络实战:从GCN到GAT、GraphSAGE、GIN的选型与避坑指南

GNN图神经网络实战:从GCN到GAT、GraphSAGE、GIN的选型与避坑指南

简介:这份资源是一套面向深度学习学习者与算法工程师的图神经网络系统课件,共100页PPT,适合希望从零建立GNN知识框架、或需要梳理GCN及其变体脉络的读者。内容按四部分展开:先讲为何需要GNN,对比欧式与非欧式数据的差异…

2026/10/13 23:45:36 阅读更多 →
端侧AI样机验收的五个维度:超维方程实战指南

端侧AI样机验收的五个维度:超维方程实战指南

端侧AI样机验收,最害人的一句话就是"能跑"。很多项目在样机阶段跑了个Demo、拍了个视频、汇报一个平均帧率,就欢天喜地进入量产,结果用户一上手,内存被杀、发热降频、画面抖动、长时间使用越来越卡,最后全部…

2026/10/13 23:45:36 阅读更多 →
卫宁电子病历表结构拆解:从三层数据模型到SQL查询实践

卫宁电子病历表结构拆解:从三层数据模型到SQL查询实践

简介:一份面向医院信息科、系统集成商与医疗信息化技术人员的电子病历表结构文档,完整覆盖卫宁EMR中800多张数据表的定义,包括职工代码库、医疗项目库、药品分类库、科室/病区代码库、收费项目库、医保分类库、诊断代码库等关键数据域。每张表…

2026/10/13 23:45:36 阅读更多 →
agent-skills:为LLM代理打造可复用的技能仓库

agent-skills:为LLM代理打造可复用的技能仓库

个项目名字很朴素,就叫“agent-skills”。但我实际把它当成了一套给 AI 代理用的“技能仓库”:模型负责理解人话,仓库负责把理解变成可执行的动作。上个月我重构了自己手头一个答疑机器人,把里面天女散花一样塞在提示词里的小工具…

2026/10/13 23:45:36 阅读更多 →
从零打通统一大模型网关:用litellm托管所有LLM调用

从零打通统一大模型网关:用litellm托管所有LLM调用

从零打通统一大模型网关:我用 litellm 托管了项目里所有 LLM 调用先抛一个场景:你负责一个需要同时对接多个大模型 API 的项目,早先用的是 OpenAI 格式,后来加上 Claude 兼容接口,又接了开源模型的内网服务&#xff0c…

2026/10/13 23:45:36 阅读更多 →
多Agent协作为什么容易互相“甩锅”?从任务分配到结果验证设计协作工作流

多Agent协作为什么容易互相“甩锅”?从任务分配到结果验证设计协作工作流

一、引言:多 Agent 系统里的"责任真空" 把单 Agent 拆成 Planner、Retriever、Coder、Reviewer、Executor 之后,很多团队会遇到一个反直觉的现象:**系统的单点能力提升了,端到端的成功率反而下降,而且失败后…

2026/10/13 23:44:36 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目,我最大的感受是,大部分坑不是出在技术前沿,而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节,最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了,很难不碰到舵机控制这道坎。做智能小车要转方向,做机械臂要抬关节,做云台要偏航俯仰,背后都是同一个东西在干活:舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”?——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台?或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜?又或者,在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

流感时间序列预测实战: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 阅读更多 →