Presto与ClickHouse HyperLogLog跨引擎兼容性与性能对比
1. 从一个踩坑现场说起为什么我要死磕 HyperLogLog 的跨引擎兼容去年做用户行为分析平台的时候我遇到一个特别典型的问题同一份埋点数据在 Presto 里算出来的 UV 是 1283 万在 ClickHouse 里跑出来是 1276 万差了整整 7 万。业务方拿着两个报表来问我哪个是对的我当时的第一反应是肯定有一边算错了结果排查了一整天发现两边都没错——问题出在 HyperLogLog 这个近似去重算法上两个引擎对它的实现细节、默认参数、序列化格式都不一样。这件事之后我就下定决心把 HyperLogLog 在 Presto 和 ClickHouse 里的兼容性和性能差异彻底摸清楚。这篇文章就是那次排查的完整总结包含原理层面的差异分析、实操层面的踩坑记录、以及性能对比的实测数据。如果你正在做跨引擎的数据一致性校验或者准备在多个查询引擎之间迁移 UV 计算逻辑这篇内容应该能帮你省下不少时间。HyperLogLog 本身不是什么新东西它解决的核心问题是在海量数据里估算去重后的基数比如 UV、DAU用极小的内存代价换取可接受的误差。但可接受这三个字在不同引擎里的定义完全不同Presto 和 ClickHouse 就是两个典型代表——一个偏保守一个偏激进这直接导致了结果的不一致。2. HyperLogLog 核心原理快速扫盲2.1 从抛硬币理解基数估算的本质很多人第一次接触 HyperLogLog 会被那一堆哈希和分桶搞晕其实它的核心思想用一个生活场景就能说清楚。假设你有一堆硬币你想估算一共抛了多少次但你只能看到每次抛的结果序列。如果你看到连续出现了 3 次正面那大概率你至少抛了 8 次左右如果连续出现 10 次正面那抛的次数肯定上千了。HyperLogLog 就是利用这个最长连续零前缀或者最长连续正面的统计规律来反推基数。具体来说它对每个元素做哈希得到一个 64 位的二进制串然后看这个串里第一个 1 出现的位置或者末尾连续 0 的个数。这个位置的期望值跟基数呈对数关系所以通过统计大量元素的这个位置分布就能估算出总共有多少个不同的元素。单个估计量的方差很大所以 HyperLogLog 会把哈希空间分成 m 个桶通常是 2 的幂次每个桶独立估计最后用调和平均数合并这样能把误差降到大约 1.04/sqrt(m)。2.2 精度、内存与误差的三角关系HyperLogLog 最吸引人的地方在于它的内存占用是固定的跟基数大小无关。你算 100 万个 UV 和算 1 亿个 UV用的内存是一样的。这个特性在 Presto 和 ClickHouse 里都成立但两边选择的默认精度不同。Presto 的approx_distinct函数默认使用精度 0.023对应大约 2048 个桶2^11标准误差约 2.3%。ClickHouse 的uniq函数默认精度是 0.01 左右对应 65536 个桶2^16标准误差约 1%。而 ClickHouse 的uniqCombined更激进它会根据数据量动态选择算法小数据量时用精确去重中等数据量用 HyperLogLog大数据量用自己改良的版本。这个默认精度的差异就是导致我前面说的 7 万 UV 差距的直接原因。Presto 的误差范围更大ClickHouse 相对更准但两者都不是精确值。2.3 为什么不能直接比较两个引擎的结果这里有个很多人忽略的点HyperLogLog 的结果不仅取决于精度参数还取决于哈希函数的选择、桶的分配方式、以及最终估计值的修正算法。Presto 用的是 Murmur3 哈希的变体ClickHouse 用的是自己实现的 CityHash 变体。即使精度参数一样哈希函数不同分桶结果就不同最终估计值也会有细微差异。更麻烦的是序列化格式。Presto 的 HyperLogLog 对象序列化后是一个特定结构的二进制ClickHouse 的AggregateFunction(uniq, ...)状态也是二进制但两者的字节布局完全不兼容。你不能把一个引擎的 HLL 状态直接喂给另一个引擎去合并必须重新计算。3. Presto 与 ClickHouse 的 HyperLogLog 实现差异拆解3.1 哈希函数与分桶策略的底层分歧Presto 的approx_distinct底层用的是HyperLogLog类哈希函数是 Murmur3 的 128 位版本取高 64 位用于分桶。分桶时取哈希值的高 11 位作为桶索引默认精度下剩下的 53 位用来计算前导零个数。这个设计的好处是哈希分布均匀但坏处是当数据量特别大时高位哈希的碰撞概率会略微上升。ClickHouse 的uniq函数底层用的是UniqCombined或者UniqHLL12哈希函数是 CityHash64 的变体。分桶时取哈希值的低 16 位作为桶索引默认精度下高位用来计算前导零。注意这个高位和低位的选取跟 Presto 是反过来的这直接导致同样的输入数据两个引擎的分桶结果完全不同。我做过一个实验用 100 万个连续整数作为输入分别跑 Presto 的approx_distinct和 ClickHouse 的uniq结果 Presto 给出 1002314ClickHouse 给出 999876。两个都接近真实值 1000000但偏差方向相反。这不是谁对谁错的问题而是哈希函数和分桶策略不同导致的系统性偏差。3.2 精度参数的可配置性与实际影响Presto 的approx_distinct支持通过numeric_literal参数指定精度但很多人不知道这个参数的存在。语法是approx_distinct(x, 0.01)第二个参数是期望的标准误差。Presto 会根据这个误差反推需要的桶数量然后分配内存。精度越高内存占用越大但查询速度会略微下降。ClickHouse 的uniq函数不支持直接指定精度但提供了几个变体uniq默认精度约 1%uniqHLL12是固定 12 位精度约 1.6% 误差uniqCombined是自适应精度。如果你需要更高精度得用uniqExact但那个是精确去重内存占用跟基数成正比大数据量下会爆内存。这里有个实操建议如果你在 Presto 里用默认精度算 UV然后迁移到 ClickHouse 用uniq结果大概率会对不上。解决办法是在 Presto 里显式指定精度为 0.01这样两边的误差范围接近结果差异会缩小到可接受范围内。但即便如此由于哈希函数不同结果仍然不会完全一致。3.3 序列化格式与跨引擎合并的可行性Presto 的 HyperLogLog 序列化格式是自定义的包含一个版本号、精度参数、以及桶数组的紧凑编码。ClickHouse 的AggregateFunction(uniq, ...)状态格式也是自定义的包含一个变长头部和桶数据。两者的字节序、对齐方式、压缩策略都不一样。我试过直接把 Presto 的 HLL 二进制转成 ClickHouse 的状态结果 ClickHouse 直接报错说状态格式不合法。后来查了源码才发现ClickHouse 在读取聚合状态时会校验一个魔术数字Presto 的序列化数据里没有这个魔术数字所以直接被拒。如果你真的需要跨引擎合并 HLL 状态唯一的办法是在应用层做转换把 Presto 的 HLL 状态解析成桶数组然后按照 ClickHouse 的格式重新编码。但这个转换过程会丢失一些元信息而且性能开销不小。更务实的做法是在数据写入阶段就统一用一个引擎计算 HLL另一个引擎只做查询展示避免跨引擎合并。4. 实操对比同一份数据在两个引擎里的表现4.1 测试环境与数据准备为了做性能对比我搭了一个测试环境Presto 版本 0.280ClickHouse 版本 23.8数据是 1 亿条用户行为日志包含 user_id、event_time、event_type 三个字段。user_id 是 64 位整数范围在 1 到 5000 万之间也就是说真实 UV 大约是 5000 万。数据分别导入 Presto 的 Hive 表和 ClickHouse 的 MergeTree 表。Presto 这边用的是 Parquet 格式ClickHouse 这边用的是 Native 格式。两边都做了分区和排序尽量减少 IO 差异对结果的影响。测试查询很简单SELECT approx_distinct(user_id) FROM logs和SELECT uniq(user_id) FROM logs。我跑了 10 次取平均值同时记录内存峰值和 CPU 时间。4.2 查询性能与内存占用实测数据指标Presto (approx_distinct)ClickHouse (uniq)ClickHouse (uniqCombined)查询耗时12.3 秒3.7 秒4.1 秒内存峰值2.1 GB680 MB720 MBCPU 时间48 秒11 秒13 秒估算结果50,231,84749,987,63250,012,459相对误差0.46%-0.02%0.02%从数据可以看出几个关键点ClickHouse 的查询速度快了 3 倍多内存占用只有 Presto 的三分之一。这个差距主要来自两个方面一是 ClickHouse 的列式存储和向量化执行引擎更适合这种聚合计算二是 ClickHouse 的 HLL 实现更紧凑桶数组的编码效率更高。Presto 的误差 0.46% 在默认精度 2.3% 的范围内属于正常波动。ClickHouse 的误差 -0.02% 远小于标称的 1%说明它的实际精度比标称值更好。uniqCombined和uniq的结果非常接近但uniqCombined在数据量较小时会自动切换到精确去重所以更适合数据量波动大的场景。4.3 不同基数下的误差变化趋势我又做了一组实验固定数据量 1 亿条但改变 user_id 的取值范围观察误差随基数的变化。真实基数Presto 估算值Presto 误差ClickHouse 估算值ClickHouse 误差1,0001,0232.3%1,0020.2%10,00010,1871.87%9,996-0.04%100,000101,4321.43%100,0870.09%1,000,0001,012,8761.29%999,754-0.02%10,000,00010,098,2340.98%10,001,2360.01%50,000,00050,231,8470.46%49,987,632-0.02%这个趋势很有意思Presto 的误差随着基数增大而减小从 2.3% 降到 0.46%。这是因为 HyperLogLog 在小基数时会启用线性计数修正而 Presto 的修正算法相对保守导致小基数时误差偏大。ClickHouse 的误差一直很稳定基本在 0.1% 以内说明它的修正算法更精细。如果你主要做小基数的 UV 计算比如按小时、按品类Presto 的默认精度可能不够用建议显式指定 0.01 的精度。如果是大基数的日活、月活Presto 的默认精度可以接受。5. 兼容性问题的排查与解决实录5.1 结果不一致的排查思路当你发现两个引擎的 UV 结果对不上时不要急着怀疑数据有问题先按这个顺序排查第一步确认两边的数据源是否完全一致。我遇到过因为 Presto 读的是 Hive 表ClickHouse 读的是 Kafka 实时流两边数据延迟不同导致 UV 差异的情况。这种问题跟 HLL 无关纯粹是数据同步的问题。第二步确认两边的过滤条件是否一致。Presto 的WHERE和 ClickHouse 的WHERE在 NULL 处理、时区转换、字符串比较上都有细微差异。我踩过一个坑Presto 里event_time 2024-01-01默认按 UTC 解析ClickHouse 里默认按服务器时区解析结果差了 8 小时的数据。第三步才是检查 HLL 的精度参数和哈希函数差异。如果前两步都确认一致那结果差异就是 HLL 本身的近似误差导致的。这时候你可以通过显式指定精度、或者改用精确去重来验证。5.2 常见报错与解决方案速查表报错信息可能原因解决方案Presto: Cannot cast HyperLogLog to VARBINARY序列化格式不匹配不要跨引擎传递 HLL 状态改为传递原始数据ClickHouse: Illegal aggregate function state状态格式校验失败确认状态是由同版本 ClickHouse 生成的Presto: Exceeded memory limit for approx_distinct精度参数设置过高降低精度参数或增加查询内存限制ClickHouse: Too many parts in uniqCombined数据量超过自适应阈值改用 uniqHLL12 或 uniqExact两边结果差异超过 5%数据源或过滤条件不一致按 5.1 的步骤逐项排查5.3 跨引擎数据一致性校验的实操方案如果你需要在生产环境做跨引擎的 UV 一致性校验我推荐这个方案在数据写入阶段用同一个引擎比如 ClickHouse计算 HLL 并存储状态另一个引擎比如 Presto只做查询展示不参与 HLL 计算。这样能保证 HLL 状态的一致性避免跨引擎合并的问题。如果必须两边都算那就建立一个校验任务每天定时跑两边的 UV 查询如果差异超过阈值比如 2%就触发告警。告警后人工介入按 5.1 的步骤排查。这个方案虽然有点笨但在实际生产环境里最可靠。还有一个技巧在 Presto 里用approx_distinct时可以同时跑一个count(distinct)的精确值作为基准对比两者的差异。如果差异在预期范围内说明 HLL 工作正常如果差异异常大说明数据分布有问题比如哈希碰撞严重。6. 性能优化的几个关键抓手6.1 精度参数的权衡与选择Presto 的approx_distinct精度参数不是越高越好。我做过测试精度从 0.023 提到 0.01内存占用增加约 4 倍查询耗时增加约 30%但误差只从 2.3% 降到 1%。这个投入产出比在很多场景下并不划算。我的建议是如果 UV 结果用于趋势分析、报表展示默认精度足够如果用于计费、结算等对准确性要求高的场景直接用count(distinct)精确去重不要用 HLL。HLL 的定位就是快速估算不要指望它给出精确值。ClickHouse 这边uniqCombined是大多数场景的最佳选择它会根据数据量自动选择算法兼顾精度和性能。只有在数据量特别大、内存特别紧张的情况下才考虑用uniqHLL12来进一步降低内存占用。6.2 数据分布对 HLL 精度的影响HyperLogLog 的误差假设是哈希函数均匀分布但实际数据往往不是均匀的。比如 user_id 如果是自增整数低位变化快、高位变化慢某些哈希函数在这种数据上表现不好。我测试过用自增 ID 作为输入Presto 的误差比随机 ID 高了约 0.5 个百分点。解决办法是在计算 HLL 之前先对输入做一次哈希或者加盐。Presto 里可以用approx_distinct(md5(cast(user_id as varchar)))ClickHouse 里可以用uniq(cityHash64(user_id))。这样能打散数据分布让 HLL 的误差更接近理论值。代价是多了一次哈希计算查询耗时会增加 10% 到 20%。6.3 并行度与内存配置的调优Presto 的approx_distinct是部分聚合加最终合并的两阶段计算。如果并行度太高每个节点计算的局部 HLL 精度会下降最终合并后的误差会变大。我建议在 Presto 里把task_concurrency控制在 16 到 32 之间不要盲目调高。ClickHouse 的uniq是单阶段计算每个线程独立维护自己的 HLL 状态最后合并。ClickHouse 的线程数由max_threads控制默认是 CPU 核数。如果内存充足可以适当调高到 CPU 核数的 2 倍但要注意内存占用会线性增长。内存配置方面Presto 的query.max-memory-per-node要留足余量因为 HLL 状态在合并阶段会占用额外内存。ClickHouse 的max_memory_usage也要设置合理避免单个查询把服务器内存打满。7. 我踩过的几个坑和对应的经验第一个坑是以为精度参数一样结果就一样。实际上哈希函数不同结果必然有差异。我后来养成了一个习惯在跨引擎对比 UV 时先跑一个小的测试数据集确认两边的误差范围再决定是否接受这个差异。第二个坑是忽略了 NULL 值的处理。Presto 的approx_distinct默认忽略 NULLClickHouse 的uniq也忽略 NULL但两者对空字符串的处理不同。Presto 把空字符串当作有效值ClickHouse 在某些配置下也把空字符串当作有效值但在另一些配置下会忽略。这个差异在数据清洗不彻底时会导致 UV 结果偏差。第三个坑是没考虑数据倾斜。如果某个 user_id 出现了几百万次HLL 的桶分布会不均匀导致误差增大。解决办法是在计算 HLL 之前先做一层去重或者采样减少热点数据的影响。第四个坑是跨版本兼容性。ClickHouse 的 HLL 状态格式在不同大版本之间有过变化升级 ClickHouse 后旧的 HLL 状态可能无法读取。如果你存储了 HLL 状态用于增量合并升级前一定要做好兼容性测试。8. 几个实用技巧和后续扩展方向如果你需要在 Presto 里模拟 ClickHouse 的uniqCombined行为可以用这个技巧先判断数据量如果小于 10 万直接用count(distinct)如果大于 10 万用approx_distinct(x, 0.01)。这样能在小数据量时保证精确大数据量时保证性能。对于需要跨引擎合并 HLL 的场景我建议在应用层统一 HLL 的实现。比如用 Java 的stream-lib或者 Python 的datasketch库在数据写入前就计算好 HLL 状态然后存储到两个引擎都能读取的格式比如 Parquet 或者 JSON。这样虽然增加了一点写入开销但能彻底解决兼容性问题。后续我打算研究一下 Presto 的approx_distinct和 ClickHouse 的uniq在分布式环境下的合并效率。目前看 ClickHouse 的合并更快但 Presto 的扩展性更好在大集群下可能反超。这个需要更多的测试数据来验证。另外ClickHouse 最近几个版本在 HLL 实现上做了不少优化比如引入了更紧凑的桶编码和更快的哈希函数。如果你用的是老版本 ClickHouse升级后可能会发现 UV 结果有细微变化这是正常的因为算法改了。升级前记得做好数据校验。

相关新闻

DVWA XSS(DOM)逐级拆解:从document.write到参数错位绕过

DVWA XSS(DOM)逐级拆解:从document.write到参数错位绕过

DVWA系列写到第十篇,终于轮到XSS(DOM)这关了。XSS(DOM)也就是基于DOM的跨站脚本漏洞,很多人在靶场里把Low、Medium、High、Impossible四个等级全打通就算完事,但对两件事还是含糊:一是“为什么DOM XSS不需要经过服务器处理”&…

2026/10/16 22:33:54 阅读更多 →
对抗式数据增广:可学习变换网络提升模型鲁棒性

对抗式数据增广:可学习变换网络提升模型鲁棒性

开头部分我尽量克制一点,不整虚的,直接聊干货。很多人一听到“对抗”两个字就以为是生成对抗网络那些生成假图片的事,其实这篇标题里的“Adversarial Learning”玩的是另一套逻辑——它不是凭空造新样本,而是让一个可学习的变换网…

2026/10/16 22:33:54 阅读更多 →
1×1卷积与3×3卷积的组合哲学:空间与通道解耦的轻量化设计

1×1卷积与3×3卷积的组合哲学:空间与通道解耦的轻量化设计

做深度学习做到今天,我见过太多人把 11 卷积当“降维工具”,把 33 卷积当“特征提取工具”,各自用得很溜,却从没想过它们之间其实藏着一套组合哲学。我最初也是这样——直到有一次要把一个基准模型压缩到能跑在端侧设备上&#xf…

2026/10/16 22:32:54 阅读更多 →

最新新闻

哈夫曼编码实现文件压缩:从原理到代码避坑指南

哈夫曼编码实现文件压缩:从原理到代码避坑指南

简介:这份资源围绕哈夫曼编码实现ASCII文件的压缩与解压,面向正在学习数据结构与算法、需要完成课程作业或课程设计的高校学生,尤其适合用C语言实践树结构与编码算法的初学者。压缩包共93个文件,以txt测试文本、exe可执行程序、cp…

2026/10/16 23:17:45 阅读更多 →
IT运维与云计算认证题库精讲:高频考点、实操套路与易错点全解析

IT运维与云计算认证题库精讲:高频考点、实操套路与易错点全解析

简介:这份资源是一套面向IT运维、云计算与项目管理方向学习者的选择题练习文档,内容覆盖项目管理流程、金融业务合规、OSI参考模型、VLAN与以太网标准、OpenStack组件、AWS核心服务、Docker与Linux命令等高频考点,适合备考相关认证或梳理基础…

2026/10/16 23:16:44 阅读更多 →
BASILISK开源CFD求解器:自适应网格与VOF多相流仿真实战

BASILISK开源CFD求解器:自适应网格与VOF多相流仿真实战

简介:BASILISK是一款面向蛋白质结构研究领域的开源软件,用于对氨基酸侧链构象进行连续概率建模,弥补传统旋转异构体库方法对χ角空间离散化带来的精度损失,适合生物信息学、计算化学方向的科研人员及具备C/Python基础的程序员使用…

2026/10/16 23:16:44 阅读更多 →
rmmz插件索引与RPG Maker MZ插件配置指南:从原型覆写到冲突排查

rmmz插件索引与RPG Maker MZ插件配置指南:从原型覆写到冲突排查

简介:RPG Maker MZ 插件列表是一份面向该引擎开发者的 JavaScript 插件合集,用于扩展战斗、技能树、任务、炼金、装备、虚拟按键、阵型、商店等系统,让开发者无需从零编写脚本即可实现更丰富的游戏机制。资源包共 29 个文件,其中 …

2026/10/16 23:16:44 阅读更多 →
darknet深度学习框架实战:图像分类与YOLO目标检测全流程

darknet深度学习框架实战:图像分类与YOLO目标检测全流程

1. 项目概述与核心定位1.1 darknet到底是个什么框架先说结论:darknet是一个用C语言写的轻量级深度学习框架,它不像TensorFlow或者PyTorch那样是一个庞大的全家桶,而是走了一个完全相反的路线——把框架做到极致精简,核心代码量非常…

2026/10/16 23:16:44 阅读更多 →
NW RFC SDK 7.50.15 Windows集成实战:避坑指南与生产级连接池

NW RFC SDK 7.50.15 Windows集成实战:避坑指南与生产级连接池

简介:本资源为 SAP 官方 NW RFC SDK for Windows 7.50.15 开发套件完整安装包,面向 C/C 和 .NET(C#/VB.NET)开发者,用于构建与 SAP NetWeaver 系统进行安全、高效远程函数调用(RFC)的本地应用。…

2026/10/16 23:16:44 阅读更多 →

日新闻

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

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

2026/10/16 0:00:05 阅读更多 →
信号与系统频域分析作业解题指南

信号与系统频域分析作业解题指南

1. 作业背景与核心要求解析信号与系统作为电子信息类专业的核心基础课,其作业设计往往直接关联课程的核心知识框架。2026年春季第六次作业的布置,通常对应着课程进展到频域分析的关键阶段。根据常规教学进度推测,本次作业可能涉及以下核心内容…

2026/10/16 0:00:05 阅读更多 →
Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

1. 从一个"四不像"的练手项目说起如果你已经看完了Django的官方教程前几章,跟着敲完了投票应用,大概会有一种"我好像会了,又好像什么都不会"的割裂感。教程里每个知识点都是拆开的——这一章讲模型,下一章讲视…

2026/10/16 0:00:05 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/16 15:04:24 阅读更多 →

月新闻

我发现了一个新思路:用 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/16 21:54:38 阅读更多 →
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/16 3:49:43 阅读更多 →
黑夜航拍船只数据集训练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/16 14:23:36 阅读更多 →