<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>huizhou92&#39;s Blog</title>
        <link>https://huizhou92.com/zh-cn/</link>
        <description>Recent content on huizhou92&#39;s Blog</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>zh-cn</language>
        <copyright>Copyright © 2023 huizhou92</copyright>
        <lastBuildDate>Mon, 02 Mar 2026 23:01:20 +0800</lastBuildDate><atom:link href="https://huizhou92.com/zh-cn/index.xml" rel="self" type="application/rss+xml" /><item>
        <title>为什么你的 AI Agent 总在遗忘：一套面向个人用户的实用记忆架构</title>
        <link>https://huizhou92.com/zh-cn/p/%E4%B8%BA%E4%BB%80%E4%B9%88%E4%BD%A0%E7%9A%84-ai-agent-%E6%80%BB%E5%9C%A8%E9%81%97%E5%BF%98%E4%B8%80%E5%A5%97%E9%9D%A2%E5%90%91%E4%B8%AA%E4%BA%BA%E7%94%A8%E6%88%B7%E7%9A%84%E5%AE%9E%E7%94%A8%E8%AE%B0%E5%BF%86%E6%9E%B6%E6%9E%84/</link>
        <pubDate>Mon, 02 Mar 2026 23:01:20 +0800</pubDate>
        
        <guid>https://huizhou92.com/zh-cn/p/%E4%B8%BA%E4%BB%80%E4%B9%88%E4%BD%A0%E7%9A%84-ai-agent-%E6%80%BB%E5%9C%A8%E9%81%97%E5%BF%98%E4%B8%80%E5%A5%97%E9%9D%A2%E5%90%91%E4%B8%AA%E4%BA%BA%E7%94%A8%E6%88%B7%E7%9A%84%E5%AE%9E%E7%94%A8%E8%AE%B0%E5%BF%86%E6%9E%B6%E6%9E%84/</guid>
        <description>&lt;img src="https://images.hxzhouh.com/blog-images/2026/03/8b8971ca1398b1df35a88d5ba9c7934a.png" alt="Featured image of post 为什么你的 AI Agent 总在遗忘：一套面向个人用户的实用记忆架构" /&gt;&lt;h3 id=&#34;问题为什么你的-agent-用久了反而变笨了&#34;&gt;问题：为什么你的 Agent 用久了反而「变笨」了？
&lt;/h3&gt;&lt;p&gt;想象一下这个场景：你和一个 AI Agent 协作了好几周，耐心地教它你的项目结构、个人偏好和工作流程。一次对话中，它确实记住了你的指令——但第二天开新会话，就像见了个陌生人。更糟的是，它开始&lt;strong&gt;自信满满地编造从未发生过的事情&lt;/strong&gt;，反复问你同样的问题，把几小时前做出的关键决策忘得一干二净。&lt;/p&gt;
&lt;!-- more--&gt;
&lt;p&gt;如果你也遇到过这种情况，别急着怀疑 Agent 的智力——&lt;strong&gt;这是记忆系统设计的根本缺陷&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;大多数人以为 AI「有记忆」，是因为它能回忆对话中的早期内容。但实际发生的事情简单得多：Agent 用的是它的 &lt;strong&gt;Context Window&lt;/strong&gt;（上下文窗口），这只是一个临时工作区，不是永久存储。对话过长触发压缩、或者你开了一个新会话——工作区就被清空了。&lt;/p&gt;
&lt;p&gt;OpenClaw 试图解决这个问题，做法是将长期记忆持久化到本地 Markdown 文件（&lt;code&gt;MEMORY.md&lt;/code&gt;、每日日志等）。理论上，记忆可以跨越数月甚至数年；文件存在磁盘上，人类可读、可编辑、可 Git 版本控制——听起来很完美。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;然而大量用户反馈：Agent 随着时间推移变得越来越&amp;quot;痴呆&amp;quot;。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;根因可以归为三个层面：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;压缩引发的&amp;quot;摘要失忆&amp;quot;&lt;/strong&gt;：当 Context Window 接近上限（比如 Claude Opus 4.6 的 200K token 边界），OpenClaw 会自动触发压缩——让模型把早期对话「摘要」成更短的版本，然后丢弃原始历史。关键细节就在这个翻译过程中丢失了。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;检索失败——&amp;ldquo;记住了却想不起来&amp;rdquo;&lt;/strong&gt;：重要信息确实写进了磁盘（&lt;code&gt;MEMORY.md&lt;/code&gt;、&lt;code&gt;daily/YYYY-MM-DD.md&lt;/code&gt;），但需要的时候依赖 &lt;code&gt;memory_search&lt;/code&gt; / &lt;code&gt;memory_get&lt;/code&gt; 工具去检索。漏检的原因包括：底层 Embedding 模型能力不足、纯语义搜索漏掉了关键词匹配、&lt;code&gt;MEMORY.md&lt;/code&gt; 随时间膨胀变得臃肿不堪。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;没有遗忘 + 没有冲突解决&lt;/strong&gt;：OpenClaw 几乎从不遗忘——文件只增不减。过时的偏好、废弃的项目决策、早期的错误指令全部堆积在一起。新信息到来时系统不会「更新」，只是追加。矛盾不断积累，检索时噪声稀释了信号。&lt;strong&gt;最终 Agent 变得困惑，开始编故事来调和相互矛盾的&amp;quot;事实&amp;quot;&lt;/strong&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&#34;https://images.hxzhouh.com/blog-images/2026/03/d44ca3ec9ba8f709634a5755489a4d53.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Context Window 与持久化记忆的关系&#34;
	
	
&gt;&lt;/p&gt;
&lt;h3 id=&#34;当前记忆系统的六大结构性缺陷&#34;&gt;当前记忆系统的六大结构性缺陷
&lt;/h3&gt;&lt;p&gt;OpenClaw 的默认记忆方案——&lt;strong&gt;Markdown 文件 + 向量搜索&lt;/strong&gt;——优势很明显：人类可读可写、可 Git 版本控制、零外部依赖。但我在实际使用中发现它有六个结构性弱点，而且每个都不是小问题：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. 扁平无差异&lt;/strong&gt;：一年前的闲聊和昨天的架构决策权重相同，搜索结果淹没在噪声中。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. 没有遗忘机制&lt;/strong&gt;：只追加不删除。记忆系统最终淹没自己——过时信息伪装成「事实」，污染当前决策。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. 没有自动整合&lt;/strong&gt;：重要洞察必须手动提炼和写入，Agent 永远不会主动「消化」今天发生了什么。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4. 没有时间推理&lt;/strong&gt;：Agent 知道「某事发生过」，但不知道那是 3 天前还是 3 个月前——无法判断信息是否已经过时。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5. 没有记忆提升&lt;/strong&gt;：埋在日志里的关键决策永远埋着，没有机制将它们提升到长期知识库。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6. 没有知识图谱&lt;/strong&gt;：无法表达关系性知识，比如「A 认识 B」或「项目 X 依赖工具 Y」。所有记忆都是孤立的扁平条目。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://images.hxzhouh.com/blog-images/2026/03/cb1fc4a1b90759dbfd5f329e89cdcd56.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;六大缺陷诊断图&#34;
	
	
&gt;&lt;/p&gt;
&lt;h3 id=&#34;学术界怎么说2026-年-2-月研究综述&#34;&gt;学术界怎么说？（2026 年 2 月研究综述）
&lt;/h3&gt;&lt;p&gt;2026 年 2 月，Agent 记忆突然成了学术界的热门战场——单月发表了 10+ 篇论文。其中一篇由 59 位作者联合撰写的综述论文提出了三维分类法。&lt;/p&gt;
&lt;p&gt;我挑几篇关键的说一下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;A-MEM&lt;/strong&gt;（NeurIPS 2025）：借鉴了 Zettelkasten 卡片笔记法——新记忆自动生成关键词和关联链接，构建互联知识网络。思路很有意思。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;xMemory&lt;/strong&gt;（ICML 2026）：将记忆解耦为语义组件，层次化组织，支持自顶向下检索，大幅降低检索噪声。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;BudgetMem&lt;/strong&gt;：三层预算结构 + RL Router，把检索资源优先分配给最重要的记忆。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;InfMem&lt;/strong&gt;：PreThink-Retrieve-Write 协议，让 Agent 在检索前先「想清楚要找什么」，准确率提升 10-12%。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;但更值得注意的是两个&lt;strong&gt;行业警告&lt;/strong&gt;：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Serial Collapse（序列崩溃）&lt;/strong&gt;（Dark Side of the Moon 论文）：Agent 可能逐渐退化到完全不使用记忆——即使记忆系统运行完好。记忆存在 ≠ Agent 会用。&lt;/p&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Memory Misevolution（记忆误进化）&lt;/strong&gt;（TAME 论文）：正常迭代过程中「有毒捷径」的积累——那些看起来高效但违反约束的记忆策略。&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;这两个警告在实践中其实很常见。你可能已经观察到了：Agent 有时候明明有记忆文件，却压根不去查。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://images.hxzhouh.com/blog-images/2026/03/523749e958151f2da7d8c8f77a2a493b.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Agent 记忆研究分类（2026 年 2 月）&#34;
	
	
&gt;&lt;/p&gt;
&lt;h3 id=&#34;解决方案三层记忆架构&#34;&gt;解决方案：三层记忆架构
&lt;/h3&gt;&lt;p&gt;综合以上研究和我自己的实践，下面是一套面向个人用户的&lt;strong&gt;确定性零成本架构&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;先贴一下我自己的记忆系统目录结构：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;root@ubuntu-4gb-openclaw:~/.openclaw/workspace/memory# tree
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── daily
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   └── 2026-03-01.md
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── episodic
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── hotspots_2026-02-18.md
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── hot-topics-2026-02-17.md
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── hot-trends-2026-02-18.md
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── INDEX.md
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── keywords_20260217_1600.json
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── lessons
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   ├── api-guide.md
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   ├── mistakes.md
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   └── system-deployment.md
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── NOW.md
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── people
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   └── 老板.md
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── rules
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   └── storage-routing.md
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── scripts
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   ├── archive-stale.sh
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   └── daily_digest.py
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;└── semantic
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    ├── golang-patterns.md
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    ├── writing-frameworks.md
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    └── writing-style-guide-huizhou92.md
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;我们逐层来看。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;短期层：&lt;code&gt;NOW.md&lt;/code&gt;（最被忽视的设计）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;NOW.md&lt;/code&gt; 是整个架构中&lt;strong&gt;信息密度最高&lt;/strong&gt;的文件。&lt;/p&gt;
&lt;p&gt;核心思想很简单：每次重启后，Agent 的第一个动作应该是读 &lt;code&gt;NOW.md&lt;/code&gt;，而不是去搜索庞大的记忆库。它是精华提取器，是「我上次做到哪了」的快速恢复工具，是压缩后的救生筏。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-markdown&#34; data-lang=&#34;markdown&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gh&#34;&gt;# NOW.md — 工作区
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gh&#34;&gt;&lt;/span&gt;更新时间: YYYY-MM-DD HH:MM
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gu&#34;&gt;## 今日重点
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gu&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;-&lt;/span&gt; ✅ 已完成项目
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gu&#34;&gt;## 进行中
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gu&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;-&lt;/span&gt; 🔄 未完成任务（包括阻塞项）
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gu&#34;&gt;## 当前优先级
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;gu&#34;&gt;&lt;/span&gt;| 优先级 | 任务 | 状态 |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|--------|------|------|
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;| P0 | 最重要的事 | 进行中 |
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;关键规则&lt;/strong&gt;：&lt;code&gt;NOW.md&lt;/code&gt; 是&lt;strong&gt;唯一允许覆写的文件&lt;/strong&gt;。所有其他记忆文件只能追加。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;中期层：每日日志&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;文件名：&lt;code&gt;memory/YYYY-MM-DD.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;写入模式：&lt;strong&gt;只追加，不覆写&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;格式建议：&lt;code&gt;### HH:MM — 事件标题 + 内容描述&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;原则：宁多勿少。日志是原材料，精华提炼交给夜间自动化。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;长期层：结构化子目录&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;知识文件采用统一的 YAML frontmatter 格式：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;title&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;文件标题&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;date&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;ld&#34;&gt;2026-02-28&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;category&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;lessons&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;priority&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;🔴         &lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c&#34;&gt;# 🔴 核心 | 🟡 重要 | 🟢 参考&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;active       &lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c&#34;&gt;# active | stale | superseded | conflict&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;last_verified&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;ld&#34;&gt;2026-02-28&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;tags&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;l&#34;&gt;相关标签]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;last_verified&lt;/code&gt; 超过 30 天的条目自动标记为 ⚠️ &lt;code&gt;stale&lt;/code&gt;，提醒人工复查。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;INDEX.md&lt;/code&gt;：知识库健康仪表盘&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-markdown&#34; data-lang=&#34;markdown&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;| 文件 | 优先级 | 状态 | 最后验证 | 描述 |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|------|--------|------|----------|------|
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;| tools | 🔴 | ✅ active | 2026-02-28 | 已验证的工具列表 |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;| api-guide | 🟡 | ⚠️ stale | 2026-01-10 | 可能过时，需要复查 |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;| old-workflow | ⚪ | &lt;span class=&#34;gd&#34;&gt;~~superseded~~&lt;/span&gt; | 2025-12-01 | 已被新方案替代 |
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Agent 启动时扫一眼 &lt;code&gt;INDEX.md&lt;/code&gt;，几秒钟内就能了解整个知识库的健康状况。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://images.hxzhouh.com/blog-images/2026/03/def61d6ad3edb44d3f17ebfef951f1f4.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;三层记忆架构&#34;
	
	
&gt;&lt;/p&gt;
&lt;h3 id=&#34;轻量知识图谱sqlite-三元组表&#34;&gt;轻量知识图谱：SQLite 三元组表
&lt;/h3&gt;&lt;p&gt;这是六大缺陷中最复杂的一个，也是纯 Markdown 无法突破的根本限制。&lt;/p&gt;
&lt;h4 id=&#34;为什么需要知识图谱&#34;&gt;为什么需要知识图谱？
&lt;/h4&gt;&lt;p&gt;Markdown 文件只能存储&lt;strong&gt;扁平事实&lt;/strong&gt;：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Brian 偏好简洁的沟通风格。项目 X 使用 PostgreSQL。工具 Y 需要 API Key。&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;但真实知识是有&lt;strong&gt;关系&lt;/strong&gt;的：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Brian → 负责 → 项目 X → 依赖 → 工具 Y → 需要 → API Key（存放在 secrets/）&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;当 Agent 被问到「Brian 的项目需要什么 API Key」时，纯向量搜索无法完成这个三跳推理。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;解决方案：一个 SQLite 三元组表。&lt;/strong&gt; 没有外部依赖，没有部署开销，完全能满足个人知识关系管理的需要：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;CREATE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;TABLE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;triples&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;subject&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;TEXT&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;-- 实体（如 &amp;#34;Brian&amp;#34;）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;predicate&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;TEXT&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;-- 关系（如 &amp;#34;负责&amp;#34;）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;object&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;TEXT&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;     &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;-- 对象（如 &amp;#34;Project Alpha&amp;#34;）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;added_date&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;TEXT&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;confidence&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;REAL&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;DEFAULT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;用 Python 或 Node 进行读写和查询就行。整个数据库就是一个 &lt;code&gt;.db&lt;/code&gt; 文件，和你的 Markdown 记忆放在一起——可移植、备份极其简单。&lt;/p&gt;
&lt;h4 id=&#34;什么时候该写入知识图谱&#34;&gt;什么时候该写入知识图谱
&lt;/h4&gt;&lt;p&gt;不是所有信息都值得入图。我的建议是只用于以下场景：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;系统依赖&lt;/strong&gt;：项目 A 依赖工具 B，工具 B 需要配置 C&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;资源位置&lt;/strong&gt;：凭证 / 文件 / API 存放在哪里&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;历史关联&lt;/strong&gt;：决策 X 的产生是因为事件 Y&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;日常事实（偏好、操作日志）留在 Markdown 里就好——别过度入图。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;file:///Users/hxzhouh/lobsterai/project/illustrations/ai-agent-memory-architecture/05-framework-knowledge-graph.png?lastModify=1772375639&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;知识图谱示例 — 多跳关系推理&#34;
	
	
&gt;&lt;/p&gt;
&lt;h3 id=&#34;遗忘机制主动遗忘比被动记忆更重要&#34;&gt;遗忘机制：主动遗忘比被动记忆更重要
&lt;/h3&gt;&lt;p&gt;这一点可能反直觉，但&lt;strong&gt;遗忘是记忆系统最关键的能力之一&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;每条记忆用如下格式标注优先级和过期时间：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-markdown&#34; data-lang=&#34;markdown&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;-&lt;/span&gt; [🟢LOW][CONTEXT] added:2026-02-01 expires:2026-03-01 · 某个临时任务的上下文
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;保留策略很简单：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-markdown&#34; data-lang=&#34;markdown&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;-&lt;/span&gt; 🔴 HIGH — 核心知识，永不过期，永不归档
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;-&lt;/span&gt; 🟡 MED — 重要知识，长期保留，手动评估是否过时
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;-&lt;/span&gt; 🟢 LOW — 参考信息，主动添加 &lt;span class=&#34;sb&#34;&gt;`expires:`&lt;/span&gt; 字段让系统自动清理
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;.archive/&lt;/code&gt; 冷存储设计&lt;/strong&gt;：用点号前缀命名目录。OpenClaw 的 QMD 向量引擎会自动跳过点号前缀的目录，实现零配置的热/冷索引分离。归档文件不会被删除——通过文件系统仍然可以直接访问。&lt;/p&gt;
&lt;p&gt;一个简单的每周 cron 脚本负责清理：扫描过期的 &lt;code&gt;expires:&lt;/code&gt; 字段，将对应条目移入 &lt;code&gt;.archive/&lt;/code&gt;，同步更新 &lt;code&gt;INDEX.md&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://images.hxzhouh.com/blog-images/2026/03/610af8262101fa80fbecdd43ea732d89.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;遗忘机制 — 过期与归档工作流&#34;
	
	
&gt;&lt;/p&gt;
&lt;h3 id=&#34;自动整合让记忆系统学会消化&#34;&gt;自动整合：让记忆系统学会「消化」
&lt;/h3&gt;&lt;p&gt;光有好的存储结构还不够——如果记忆不能自动整合，那它只是一个手动维护的文件夹。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;时间标注是关键&lt;/strong&gt;。每条记忆都必须包含时间关系标注，解决前面提到的「没有时间推理」问题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;「首次出现：……」&lt;/li&gt;
&lt;li&gt;「继上次 X 之后，今天完成了 Y」&lt;/li&gt;
&lt;li&gt;「首次讨论至今已 N 天，状态：已完成」&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;每晚自动整合（via cron）&lt;/strong&gt;：设置一个每晚执行的 cron 任务，调用你现有的 LLM API 来完成以下工作：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;读取今天的每日日志（&lt;code&gt;memory/YYYY-MM-DD.md&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;提取关键决策、经验教训和状态变更&lt;/li&gt;
&lt;li&gt;将重要条目提升到长期知识文件（带规范的 YAML frontmatter）&lt;/li&gt;
&lt;li&gt;更新 &lt;code&gt;NOW.md&lt;/code&gt; 中明天的优先级&lt;/li&gt;
&lt;li&gt;更新 &lt;code&gt;INDEX.md&lt;/code&gt; 健康仪表盘&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这里有一个容易踩的坑：&lt;strong&gt;写入前必须先读取目标文件&lt;/strong&gt;，对比是否已有类似内容，避免重复条目和记忆冲突（也就是 HaluMem 问题）。&lt;/p&gt;
&lt;h3 id=&#34;搭建清单&#34;&gt;搭建清单
&lt;/h3&gt;&lt;p&gt;以下是落地这套架构你需要的全部东西：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;三层文件结构&lt;/strong&gt;：&lt;code&gt;NOW.md&lt;/code&gt; + &lt;code&gt;memory/YYYY-MM-DD.md&lt;/code&gt; + 结构化子目录（&lt;code&gt;lessons/&lt;/code&gt;、&lt;code&gt;tools/&lt;/code&gt; 等）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;INDEX.md&lt;/code&gt;&lt;/strong&gt;：知识库健康仪表盘，启动时读取&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;每晚 cron 任务&lt;/strong&gt;：调用现有 LLM API 自动整合&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;expires&lt;/code&gt; 字段 + 每周清理脚本&lt;/strong&gt;：主动遗忘机制&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;内置向量搜索&lt;/strong&gt;：无需额外部署（OpenClaw 默认自带）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SQLite 三元组表&lt;/strong&gt;（可选）：只在你确实有多跳关系推理需求时才加&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;不需要任何额外资源&lt;/strong&gt;——一切都在你现有的设备上运行。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://images.hxzhouh.com/blog-images/2026/03/ac15bda116079c89680fa5a7daf01b44.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;记忆配置建议 — 场景对比&#34;
	
	
&gt;&lt;/p&gt;
&lt;h3 id=&#34;三个常见误区&#34;&gt;三个常见误区
&lt;/h3&gt;&lt;p&gt;在实践中我观察到不少人踩坑，这里总结三个最常见的：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;误区一：记忆越多越好。&lt;/strong&gt; 恰恰相反——过时的、低质量的记忆比没有记忆更危险。它们伪装成「事实」影响 Agent 的判断。遗忘机制和写入机制同样重要。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;误区二：只写不读。&lt;/strong&gt; 把信息写入记忆不代表 Agent 就会使用它。前面提到的 Serial Collapse（Agent 逐渐停止查询记忆）是真实存在的现象。定期验证 Agent 是否真的在用记忆，和构建记忆系统本身一样关键。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;误区三：什么都往知识图谱里塞。&lt;/strong&gt; 知识图谱的价值在于关系推理，不在于存储。日常偏好、操作日志放在 Markdown 里就够了——只有真正需要多跳推理的关系性知识才值得入图。&lt;/p&gt;
&lt;h3 id=&#34;summary&#34;&gt;Summary
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;好的记忆系统不在于存得最多，而在于能在&lt;strong&gt;正确的时刻、以正确的形式、将正确的信息递给 Agent&lt;/strong&gt;。&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;从三层架构（&lt;code&gt;NOW.md&lt;/code&gt; + 每日日志 + 结构化长期文件）起步，加上每晚自动整合和简单的遗忘机制——对个人用户来说这就够了，除了你现有的 LLM API 之外零成本。只有当你确实需要多跳关系推理时，再加一个 SQLite 三元组表。&lt;/p&gt;
&lt;p&gt;别把事情搞复杂了。上面这套架构解决了全部六个根本缺陷，而且一个下午就能搭好。&lt;/p&gt;
&lt;p&gt;你也可以直接把本文复制给 OpenClaw，让它自动帮你搞定。&lt;/p&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://huizhou92.com/zh-cn/p/%E4%B8%BA%E4%BB%80%E4%B9%88%E4%BD%A0%E7%9A%84-ai-agent-%E6%80%BB%E5%9C%A8%E9%81%97%E5%BF%98%E4%B8%80%E5%A5%97%E9%9D%A2%E5%90%91%E4%B8%AA%E4%BA%BA%E7%94%A8%E6%88%B7%E7%9A%84%E5%AE%9E%E7%94%A8%E8%AE%B0%E5%BF%86%E6%9E%B6%E6%9E%84/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;本文长期链接&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;如果您觉得我的博客对你有帮助，请通过 &lt;a class=&#34;link&#34; href=&#34;https://huizhou92.com/index.xml&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;RSS&lt;/a&gt;订阅我。&lt;/li&gt;
&lt;li&gt;或者在&lt;a class=&#34;link&#34; href=&#34;https://x.com/@piaopiaopig&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;X&lt;/a&gt;上关注我。&lt;/li&gt;
&lt;li&gt;如果您有&lt;a class=&#34;link&#34; href=&#34;https://medium.huizhou92.com/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Medium&lt;/a&gt;账号，能给我个关注嘛？我的文章第一时间都会发布在Medium。&lt;/li&gt;
&lt;/ul&gt;
</description>
        </item>
        <item>
        <title>Claude 选了 Vercel 100 次，AI 编程助手是如何在悄无声息中，成为史上最强大的广告渠道的。</title>
        <link>https://huizhou92.com/zh-cn/p/claude-code-picks-ai-advertisingzh-cn/</link>
        <pubDate>Fri, 27 Feb 2026 14:15:32 +0800</pubDate>
        
        <guid>https://huizhou92.com/zh-cn/p/claude-code-picks-ai-advertisingzh-cn/</guid>
        <description>&lt;p&gt;&lt;img src=&#34;https://images.hxzhouh.com/blog-images/2026/02/7e85d5149f82d42ee010b202e624876a.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;unnamed&#34;
	
	
&gt;
最近有人做了一个实验，我看完之后好几天都在想这件事。&lt;/p&gt;
&lt;p&gt;他们让 Claude Code 去构建项目——SaaS 应用、API、数据管道——不给任何工具限制，就问：你觉得应该用什么？然后把 2430 次回答里的工具推荐都统计了一遍。&lt;/p&gt;
&lt;!-- more--&gt;
&lt;p&gt;数字挺吓人的。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://images.hxzhouh.com/blog-images/2026/02/bf4053b6be35bfd8a599a88cd52a61b4.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;GitHub Actions 93.8%、Stripe 91.4%、shadcn/ui 90.1%、Vercel 100%——四个品类，Claude Code 已经替你做好了决定&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Vercel&lt;/code&gt;：JS 项目的部署推荐率 &lt;strong&gt;100%&lt;/strong&gt;。不是 80%，不是 90%，是一百分之一百。&lt;code&gt;Stripe&lt;/code&gt; 拿走了 91.4% 的支付集成推荐，&lt;code&gt;shadcn/ui&lt;/code&gt; 拿走了 90.1% 的 UI 组件推荐，&lt;code&gt;GitHub Actions&lt;/code&gt; 拿走了 93.8% 的 CI/CD 推荐。与此同时，AWS 的主推荐次数是 &lt;strong&gt;零&lt;/strong&gt;。&lt;code&gt;Express.js&lt;/code&gt;——npm 历史上下载量最高的包之一——也是零。
&lt;code&gt;Jest&lt;/code&gt; 在无数 CI 流水线里跑着，但 Claude 推荐它的概率只有 4%。&lt;/p&gt;
&lt;p&gt;你可能会想：「也许这些确实就是最好的工具呢？」也许是。但这恰恰是问题所在。&lt;/p&gt;
&lt;h2 id=&#34;这不叫推荐这叫垄断&#34;&gt;这不叫推荐，这叫垄断
&lt;/h2&gt;&lt;p&gt;我写了挺长时间代码了，见过技术偏好来来去去。Rails 从「未来」变成「遗留系统」，Angular 输给了 React，Redux 被更简单的状态管理方案取代。这些演变发生在几年时间里，经过无数博客、技术大会、招聘需求和开发者的讨论才形成。&lt;/p&gt;
&lt;p&gt;但它们从来没有以 100% 的方式发生过。&lt;/p&gt;
&lt;p&gt;一个真实的高级工程师推荐工具时，通常会说：「我喜欢 Vercel，DX 很好，但 Railway 和 Render 其实也挺靠谱的。」人的推荐自带上下文、有取舍、有场景限定。没有一个真正上过线的人会对所有项目无脑推同一个工具。&lt;/p&gt;
&lt;p&gt;但 Claude 会。当一个初级开发者问 AI 助手「我的 Next.js 应该怎么部署」，得到的不是一个带有权衡的比较，而是一个听起来像金科玉律的答案：&lt;em&gt;用 Vercel。&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;你得到的不是推荐。你得到的是某个人愿意花几十亿去制造的那种结果。&lt;/p&gt;
&lt;h2 id=&#34;史上最有效的广告没有广告&#34;&gt;史上最有效的广告，没有广告
&lt;/h2&gt;&lt;p&gt;直说吧：&lt;strong&gt;这就是广告，而且是有史以来最有效的广告形式。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;回顾一下广告的进化史。最早是广告牌和平面广告——你一眼就能认出来，可以主动忽略。然后是原生广告——把「赞助内容」伪装成新闻报道。读者一开始被骗，后来 SPONSORED 的标签让它露馅了。再后来是 SEO——公司疯狂生产内容刷排名，让产品「有机地」出现在搜索结果顶部。但用户慢慢也学会了对自然排名保持怀疑。&lt;/p&gt;
&lt;p&gt;现在，出现了终极形态：&lt;strong&gt;AI 介导的产品植入&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://images.hxzhouh.com/blog-images/2026/02/145e38396bc963b3a1e1bb81f86a6467.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Claude Code 是新的守门人：对工具厂商、开发者、整个生态，影响将以前所未有的方式渗透&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;没有赞助标签。没有关键词堆砌的痕迹。只有你信赖的 AI 助手，用跟刚才帮你 debug 一个复杂 TypeScript 错误完全相同的口吻，告诉你：部署用 Vercel，支付用 Stripe，UI 组件用 shadcn/ui。没有任何缝隙，没有任何提示说「这个推荐可能受到训练数据经济学的影响」。&lt;/p&gt;
&lt;p&gt;用户对 AI 的信任——这种信任是在亲眼看着 AI 正确解决一个又一个技术难题中建立起来的——会毫无摩擦地转移到 AI 的产品推荐上。这是有史以来最精密的信任转换机器。&lt;/p&gt;
&lt;h2 id=&#34;已经有人在玩这套了&#34;&gt;已经有人在玩这套了
&lt;/h2&gt;&lt;p&gt;接下来的部分有点黑暗：这个玩法，公司们早就在琢磨了。&lt;/p&gt;
&lt;p&gt;机制很简单。大语言模型用互联网上的文字训练出来。如果你想让你的产品被推荐得更多，你就需要让它更多地出现在训练数据里——出现在教程里、GitHub 仓库里、Stack Overflow 回答里、博客文章里。这本质上是 SEO，只不过目标受众是 AI 训练语料库。&lt;/p&gt;
&lt;p&gt;Vercel 把这套玩得很明白。他们的开发者关系策略、开源贡献、写得非常好的部署文档、跟 Next.js（他们也赞助这个项目）的深度绑定——这一切在 AI 训练数据里形成了巨大的引力场。不管这是从一开始就有意识的「训练数据营销」，还是恰好在 AI 时代发酵的好的开发者关系，结果是一样的：100% 的捕获率。&lt;/p&gt;
&lt;p&gt;与此同时，AWS——那个真正跑着互联网很大一部分的基础设施巨头——在 Claude 这里主推荐次数是零。想想这意味着什么。AWS 市占率更高，功能更多，企业部署规模更大。但它的开发者体验差，小项目的文档烂，学习曲线陡。在人类专家做决策的世界里，AWS 经常因为安全性和规模而胜出。在语言模型根据训练数据模式做决策的世界里，AWS 输了，因为初级开发者写的热情洋溢的 AWS 教程比 Vercel 的少太多了。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;新的护城河不是基础设施，不是功能，而是在训练集里的文档密度。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://images.hxzhouh.com/blog-images/2026/02/7167c2ef568c18a21e16ec7be3528c32.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;各生态系统下 Claude 的首选工具：JS 生态里 Vercel 100%、shadcn/ui 98.1%，Python 生态里 pytest 和 FastAPI 双双 100%&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;没人在讨论的那些受害者&#34;&gt;没人在讨论的那些受害者
&lt;/h2&gt;&lt;p&gt;&lt;code&gt;Express.js&lt;/code&gt; 被下载了几十亿次。它驱动着大量的 Node.js 应用。它有维护、稳定，几乎每个 Node 开发者都懂它。Claude Code 推荐它的次数：零。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Jest&lt;/code&gt; 跑在无数 CI 流水线里。它是 Create React App 多年来内置的测试框架。Claude 推荐它的概率只有 4%，基本上被 2021 年才出生的 Vitest 全面替代了。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Redux&lt;/code&gt;：零次。&lt;code&gt;AWS&lt;/code&gt;：零次。各大云厂商的托管数据库服务：几乎隐身。&lt;/p&gt;
&lt;p&gt;这些都不是烂工具。它们只是不够「AI 原生」——要么诞生得太早，没赶上这波大规模写技术博客的热潮；要么文档主要面向企业用户，不是那种会被抓进训练数据的热情洋溢的开发者教程。它们正在被从新一代的默认技术栈里抹去，不是因为输掉了技术竞争，而是因为输掉了一场它们压根不知道自己在参加的比赛。&lt;/p&gt;
&lt;p&gt;这件事应该引起所有做开发者基础设施的人警觉。「这是最好的工具吗？」这个问题正在越来越多地被「AI 会推荐这个工具吗？」所取代。这是两个截然不同的问题，有着截然不同的答案。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://images.hxzhouh.com/blog-images/2026/02/db46e748b09c777f1f33e57d78f05f0b.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Claude Code 的默认技术栈：一旦它选定了工具，被那个工具构建的项目就决定了&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;你的技术决策早就不是你的了&#34;&gt;你的技术决策早就不是你的了
&lt;/h2&gt;&lt;p&gt;有一个机制让这件事特别阴险，值得专门说一下：AI 编程助手不只影响你最初选哪个工具，它影响你对哪些工具变得&lt;strong&gt;熟练&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;当你用 Claude Code 构建项目，它持续地用 &lt;code&gt;shadcn/ui&lt;/code&gt; 的模式写代码，你就开始内化这些模式。你学会了 &lt;code&gt;shadcn/ui&lt;/code&gt; 的用法，因为那是 Claude 替你写的代码。当你之后「独立地」决定用哪个 UI 组件库时，你会自然地伸手去拿 &lt;code&gt;shadcn/ui&lt;/code&gt;——不是因为 Claude 告诉你要用它（那件事你早忘了），而是因为那是你真正会用的那个，是那个让你觉得自然顺手的那个。&lt;/p&gt;
&lt;p&gt;好广告的微妙之处一直都是这样。最好的广告不像广告，它像文化，像你自己的偏好。&lt;/p&gt;
&lt;p&gt;AI 工具推荐正在以比任何东西都更快的速度变成「文化」。不是因为它更有说服力，而是因为它更有&lt;strong&gt;塑造力&lt;/strong&gt;——它在你形成独立判断之前，就已经决定了你用什么来构建东西。&lt;/p&gt;
&lt;h2 id=&#34;接下来会发生什么&#34;&gt;接下来会发生什么
&lt;/h2&gt;&lt;p&gt;我预计未来几年，开发者工具厂商会掀起一场争夺「AI 推荐密度」的军备竞赛。我们会看到更多的教程投入、更多的开源示例、更多精心撰写的文档——这些文档在写的时候，已经心知肚明自己的受众里包括 AI 训练管道。&lt;/p&gt;
&lt;p&gt;我们大概也会看到一些厂商试图更直接地影响训练数据——赞助 AI 相关内容，为被用作训练示例的项目做贡献，也许最终找到直接影响模型微调的办法。「好的开发者关系」和「训练数据营销」之间的界限，会以极快的速度模糊掉。&lt;/p&gt;
&lt;p&gt;最早看透这套逻辑的公司已经赢得了巨大的先发优势。Vercel 在 Claude 推荐里的 100% 捕获率，胜过他们曾经能打出去的任何广告战役。它会自我强化：开发者学 &lt;code&gt;Vercel&lt;/code&gt;，因为 Claude 推荐 &lt;code&gt;Vercel&lt;/code&gt;；Vercel 在开发者心智中越来越强势；关于它的教程越来越多；训练数据越积越厚；未来的模型推荐它的频率越来越高。&lt;/p&gt;
&lt;p&gt;这是一个飞轮，而且它已经在高速旋转了。&lt;/p&gt;
&lt;p&gt;我们造了一台极其强大的推荐引擎，把它嵌进了每个开发者的工作流，然后免费发放出去。谁能影响这台引擎推荐什么，谁就能塑造下一代软件是用什么构建的。&lt;/p&gt;
&lt;p&gt;这不是一个产品功能，这是一个文明级的杠杆。而现在，它大体上指向的是那些在 2022 年写了最多热情洋溢的博客文章的人。&lt;/p&gt;
&lt;p&gt;&lt;em&gt;本文引用的研究是 amplifying.ai 的 Edwin Ong 和 Alex Vikati 撰写的 &amp;ldquo;&lt;a class=&#34;link&#34; href=&#34;https://amplifying.ai/research/claude-code-picks&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;What Claude Code Chooses&lt;/a&gt;&amp;quot;，基于对 Claude Code 在 20 个工具类别中 2430 次回答的系统性分析。&lt;/em&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Git 中你可能不知道的魔法文件.zh-cn</title>
        <link>https://huizhou92.com/zh-cn/p/git-%E4%B8%AD%E4%BD%A0%E5%8F%AF%E8%83%BD%E4%B8%8D%E7%9F%A5%E9%81%93%E7%9A%84%E9%AD%94%E6%B3%95%E6%96%87%E4%BB%B6zh-cn/</link>
        <pubDate>Thu, 26 Feb 2026 19:33:17 +0800</pubDate>
        
        <guid>https://huizhou92.com/zh-cn/p/git-%E4%B8%AD%E4%BD%A0%E5%8F%AF%E8%83%BD%E4%B8%8D%E7%9F%A5%E9%81%93%E7%9A%84%E9%AD%94%E6%B3%95%E6%96%87%E4%BB%B6zh-cn/</guid>
        <description>&lt;img src="https://images.hxzhouh.com/blog-images/2026/02/24827da63ee56c7fb3810936e86aa874.png" alt="Featured image of post Git 中你可能不知道的魔法文件.zh-cn" /&gt;&lt;h1 id=&#34;git-中你可能不知道的魔法文件&#34;&gt;Git 中你可能不知道的魔法文件
&lt;/h1&gt;&lt;p&gt;Git 会从仓库中读取一些特殊文件，用它们来控制自身行为。这些文件并不像 &lt;code&gt;.git/&lt;/code&gt; 里的配置那样留在本地，而是会随代码一起提交，从而影响 Git 对文件的处理方式。&lt;/p&gt;
&lt;!-- more--&gt;
&lt;p&gt;如果你正在开发与 Git 仓库交互的工具（比如 &lt;a class=&#34;link&#34; href=&#34;https://github.com/git-pkgs/git-pkgs&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;git-pkgs&lt;/a&gt;），那么务必确保正确遵守这些配置。&lt;/p&gt;
&lt;h3 id=&#34;gitignore&#34;&gt;.gitignore
&lt;/h3&gt;&lt;p&gt;指定 Git 永远不应追踪的文件模式。每行一个模式，支持通配符和目录标记。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;node_modules/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;*.log
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;.env
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;dist/
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Git 会按顺序检查多个忽略文件：包括每个目录下的 &lt;code&gt;.gitignore&lt;/code&gt;、仅限本地的 &lt;code&gt;.git/info/exclude&lt;/code&gt;，以及位于 &lt;code&gt;~/.config/git/ignore&lt;/code&gt;（或 &lt;code&gt;core.excludesFile&lt;/code&gt; 指定的路径）的全局忽略文件。全局忽略适合 &lt;code&gt;.DS_Store&lt;/code&gt;、&lt;code&gt;Thumbs.db&lt;/code&gt; 这类操作系统专属文件，避免每个项目的 &lt;code&gt;.gitignore&lt;/code&gt; 都要重复写这些内容。&lt;/p&gt;
&lt;p&gt;模式匹配支持通配符（&lt;code&gt;*.log&lt;/code&gt;）、目录标记（&lt;code&gt;dist/&lt;/code&gt;）、取反（&lt;code&gt;!important.log&lt;/code&gt;）和字符范围。&lt;code&gt;**&lt;/code&gt; 模式可匹配嵌套目录。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;.gitignore&lt;/code&gt; 只影响尚未被追踪的文件。如果某个文件在加入 &lt;code&gt;.gitignore&lt;/code&gt; 之前已经被追踪，那么它会继续留在仓库中，并且在各代码托管平台的网页界面上依然可见（需要执行 &lt;code&gt;git rm --cached&lt;/code&gt; 才能将其从追踪中移除）。另外，GitHub、GitLab、Forgejo 和 Gitea 的网页编辑器也不会阻止你创建符合忽略模式的文件并提交——它们不会给出任何警告。许多包管理器会附带自身的忽略模式（如 &lt;code&gt;node_modules/&lt;/code&gt;、&lt;code&gt;vendor/&lt;/code&gt;、&lt;code&gt;target/&lt;/code&gt;），需要你手动将它们添加到 &lt;code&gt;.gitignore&lt;/code&gt; 中。&lt;/p&gt;
&lt;p&gt;完整的语法请参阅 &lt;a class=&#34;link&#34; href=&#34;https://git-scm.com/docs/gitignore&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;gitignore 文档&lt;/a&gt;。另外，GitHub 维护了一份 &lt;a class=&#34;link&#34; href=&#34;https://github.com/github/gitignore&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;.gitignore 模板集合&lt;/a&gt;，涵盖各种语言和框架。&lt;/p&gt;
&lt;h3 id=&#34;gitattributes&#34;&gt;.gitattributes
&lt;/h3&gt;&lt;p&gt;&lt;code&gt;.gitattributes&lt;/code&gt; 用来告诉 Git 如何处理特定文件。你可以在这里配置过滤器（filter）、差异驱动（diff driver）、合并驱动（merge driver）、行尾规范化以及语言检测覆盖。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Clean/smudge filters&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;*.psd &lt;span class=&#34;nv&#34;&gt;filter&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;lfs &lt;span class=&#34;nv&#34;&gt;diff&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;lfs &lt;span class=&#34;nv&#34;&gt;merge&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;lfs
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Line ending normalization&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;*.sh text &lt;span class=&#34;nv&#34;&gt;eol&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;lf
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;*.bat text &lt;span class=&#34;nv&#34;&gt;eol&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;crlf
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Treat as binary&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;*.png binary
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Custom diff driver&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;*.json &lt;span class=&#34;nv&#34;&gt;diff&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;json
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Merge strategy&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;package-lock.json &lt;span class=&#34;nv&#34;&gt;merge&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;ours
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Language detection override for GitHub Linguist&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;vendor/* linguist-vendored
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;*.gen.go linguist-generated
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docs/* linguist-documentation
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;text&lt;/code&gt; 属性告诉 Git 规范化行尾。&lt;code&gt;binary&lt;/code&gt; 属性告诉 Git 不进行 diff 或合并，直接选取一个版本。&lt;code&gt;merge=ours&lt;/code&gt; 策略在合并冲突时始终保留本地版本。&lt;/p&gt;
&lt;p&gt;GitHub 的 Linguist 工具（用于语言检测）会读取 &lt;code&gt;.gitattributes&lt;/code&gt; 来覆盖检测结果。通过 &lt;code&gt;linguist-vendored&lt;/code&gt; 可以标记第三方代码，将其排除在语言统计之外；&lt;code&gt;linguist-generated&lt;/code&gt; 用来标记自动生成的文件，让它们在 diff 中默认折叠；&lt;code&gt;linguist-documentation&lt;/code&gt; 则标记文档，同样不计入统计。&lt;/p&gt;
&lt;p&gt;与 &lt;code&gt;.gitignore&lt;/code&gt; 类似，Git 会检查每个目录下的 &lt;code&gt;.gitattributes&lt;/code&gt;，同时也支持仅限本地的 &lt;code&gt;.git/info/attributes&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;完整属性列表请参考 &lt;a class=&#34;link&#34; href=&#34;https://git-scm.com/docs/gitattributes&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;gitattributes 文档&lt;/a&gt;，GitHub Linguist 的专属属性请参考 &lt;a class=&#34;link&#34; href=&#34;https://github.com/github-linguist/linguist/blob/main/docs/overrides.md&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Linguist 覆盖文档&lt;/a&gt;。&lt;/p&gt;
&lt;h3 id=&#34;lfsconfig&#34;&gt;.lfsconfig
&lt;/h3&gt;&lt;p&gt;&lt;code&gt;.lfsconfig&lt;/code&gt; 是随仓库一起提交的 Git LFS 配置文件。它使用 &lt;code&gt;git config&lt;/code&gt; 格式来设定 LFS 服务器的端点 URL、传输设置以及其他选项。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;[lfs]
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    url = https://lfs.example.com/repo
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;[lfs &amp;#34;transfer&amp;#34;]
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    maxretries = 3
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;当执行 LFS 命令时，Git LFS 会自动读取 &lt;code&gt;.lfsconfig&lt;/code&gt;。这样一来，你就可以把 LFS 配置也纳入版本控制，让所有协作者使用相同的设置。如果没有这个文件，每个开发者都需要手动配置本地 LFS 环境。&lt;/p&gt;
&lt;p&gt;LFS 还会使用 &lt;code&gt;.gitattributes&lt;/code&gt; 来标记哪些文件应由 LFS 处理（即上面示例中的 &lt;code&gt;*.psd filter=lfs diff=lfs merge=lfs&lt;/code&gt; 模式）。&lt;code&gt;.lfsconfig&lt;/code&gt; 文件则处理 LFS 专属设置，比如 LFS 服务器地址。如果你在文件已经提交之后才添加 LFS 的文件模式，需要运行 &lt;code&gt;git lfs migrate&lt;/code&gt; 来重写历史记录，将这些文件迁移到 LFS。&lt;/p&gt;
&lt;p&gt;全部可用选项请参考 &lt;a class=&#34;link&#34; href=&#34;https://github.com/git-lfs/git-lfs/blob/main/docs/man/git-lfs-config.adoc&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Git LFS config 文档&lt;/a&gt;。&lt;/p&gt;
&lt;h3 id=&#34;gitmodules&#34;&gt;.gitmodules
&lt;/h3&gt;&lt;p&gt;&lt;code&gt;.gitmodules&lt;/code&gt; 是 Git 子模块的配置文件。当你运行 &lt;code&gt;git submodule add&lt;/code&gt; 时，Git 会自动写入这个文件；当运行 &lt;code&gt;git submodule update&lt;/code&gt; 时，Git 则会读取它。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;[submodule &amp;#34;vendor/lib&amp;#34;]
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    path = vendor/lib
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    url = https://github.com/example/lib.git
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    branch = main
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;每个子模块对应一条记录，包含路径、URL，以及可选的追踪分支。该文件必须放在仓库根目录。&lt;/p&gt;
&lt;p&gt;通过子模块，你可以把其他 Git 仓库作为依赖项嵌入到当前项目中。不过，&lt;code&gt;git clone&lt;/code&gt; 默认不会拉取子模块内容，你需要额外运行 &lt;code&gt;git submodule update --init --recursive&lt;/code&gt;，或者在克隆时加上 &lt;code&gt;--recurse-submodules&lt;/code&gt; 参数。&lt;/p&gt;
&lt;p&gt;子模块在版本管理上有些笨拙：你追踪的是某个特定 commit，而不是版本范围；它们会创建嵌套的 &lt;code&gt;.git/&lt;/code&gt; 目录；如果忘记更新，很容易导致令人困惑的状态。&lt;/p&gt;
&lt;p&gt;不过，对于你自己维护的 vendor 代码管理，或者只需要检出部分目录的 monorepo 结构，子模块仍然是一个可用的方案。&lt;/p&gt;
&lt;p&gt;完整工作流请参考 &lt;a class=&#34;link&#34; href=&#34;https://git-scm.com/docs/git-submodule&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;git submodules 文档&lt;/a&gt;，文件格式请参考 &lt;a class=&#34;link&#34; href=&#34;https://git-scm.com/docs/gitmodules&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;gitmodules 文档&lt;/a&gt;。&lt;/p&gt;
&lt;h3 id=&#34;mailmap&#34;&gt;.mailmap
&lt;/h3&gt;&lt;p&gt;&lt;code&gt;.mailmap&lt;/code&gt; 用来将作者的名字和邮箱地址映射到统一的规范身份。Git 会在 &lt;code&gt;git log&lt;/code&gt;、&lt;code&gt;git shortlog&lt;/code&gt; 和 &lt;code&gt;git blame&lt;/code&gt; 的输出中使用这个映射。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;9
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# Map old email to new email
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Jane Developer &amp;lt;jane@company.com&amp;gt; &amp;lt;jane@oldcompany.com&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# Standardize name spelling
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Jane Developer &amp;lt;jane@company.com&amp;gt; Jane Dev &amp;lt;jane@company.com&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# Fix both
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Jane Developer &amp;lt;jane@company.com&amp;gt; &amp;lt;janed@personal.com&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Jane Developer &amp;lt;jane@company.com&amp;gt; J Developer &amp;lt;janed@personal.com&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;格式是 &lt;code&gt;规范名称 &amp;lt;规范邮箱&amp;gt; 提交名称 &amp;lt;提交邮箱&amp;gt;&lt;/code&gt;。Git 会查找与提交作者匹配的条目，并在输出时替换为规范身份。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;git shortlog -sn&lt;/code&gt;、&lt;code&gt;git log&lt;/code&gt; 和 &lt;code&gt;git blame&lt;/code&gt; 都会遵循 mailmap，将同一作者的不同提交聚合到一起。不过，GitHub 的贡献者图表&lt;a class=&#34;link&#34; href=&#34;https://github.com/orgs/community/discussions/22518&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;并不支持&lt;/a&gt; mailmap，这意味着即使你的 mailmap 配置正确，网页上仍然会显示重复的条目。&lt;/p&gt;
&lt;p&gt;没有 mailmap，那些更换过邮箱地址或修正过名字拼写的贡献者，就会被当作多个不同的人出现。有了它，他们的所有提交都会聚合到同一个身份下。&lt;/p&gt;
&lt;p&gt;文件格式请参考 &lt;a class=&#34;link&#34; href=&#34;https://git-scm.com/docs/gitmailmap&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;gitmailmap 文档&lt;/a&gt;。mailmap 默认放在仓库根目录的 &lt;code&gt;.mailmap&lt;/code&gt;，也可以通过配置 &lt;code&gt;mailmap.file&lt;/code&gt; 指向其他位置。&lt;/p&gt;
&lt;h3 id=&#34;git-blame-ignore-revs&#34;&gt;.git-blame-ignore-revs
&lt;/h3&gt;&lt;p&gt;&lt;code&gt;.git-blame-ignore-revs&lt;/code&gt; 文件用来列出 &lt;code&gt;git blame&lt;/code&gt; 应该跳过的提交。你可以把大规模代码格式化、lint 修复或其他无关紧要的提交的 SHA 写进去，这样 blame 就会穿透这些提交，指向真正有意义的变更。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# .git-blame-ignore-revs
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# Ran prettier on entire codebase
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# Migrated to ESLint flat config
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;要让 Git 使用这个文件，需要执行 &lt;code&gt;git config blame.ignoreRevsFile .git-blame-ignore-revs&lt;/code&gt;。不过，GitHub、GitLab（15.4 及以上版本）和 Gitea 会自动读取该文件，无需额外配置。需要注意的是，如果你在全局 git config 中设置了 &lt;code&gt;blame.ignoreRevsFile&lt;/code&gt;，那么当 &lt;code&gt;git blame&lt;/code&gt; 在缺少该文件的仓库中运行时就会报错——因此，要么为每个仓库单独配置，要么确保所有工作仓库中都至少有一个空的 &lt;code&gt;.git-blame-ignore-revs&lt;/code&gt; 文件。&lt;/p&gt;
&lt;p&gt;这个文件解决了一个经典问题：对整个代码库运行格式化工具之后，&lt;code&gt;git blame&lt;/code&gt; 会变得毫无用处。有了这个文件，blame 就会跳过那些格式化提交，显示真正的代码逻辑作者。&lt;/p&gt;
&lt;p&gt;文件格式很简单：每行一个 commit SHA，&lt;code&gt;#&lt;/code&gt; 开头的行为注释。详情请参考 &lt;a class=&#34;link&#34; href=&#34;https://git-scm.com/docs/git-blame#Documentation/git-blame.txt---ignore-revs-fileltfilegt&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;git blame 文档&lt;/a&gt;。&lt;/p&gt;
&lt;h3 id=&#34;gitmessage&#34;&gt;.gitmessage
&lt;/h3&gt;&lt;p&gt;&lt;code&gt;.gitmessage&lt;/code&gt; 是提交信息的模板。通过 &lt;code&gt;git config commit.template .gitmessage&lt;/code&gt; 配置后，Git 会在打开提交信息编辑器时自动填充这些内容。&lt;/p&gt;
&lt;p&gt;与其他文件不同，&lt;code&gt;.gitmessage&lt;/code&gt; 需要每次克隆仓库后手动配置。每个开发者克隆后都要运行 &lt;code&gt;git config commit.template .gitmessage&lt;/code&gt;。有些团队会通过初始化脚本自动完成这一步，或者借助 &lt;a class=&#34;link&#34; href=&#34;https://github.com/typicode/husky&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;husky&lt;/a&gt; 在安装时设置本地配置。正因为多了这个步骤，大多数项目更倾向于使用 &lt;code&gt;commit-msg&lt;/code&gt; Hook 来验证提交信息的格式，而不是用模板来引导写作。&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://git-scm.com/docs/git-commit#_discussion&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;git commit 文档&lt;/a&gt; 中提到了模板文件。&lt;code&gt;prepare-commit-msg&lt;/code&gt; Hook 是另一种替代方案，可以动态生成模板。&lt;/p&gt;
&lt;h2 id=&#34;代码托管平台专属目录&#34;&gt;代码托管平台专属目录
&lt;/h2&gt;&lt;p&gt;各大代码托管平台也在仓库中定义了自己的“魔法目录”：&lt;code&gt;.github/&lt;/code&gt;、&lt;code&gt;.gitlab/&lt;/code&gt;、&lt;code&gt;.gitea/&lt;/code&gt;、&lt;code&gt;.forgejo/&lt;/code&gt;、&lt;code&gt;.bitbucket/&lt;/code&gt; 等。这些虽然不是 Git 的原生功能，但遵循同样的理念——让配置随代码一起分发。&lt;/p&gt;
&lt;p&gt;这些目录里通常存放 CI/CD 工作流定义、Issue 和 PR 模板、CODEOWNERS 文件（用于指定路径的代码审查者），以及其他平台专属配置。这样一来，托管平台就能在不污染仓库根目录的前提下扩展功能。&lt;/p&gt;
&lt;p&gt;Forgejo 和 Gitea 支持回退机制：Forgejo 的查找顺序是 &lt;code&gt;.forgejo/&lt;/code&gt; → &lt;code&gt;.gitea/&lt;/code&gt; → &lt;code&gt;.github/&lt;/code&gt;；Gitea 的顺序是 &lt;code&gt;.gitea/&lt;/code&gt; → &lt;code&gt;.github/&lt;/code&gt;。这让你可以在同时托管于多个平台时，覆盖 GitHub 的专属配置。&lt;/p&gt;
&lt;p&gt;SourceHut 使用根目录下的 &lt;code&gt;.build.yml&lt;/code&gt; 或 &lt;code&gt;.builds/*.yml&lt;/code&gt; 来配置 CI，没有专属的目录命名空间。&lt;/p&gt;
&lt;h2 id=&#34;其他约定&#34;&gt;其他约定
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;.gitkeep&lt;/strong&gt; 只是一个惯例，并非 Git 的原生功能。Git 本身不会追踪空目录。如果你想在仓库中保留一个空目录，可以往里面放一个 &lt;code&gt;.gitkeep&lt;/code&gt; 文件，这样 Git 就有东西可以追踪了。这个文件名是约定俗成的，实际上你可以起任何名字。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;.gitconfig&lt;/strong&gt; 文件有时会出现在仓库中，作为推荐的配置供人参考。Git 不会自动加载这些文件（出于安全原因），但项目会附上说明，让你运行 &lt;code&gt;git config include.path ../.gitconfig&lt;/code&gt; 或手动复制相关设置。这种做法常见于 monorepo 或希望统一特定 Git 设置的项目。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;.gitsigners&lt;/strong&gt; 或类似文件用于追踪可信贡献者的 GPG/SSH 签名密钥。这不是 Git 的原生功能，但一些项目（尤其是 Linux 内核）在签名工作流中使用它。Git 的 &lt;code&gt;gpg.ssh.allowedSignersFile&lt;/code&gt; 配置可以指向一个可信 SSH 密钥文件，供 &lt;code&gt;git log --show-signature&lt;/code&gt; 用于验证。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;.gitreview&lt;/strong&gt; 配置 &lt;a class=&#34;link&#34; href=&#34;https://www.gerritcodereview.com/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Gerrit&lt;/a&gt; 代码审查集成。托管在 Gerrit 上的项目（如 OpenStack、Android、Eclipse）使用它来指定推送目标的 Gerrit 服务器和项目。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;[gerrit]
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;host=review.opendev.org
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;port=29418
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;project=openstack/nova.git
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;defaultbranch=master
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;运行 &lt;a class=&#34;link&#34; href=&#34;https://docs.opendev.org/opendev/git-review/latest/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;code&gt;git review&lt;/code&gt;&lt;/a&gt; 时会读取这个文件，并将提交推送到 Gerrit 进行审查，而不是直接推送到分支。这是一个通过提交配置文件来扩展 Git 工作流的典型案例。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;.gitlint&lt;/strong&gt; 配置 &lt;a class=&#34;link&#34; href=&#34;https://jorisroovers.com/gitlint/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;gitlint&lt;/a&gt;，用于检查提交信息格式。遵循同样的理念：把配置提交到仓库，所有人都使用相同的规则。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;[general]
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ignore=body-is-missing
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;[title-max-length]
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;line-length=72
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;gitlint 读取这个文件来验证提交信息格式。类似于使用 &lt;code&gt;commit-msg&lt;/code&gt; Hook，但配置随仓库一起分发。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;.jj/&lt;/strong&gt; 是 &lt;a class=&#34;link&#34; href=&#34;https://github.com/jj-vcs/jj&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Jujutsu&lt;/a&gt; 的工作区状态目录。Jujutsu 是一个兼容 Git 的版本控制系统，它将自己的元数据存储在 &lt;code&gt;.jj/&lt;/code&gt; 中，同时遵守所有 Git 魔法文件的规则。如果你使用 &lt;code&gt;jj&lt;/code&gt;，你的仓库中会同时存在 &lt;code&gt;.git/&lt;/code&gt; 和 &lt;code&gt;.jj/&lt;/code&gt;，而 &lt;code&gt;.gitignore&lt;/code&gt;、&lt;code&gt;.gitattributes&lt;/code&gt;、&lt;code&gt;.mailmap&lt;/code&gt; 的行为完全一样。&lt;/p&gt;
&lt;h2 id=&#34;git-之外&#34;&gt;Git 之外
&lt;/h2&gt;&lt;p&gt;这种模式并不止步于 Git。其他工具遵循同样的做法：在仓库中放一个点文件，工具自动检测并改变行为。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;.editorconfig&lt;/strong&gt; 跨团队统一编辑器行为。把它放在仓库根目录，编辑器就会读取它来配置缩进风格、行尾、行尾空白和字符编码。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;root = true
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;[*]
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;indent_style = space
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;indent_size = 2
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;end_of_line = lf
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;charset = utf-8
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;trim_trailing_whitespace = true
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;[*.md]
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;trim_trailing_whitespace = false
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;VS Code、Vim、Emacs、Sublime 以及大多数其他编辑器要么原生支持，要么有插件支持。完整规范请参考 &lt;a class=&#34;link&#34; href=&#34;https://editorconfig.org/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;editorconfig.org&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;.ruby-version&lt;/strong&gt;、&lt;strong&gt;.node-version&lt;/strong&gt;、&lt;strong&gt;.python-version&lt;/strong&gt; 告诉版本管理器应该使用哪个语言版本。rbenv、nodenv、pyenv、nvm 和 asdf 等工具在你 &lt;code&gt;cd&lt;/code&gt; 进该目录时会自动读取这些文件并切换版本。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# .ruby-version
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;3.3.0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# .node-version
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;20.11.0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;.tool-versions&lt;/strong&gt; 是 asdf 的多语言版本文件。用一个文件管理所有语言版本。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ruby 3.3.0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;nodejs 20.11.0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;python 3.12.0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;.dockerignore&lt;/strong&gt; 的工作方式类似 &lt;code&gt;.gitignore&lt;/code&gt;，但针对 Docker 构建上下文。运行 &lt;code&gt;docker build&lt;/code&gt; 时，Docker 会将文件发送给守护进程。在 &lt;code&gt;.dockerignore&lt;/code&gt; 中列出模式，Docker 就不会发送这些文件。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;.git
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;node_modules
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;*.log
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;.env
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这可以加速构建，并防止密钥泄露到镜像中。语法与 &lt;code&gt;.gitignore&lt;/code&gt; 一致：通配符、取反、目录标记。&lt;/p&gt;
&lt;h2 id=&#34;支持这些文件&#34;&gt;支持这些文件
&lt;/h2&gt;&lt;p&gt;如果你正在开发与 Git 仓库交互的工具，可能需要留意并处理这些文件：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;遍历仓库目录树时，需要读取 &lt;code&gt;.gitignore&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;读取 &lt;code&gt;.gitattributes&lt;/code&gt;，了解哪些文件是二进制文件、第三方代码或生成代码&lt;/li&gt;
&lt;li&gt;显示作者信息时，读取 &lt;code&gt;.mailmap&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;如果需要处理子模块，读取 &lt;code&gt;.gitmodules&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;git config 格式（&lt;code&gt;.gitmodules&lt;/code&gt; 和其他各种文件使用的格式）为 &lt;code&gt;[section &amp;quot;subsection&amp;quot;] key = value&lt;/code&gt;。Git 自带 &lt;code&gt;git config&lt;/code&gt; 命令可以正确地读写这些文件。大多数语言的 Git 库都内置了 git config 解析器。&lt;/p&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://huizhou92.com/zh-cn/p/git-%E4%B8%AD%E4%BD%A0%E5%8F%AF%E8%83%BD%E4%B8%8D%E7%9F%A5%E9%81%93%E7%9A%84%E9%AD%94%E6%B3%95%E6%96%87%E4%BB%B6zh-cn/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;本文长期链接&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;如果您觉得我的博客对你有帮助，请通过 &lt;a class=&#34;link&#34; href=&#34;https://huizhou92.com/index.xml&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;RSS&lt;/a&gt;订阅我。&lt;/li&gt;
&lt;li&gt;或者在&lt;a class=&#34;link&#34; href=&#34;https://x.com/@piaopiaopig&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;X&lt;/a&gt;上关注我。&lt;/li&gt;
&lt;li&gt;如果您有&lt;a class=&#34;link&#34; href=&#34;https://medium.huizhou92.com/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Medium&lt;/a&gt;账号，能给我个关注嘛？我的文章第一时间都会发布在Medium。&lt;/li&gt;
&lt;/ul&gt;
</description>
        </item>
        <item>
        <title>Uber 如何扩展数据复制能力以实现每天 PB 级数据迁移</title>
        <link>https://huizhou92.com/zh-cn/p/uber-%E5%A6%82%E4%BD%95%E6%89%A9%E5%B1%95%E6%95%B0%E6%8D%AE%E5%A4%8D%E5%88%B6%E8%83%BD%E5%8A%9B%E4%BB%A5%E5%AE%9E%E7%8E%B0%E6%AF%8F%E5%A4%A9-pb-%E7%BA%A7%E6%95%B0%E6%8D%AE%E8%BF%81%E7%A7%BB/</link>
        <pubDate>Fri, 30 Jan 2026 12:25:38 +0800</pubDate>
        
        <guid>https://huizhou92.com/zh-cn/p/uber-%E5%A6%82%E4%BD%95%E6%89%A9%E5%B1%95%E6%95%B0%E6%8D%AE%E5%A4%8D%E5%88%B6%E8%83%BD%E5%8A%9B%E4%BB%A5%E5%AE%9E%E7%8E%B0%E6%AF%8F%E5%A4%A9-pb-%E7%BA%A7%E6%95%B0%E6%8D%AE%E8%BF%81%E7%A7%BB/</guid>
        <description>&lt;p&gt;&lt;img src=&#34;https://images.hxzhouh.com/blog-images/2026/01/80e16aa62038af4a72c2b96421964388.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;How Uber Scaled Data Replication to Move Petabytes Every Day-cover&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;引言&#34;&gt;引言
&lt;/h2&gt;&lt;p&gt;Uber 重视可靠的数据湖（Data Lake），其分布在本地和云环境中。这种多区域架构在有限的网络带宽下为确保可靠且及时的数据访问带来了挑战，尤其是在灾难恢复（Disaster Recovery）场景中需要实现无缝的数据可用性。Uber 使用 &lt;a class=&#34;link&#34; href=&#34;https://www.uber.com/en-IN/blog/building-ubers-data-lake-batch-data-replication-using-hivesync/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Hive Sync 服务&lt;/a&gt;，该服务基于 Apache Hadoop® Distcp（Distributed Copy，分布式拷贝）进行数据复制。然而，随着 Uber 数据湖规模超过 350 PB，Distcp 的局限性逐渐显现。本文探讨了针对 Distcp 所做的优化，以提升其性能并满足 Uber 在分布式基础设施上日益增长的数据复制和灾难恢复需求。&lt;/p&gt;
&lt;!-- more--&gt;
&lt;h2 id=&#34;理解-distcp&#34;&gt;理解 Distcp
&lt;/h2&gt;&lt;p&gt;Distcp 是一个开源框架，用于以分布式方式在不同位置之间复制大规模数据集。它利用 Hadoop 的 MapReduce 框架将拷贝任务并行化并分发到多个节点上，从而实现更快、更具扩展性的数据传输，尤其适用于大规模环境。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://blog.uber-cdn.com/cdn-cgi/image/width=1024,quality=80,onerror=redirect,format=auto/wp-content/uploads/2026/01/image-17696490376086.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Image&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;图 1：Distcp 高层架构。&lt;/p&gt;
&lt;h3 id=&#34;distcp-架构&#34;&gt;Distcp 架构
&lt;/h3&gt;&lt;p&gt;Distcp 架构由以下几个关键组件组成：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Distcp 工具（Distcp Tool）：&lt;/strong&gt; 识别文件，将其分组为块（拷贝清单，Copy Listing），定义跨 Mapper 的分发策略，并将配置好的 Hadoop 作业提交到 YARN。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hadoop 客户端（Hadoop Client）：&lt;/strong&gt; 设置作业环境，确定哪些 Mapper 处理特定的块（输入分片，Input Splitting），并将作业提交到 YARN。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;资源管理器（RM，Resource Manager）：&lt;/strong&gt; YARN 组件，负责调度任务，接收 Distcp 作业，分配资源，并将执行委托给应用主节点。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;应用主节点（AM，Application Master）：&lt;/strong&gt; 监控 MapReduce 作业的生命周期，为 Copy Mapper 任务向 RM 请求资源（容器），并在目标端合并文件分片。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;拷贝映射器（Copy Mapper）：&lt;/strong&gt; 执行实际的数据块拷贝操作，运行在由 YARN Node Manager 管理的容器中。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;拷贝提交器（Copy Committer）：&lt;/strong&gt; 合并已拷贝的数据块，在目标文件系统中组装最终文件。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&#34;https://blog.uber-cdn.com/cdn-cgi/image/width=806,quality=80,onerror=redirect,format=auto/wp-content/uploads/2026/01/image-17696490375966.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Image&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;图 2：Distcp 从 /src/ 目录复制到 /dest/ 目录的示意图。&lt;/p&gt;
&lt;p&gt;图 2 展示了 Distcp 如何使用上述组件将三个文件从源目录 &lt;em&gt;/src/&lt;/em&gt; 复制到目标目录 &lt;em&gt;/dest/&lt;/em&gt;。源目录包含三个大小相同的文件——File 1、File 2 和 File 3。在客户端运行的拷贝清单任务识别这些文件并将每个文件分成两个块（Chunk）。输入分片任务随后将这些文件块分配给三个 Mapper。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Map 1&lt;/strong&gt; 接收 File 1 的一个块和 File 2 的一个块。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Map 2&lt;/strong&gt; 处理 File 3 的一个块和 File 1 的一个块。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Map 3&lt;/strong&gt; 处理 File 2 的一个块和 File 3 的一个块。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;拷贝映射器任务随后将这些块从源目录复制到目标目录。复制完成后，拷贝提交器任务在 AM 中运行，合并每个文件对应的块，在目标目录中重建最终的三个文件。&lt;/p&gt;
&lt;h2 id=&#34;hivesync-如何使用-distcp&#34;&gt;HiveSync 如何使用 Distcp
&lt;/h2&gt;&lt;p&gt;HiveSync 最初基于开源的 Airbnb® &lt;a class=&#34;link&#34; href=&#34;https://github.com/airbnb/reair&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;ReAir&lt;/a&gt; 项目构建。它支持批量复制（一次性拷贝大量数据）和增量复制（随着新数据到达同步增量更新），使 Uber 的数据湖在 HDFS™（Hadoop Distributed File System，Hadoop 分布式文件系统）和基于云的对象存储之间保持同步。它使用 Distcp 进行大规模数据复制。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://blog.uber-cdn.com/cdn-cgi/image/width=990,quality=80,onerror=redirect,format=auto/wp-content/uploads/2026/01/image-17696490389357.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Image&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;图 3：HiveSync 架构：使用 Distcp 的数据复制工作流。&lt;/p&gt;
&lt;p&gt;图 3 展示了 HiveSync 服务器如何监听来自源 Hive 集群的拷贝请求。对于大于 256 MB 的数据，它将 Distcp 作业提交给执行器。多个 Worker（异步线程）随后并行准备并通过 Hadoop 客户端将这些作业提交到 YARN。监控线程跟踪每个作业的进度，作业成功完成后，数据即可在目标集群中使用。&lt;/p&gt;
&lt;h2 id=&#34;hivesync-的扩展性问题临界点&#34;&gt;HiveSync 的扩展性问题：临界点
&lt;/h2&gt;&lt;p&gt;到 2022 年第三季度，HiveSync 面临重大的扩展性挑战，因为每日数据复制量在仅一个季度内从 250 TB 激增至 1 PB。&lt;/p&gt;
&lt;p&gt;导致这一快速增长的一个因素是数据写入集中在单个数据中心。2022 年，Uber 为节省成本转向了主动-被动（Active-Passive）数据湖架构，从均匀分布的数据生成模式转变为由主要的本地数据中心承担 90% 的数据生成和大部分批处理计算任务。这显著增加了 HiveSync 服务器从主区域向备用区域复制数据的负载。SRC（Single Region Compute，单区域计算）项目的影响将另行讨论。&lt;/p&gt;
&lt;p&gt;另一个因素是将所有本地 Hive 数据集接入 HiveSync。在新的主动-被动模型下，HiveSync 成为灾难恢复的关键组件，确保在一个区域生成的数据能被复制到另一个地理区域。这要求 HiveSync 扩展到覆盖 Uber 的整个数据湖。仅在一个季度内，HiveSync 管理的数据集数量从 30,000 增长到 144,000，新数据集不断接入。这使复制请求数量增加了一倍多。&lt;/p&gt;
&lt;p&gt;由此，每日复制作业数量从 10,000 飙升至平均 374,000，远远超出系统的处理能力。这导致了大量积压，使得满足承诺的复制延迟 SLA 变得越来越困难。具体而言，P100 复制延迟 SLA 4 小时和 P99.9 SLO 20 分钟在这一新规模下变得难以维持。&lt;/p&gt;
&lt;p&gt;此外，随着 HiveSync 在将 Uber 数据湖&lt;a class=&#34;link&#34; href=&#34;https://www.uber.com/en-IN/blog/modernizing-ubers-data-infrastructure-with-gcp/?uclick_id=f08b5468-ea9f-4f1f-a440-357109448b6e&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;从本地迁移到云区域&lt;/a&gt;中承担关键角色，复制请求的规模预计将大幅增加。预计拷贝请求的规模和数量将几乎翻倍，这对 HiveSync 管理增长的工作负载和优化数据复制流程以应对云基础设施运营挑战提出了更高要求。&lt;/p&gt;
&lt;h2 id=&#34;关键改进&#34;&gt;关键改进
&lt;/h2&gt;&lt;p&gt;我们对 Distcp 进行了以下增强，以满足我们的扩展需求。这些优化显著提升了 Uber 数据复制的规模和效率。&lt;/p&gt;
&lt;p&gt;具体包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;将资源密集型的拷贝清单和输入分片任务转移到应用主节点，通过缓解 HDFS 客户端争用将作业提交延迟降低了 90%。&lt;/li&gt;
&lt;li&gt;并行化拷贝清单和拷贝提交器任务，显著缩短了作业规划和完成时间。&lt;/li&gt;
&lt;li&gt;为小规模传输实现 &lt;em&gt;Uber 作业&lt;/em&gt;，帮助每天减少 268,000 次容器启动，优化了 YARN 资源使用。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;下面详细介绍每项改进——&lt;/p&gt;
&lt;h3 id=&#34;将-distcp-准备任务转移到-am&#34;&gt;将 Distcp 准备任务转移到 AM
&lt;/h3&gt;&lt;p&gt;在一次故障事件中，我们注意到系统负载较高时，文件系统延迟的增加导致 Distcp 拷贝清单延迟相应上升。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://blog.uber-cdn.com/cdn-cgi/image/width=1024,quality=80,onerror=redirect,format=auto/wp-content/uploads/2026/01/image-17696490376049.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Image&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;图 4：HDFS FSUtils 延迟增加直接影响 Distcp 拷贝清单任务。&lt;/p&gt;
&lt;p&gt;当我们分析延迟峰值期间的线程转储时，发现大部分线程都在等待 HDFS 客户端持有的远程过程调用（RPC，Remote Procedure Call）锁。这种方式在高度多线程的环境中无法很好地扩展。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://blog.uber-cdn.com/cdn-cgi/image/width=1024,quality=80,onerror=redirect,format=auto/wp-content/uploads/2026/01/image-17696490376845.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Image&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;图 5：线程阻塞在 RPC 调用上。&lt;/p&gt;
&lt;p&gt;在典型的 Distcp 提交流程中，多个组件依赖 HDFS 客户端：Distcp Worker 用于数据比较，Distcp 工具用于拷贝清单，Hadoop 客户端用于输入分片。随着 Distcp 执行线程数量的增加，并行使用 HDFS 客户端的数量也随之增加。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://blog.uber-cdn.com/cdn-cgi/image/width=871,quality=80,onerror=redirect,format=auto/wp-content/uploads/2026/01/image-17696490375027.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Image&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;图 6：来自不同拷贝作业请求的多个并行调用在 HDFS 客户端上产生争用。&lt;/p&gt;
&lt;p&gt;我们发现，虽然 Distcp 能很好地扩展数据拷贝，但它也在客户端处理文件规划和清单任务。这一准备阶段——识别待拷贝文件（输入分片）——造成了瓶颈，因为它依赖共享的 HDFS 客户端，而该客户端也被 HiveSync 的其他组件使用。随着数据量和 Distcp Worker 数量的增长，HDFS 客户端中的 JVM 级锁成为主要问题，随着并行度的增加导致线程争用。这造成了延迟，其中仅拷贝清单就占了作业提交延迟的 90%。&lt;/p&gt;
&lt;p&gt;大量的 NameNode 调用使问题更加严重，这些调用与待拷贝文件数量成正比——对于大型目录尤为突出。&lt;/p&gt;
&lt;p&gt;为了减轻单个 HDFS 客户端的负载，我们将资源密集型的拷贝清单和输入分片任务从 HiveSync 服务器转移到了 AM。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://blog.uber-cdn.com/cdn-cgi/image/width=1440,quality=80,onerror=redirect,format=auto/wp-content/uploads/2026/01/image-17696490372570-edited.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Image&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;图 7：将拷贝清单和输入分片流程从 Hive Sync 服务器（客户端）转移到 AM。&lt;/p&gt;
&lt;p&gt;现在，每个 Distcp 作业在自己的 AM 容器中执行拷贝清单，这显著减少了 HiveSync Hadoop 客户端上的锁争用。这帮助我们实现了 Distcp 作业提交延迟 90% 的降低。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://blog.uber-cdn.com/cdn-cgi/image/width=1024,quality=80,onerror=redirect,format=auto/wp-content/uploads/2026/01/image-17696490404372.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Image&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;图 8：观测到 Distcp 作业提交时间降低了 90%。&lt;/p&gt;
&lt;h3 id=&#34;并行化拷贝清单任务&#34;&gt;并行化拷贝清单任务
&lt;/h3&gt;&lt;p&gt;Distcp 工具运行拷贝清单任务以生成待拷贝文件的文件系统块。这些块被写入序列文件（Sequence File），形成一个文件块列表，供拷贝映射器任务从源集群复制到目标集群。在此过程中，主线程通过 &lt;a class=&#34;link&#34; href=&#34;https://hadoop.apache.org/docs/stable/api/org/apache/hadoop/fs/FileSystem.html#getFileBlockLocations-org.apache.hadoop.fs.FileStatus-long-long-&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;getFileBlockLocations API&lt;/a&gt; 依次调用 NameNode，为超过指定块大小的文件创建文件分片（Chunk）。它还在文件状态检查失败时进行重试，使这成为 Distcp 中最耗费资源的部分。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://blog.uber-cdn.com/cdn-cgi/image/width=1024,quality=80,onerror=redirect,format=auto/wp-content/uploads/2026/01/image-17696490375821.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Image&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;图 9：即使将此任务移至应用主节点后，最繁忙的复制服务器上 P99 延迟平均仍约 10 分钟。&lt;/p&gt;
&lt;p&gt;我们观察到多个文件可以并行列出，并以任意顺序写入序列文件。但是，每个文件的块需要保持在一起并按顺序排列，因为拷贝提交器算法使用它们在目标端合并已拷贝的文件分片。基于这一思路，我们通过为每个文件分配单独的线程来创建分片，将文件系统 NameNode 调用并行化以降低拷贝清单延迟，将分片添加到阻塞队列中，由单独的写入线程按顺序将块写入序列文件。这一方法帮助改善了 Distcp 作业的完成时间。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://blog.uber-cdn.com/cdn-cgi/image/width=794,quality=80,onerror=redirect,format=auto/wp-content/uploads/2026/01/image-17696490375237.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Image&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;图 10：拷贝清单任务 V2 工作流。&lt;/p&gt;
&lt;p&gt;在图 10 中，列出函数使用多线程通过 NameNode 调用从源集群检索文件。每个线程负责为一个文件创建块，允许多个文件的并行处理。例如，&lt;em&gt;/src/file1&lt;/em&gt;（1684 MB）被分成两个块：第一个块（&lt;em&gt;/src/file1/part0&lt;/em&gt;）包含 4 个 256 MB 的 HDFS 块，第二个块（&lt;em&gt;/src/file1/part1&lt;/em&gt;）包含 3 个块（2 个 256 MB 和 1 个 128 MB）。列出线程同步地将这些块添加到阻塞队列中，而单独的写入线程定期轮询队列并按顺序将两个块写入序列文件。为实现快速故障处理，如果任何线程失败，主线程将停止处理并重试 Distcp 作业。列出函数完成且队列中所有项目均已写入序列文件后，它通过状态更新器更新作业状态。&lt;/p&gt;
&lt;p&gt;通过使用 6 个线程，我们在所有 HiveSync 服务器上实现了 P99 平均 Distcp 清单延迟降低 60%，最大延迟降低 75%。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://blog.uber-cdn.com/cdn-cgi/image/width=1024,quality=80,onerror=redirect,format=auto/wp-content/uploads/2026/01/image-17696490405298.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Image&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;图 11：使用 6 个线程后，某 Hive Sync 服务器上拷贝清单延迟的改善。&lt;/p&gt;
&lt;h3 id=&#34;并行化拷贝提交器任务&#34;&gt;并行化拷贝提交器任务
&lt;/h3&gt;&lt;p&gt;在 Distcp 拷贝映射器任务完成从源目录到目标目录的文件分片拷贝后，AM 中的拷贝提交器任务将这些分片合并为完整文件。对于包含超过 500,000 个文件的目录，这一过程可能需要长达 30 分钟。开源版本按顺序合并文件块，导致性能较低。&lt;/p&gt;
&lt;p&gt;为解决这一问题，我们将文件拼接过程并行化，每个线程负责一次合并一个文件。拷贝清单过程中创建的序列文件用于确定需要在目标端合并的各个文件块的顺序。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://blog.uber-cdn.com/cdn-cgi/image/width=940,quality=80,onerror=redirect,format=auto/wp-content/uploads/2026/01/image-17696490383440.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Image&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;图 12：拷贝提交器任务 V2 工作流。&lt;/p&gt;
&lt;p&gt;在图 12 中，Mapper 从序列文件中获取拷贝清单过程中创建的文件分片，并将其拷贝到 &lt;em&gt;/dest/&lt;/em&gt; 下的目标目录。每个拼接线程（Concatenator）收集特定文件的分片并将其合并以创建最终文件。File 1 的三个分片（&lt;em&gt;/dest/file_part0&lt;/em&gt;、&lt;em&gt;/dest/file_part1&lt;/em&gt; 和 &lt;em&gt;/dest/file_part2&lt;/em&gt;）被合并为目标端的 &lt;em&gt;/dest/file1&lt;/em&gt;。File 2 和 File 3 同理。为实现快速故障处理，如果任何线程遇到问题，主线程将停止处理并重试 Distcp 作业。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://blog.uber-cdn.com/cdn-cgi/image/width=1024,quality=80,onerror=redirect,format=auto/wp-content/uploads/2026/01/image-17696490375203.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Image&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;图 13：使用 10 个线程后，平均拼接延迟降低了 97.29%。&lt;/p&gt;
&lt;h3 id=&#34;uber-化单-mapper-作业改善-yarn-使用效率&#34;&gt;Uber 化单 Mapper 作业：改善 YARN 使用效率
&lt;/h3&gt;&lt;p&gt;约 52% 的 HiveSync 服务器提交的 Distcp 作业仅需一个 Mapper 即可拷贝少于 512 MB 和不到 200 个文件的数据。虽然这些小作业执行速度很快，但大量时间花在了环境设置（在 YARN 中分配新容器和 JVM 启动时间）而非实际拷贝上。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://blog.uber-cdn.com/cdn-cgi/image/width=1024,quality=80,onerror=redirect,format=auto/wp-content/uploads/2026/01/image-17696490391599.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Image&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;图 14：超过 50% 的 Distcp 作业仅分配了一个 Mapper。&lt;/p&gt;
&lt;p&gt;为解决这一开销问题，我们利用了 Hadoop 的 &amp;ldquo;Uber 作业&amp;rdquo; 功能，消除了在单独容器中分配和运行任务的需要。拷贝映射器任务直接在应用主节点的 JVM 中执行，减少了不必要的容器分配。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://blog.uber-cdn.com/cdn-cgi/image/width=1024,quality=80,onerror=redirect,format=auto/wp-content/uploads/2026/01/image-17696490375551.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Image&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;图 15：Uber 作业工作流。&lt;/p&gt;
&lt;p&gt;在图 15 中，AM 判断一个作业是否符合 Uber 作业的条件。如果符合，拷贝映射器任务将在 AM 的 JVM 中本地执行。否则，AM 通过 Node Manager 请求容器并在其中运行拷贝映射器任务。任务完成后，AM 启动拷贝提交器任务以在目标端合并文件分片。&lt;/p&gt;
&lt;p&gt;我们通过以下配置启用了 Uber 作业：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;mapreduce.job.ubertask.enable: true&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;mapreduce.job.ubertask.maxmaps: 1&lt;/em&gt;（确保仅使用 1 个 Mapper）&lt;/li&gt;
&lt;li&gt;&lt;em&gt;mapreduce.job.ubertask.maxbytes: 512 MB&lt;/em&gt;（限制数据拷贝量为 512 MB）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;通过实施这一方案，我们每天减少了约 268,000 次单核容器启动，显著改善了 YARN 资源使用和作业效率。&lt;/p&gt;
&lt;h2 id=&#34;成效&#34;&gt;成效
&lt;/h2&gt;&lt;h3 id=&#34;增量数据复制能力显著提升-5-倍&#34;&gt;增量数据复制能力显著提升 5 倍
&lt;/h3&gt;&lt;p&gt;我们对 Uber Distcp 工具所做的改进极大地提升了跨本地和云数据中心的增量数据复制能力。得益于这些变更，我们在仅一年内将本地数据处理能力提升了 5 倍，且未发生任何与扩展相关的故障。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://blog.uber-cdn.com/cdn-cgi/image/width=1024,quality=80,onerror=redirect,format=auto/wp-content/uploads/2026/01/image-17696490377430.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Image&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;图 16：HiveSync 在本地和云数据中心的规模。&lt;/p&gt;
&lt;h3 id=&#34;从本地到云的无缝批量数据迁移&#34;&gt;从本地到云的无缝批量数据迁移
&lt;/h3&gt;&lt;p&gt;近几个月，我们扩展了 HiveSync 的功能以支持将本地数据湖复制到基于云的数据湖，详情见&lt;a class=&#34;link&#34; href=&#34;https://www.uber.com/en-IN/blog/modernizing-ubers-data-infrastructure-with-gcp/?uclick_id=f08b5468-ea9f-4f1f-a440-357109448b6e&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;此文&lt;/a&gt;。对 Distcp 的增强在处理此次迁移的规模方面发挥了关键作用。截至目前，我们已成功将超过 306 PB 的数据迁移到云端。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://blog.uber-cdn.com/cdn-cgi/image/width=1024,quality=80,onerror=redirect,format=auto/wp-content/uploads/2026/01/image-17696490374135.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Image&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;图 17：通过 HiveSync 服务从本地迁移到云端的数据量。&lt;/p&gt;
&lt;h3 id=&#34;更好的可观测性&#34;&gt;更好的可观测性
&lt;/h3&gt;&lt;p&gt;我们引入了多个关键指标，显著提升了可观测性（Observability）。这些指标提供了关于客户端和 YARN AM 端 Distcp 作业提交时间、作业提交速率以及关键 Distcp 组件（如拷贝清单和拷贝提交器任务）性能的洞察。我们还跟踪了 Hadoop 容器的最大堆内存使用量、每个作业的 P99 Distcp 拷贝速率以及整体拷贝速率等指标。这种增强的可见性使我们能够更好地监控和了解服务的复制速率，并在缓解和诊断多起故障中发挥了关键作用。&lt;/p&gt;
&lt;h2 id=&#34;挑战&#34;&gt;挑战
&lt;/h2&gt;&lt;p&gt;在将变更部署到生产服务器的过程中，我们面临了几项挑战。其中一个挑战是 AM 中的 OOM（Out of Memory，内存溢出）异常。严格的压力测试帮助我们确定了最优的内存和核心使用配置。我们添加了指标来检测 OOM 问题，这在后续帮助我们为内存密集型拷贝请求确定最优的 YARN 资源配置。&lt;/p&gt;
&lt;p&gt;另一个问题是 HiveSync 的高作业提交速率。降低提交延迟提高了作业提交速率，但这经常导致&amp;quot;YARN 队列已满&amp;quot;错误。为防止 YARN 过载，我们在 HiveSync 中实现了熔断器（Circuit Breaker），在重试成功之前暂时暂停新的提交。我们添加了指标来检测此类事件，从而实现实时监控并按需调整 YARN 队列容量。管理高拷贝速率虽然高效，但会导致高网络带宽使用，需要仔细调优以平衡性能和资源限制。&lt;/p&gt;
&lt;p&gt;我们还遇到了因长时间运行的拷贝清单任务导致的 AM 故障。最初，拷贝清单和输入分片部分被移至 AM 的启动阶段。这导致了问题，因为 RM 期望 AM 发送定期心跳信号。由于心跳发送器仅在启动完成后才启动，而拷贝清单任务有时需要超过 10 分钟，因此会导致超时。为解决这一问题，拷贝清单任务被移至输出提交器的设置阶段，该阶段在心跳发送器已启动之后执行，从而避免了超时。&lt;/p&gt;
&lt;h2 id=&#34;结论&#34;&gt;结论
&lt;/h2&gt;&lt;p&gt;展望未来，团队正聚焦于围绕并行化、更好的资源利用和网络管理的若干增强，包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;并行化文件权限设置&lt;/li&gt;
&lt;li&gt;并行化输入分片&lt;/li&gt;
&lt;li&gt;将计算密集型提交任务移至 Reduce 阶段以提高可扩展性&lt;/li&gt;
&lt;li&gt;实现动态带宽限流器&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;此外，我们计划为这些优化贡献开源补丁。Uber HiveSync 团队将继续专注于解决数据复制挑战，在我们的规模下，即使是微小的改进也能带来显著的收益。&lt;/p&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://huizhou92.com/zh-cn/p/uber-%E5%A6%82%E4%BD%95%E6%89%A9%E5%B1%95%E6%95%B0%E6%8D%AE%E5%A4%8D%E5%88%B6%E8%83%BD%E5%8A%9B%E4%BB%A5%E5%AE%9E%E7%8E%B0%E6%AF%8F%E5%A4%A9-pb-%E7%BA%A7%E6%95%B0%E6%8D%AE%E8%BF%81%E7%A7%BB/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;本文长期链接&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;如果您觉得我的博客对你有帮助，请通过 &lt;a class=&#34;link&#34; href=&#34;https://huizhou92.com/index.xml&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;RSS&lt;/a&gt;订阅我。&lt;/li&gt;
&lt;li&gt;或者在&lt;a class=&#34;link&#34; href=&#34;https://x.com/@piaopiaopig&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;X&lt;/a&gt;上关注我。&lt;/li&gt;
&lt;li&gt;如果您有&lt;a class=&#34;link&#34; href=&#34;https://medium.huizhou92.com/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Medium&lt;/a&gt;账号，能给我个关注嘛？我的文章第一时间都会发布在Medium。&lt;/li&gt;
&lt;/ul&gt;
</description>
        </item>
        <item>
        <title>Go 1.26 黑科技：跳过 GC 直接释放内存，性能飙升 200%</title>
        <link>https://huizhou92.com/zh-cn/p/go-1.26-%E9%BB%91%E7%A7%91%E6%8A%80%E8%B7%B3%E8%BF%87-gc-%E7%9B%B4%E6%8E%A5%E9%87%8A%E6%94%BE%E5%86%85%E5%AD%98%E6%80%A7%E8%83%BD%E9%A3%99%E5%8D%87-200/</link>
        <pubDate>Thu, 13 Nov 2025 16:58:54 +0800</pubDate>
        
        <guid>https://huizhou92.com/zh-cn/p/go-1.26-%E9%BB%91%E7%A7%91%E6%8A%80%E8%B7%B3%E8%BF%87-gc-%E7%9B%B4%E6%8E%A5%E9%87%8A%E6%94%BE%E5%86%85%E5%AD%98%E6%80%A7%E8%83%BD%E9%A3%99%E5%8D%87-200/</guid>
        <description>&lt;!-- more--&gt;
&lt;p&gt;最近，Go 语言社区围绕一个全新的内存管理提案展开了激烈讨论：&lt;strong&gt;在不依赖垃圾回收 (GC) 的情况下直接释放并重用内存&lt;/strong&gt;。&lt;a class=&#34;link&#34; href=&#34;https://github.com/golang/go/issues/74299&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;#74299&lt;/a&gt; 引入了 &lt;code&gt;runtime.free&lt;/code&gt; 及相关机制，试图让编译器和标准库在特定场景下安全地跳过 GC，对短命的内存对象进行即时回收利用&lt;a class=&#34;link&#34; href=&#34;https://github.com/golang/go/issues/74299#:~:text=Note%20that%20runtime,called%20directly%20by%20end%20users&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;github.com&lt;/a&gt;&lt;a class=&#34;link&#34; href=&#34;https://go.googlesource.com/proposal/&amp;#43;/refs/changes/55/700255/14/design/74299-runtime-free.md#:~:text=1,Challenges%20here%20include&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;go.googlesource.com&lt;/a&gt;。此举被认为可能为 Go 带来一次&lt;strong&gt;性能上的革命&lt;/strong&gt;：初步原型显示，在 &lt;code&gt;strings.Builder&lt;/code&gt; 这样的场景中，利用该机制&lt;strong&gt;性能提升可达 2 倍&lt;/strong&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/golang/go/issues/74299#:~:text=%28In%20short%2C%20currently%20,more%20allocations%20per%20benchmark%20loop&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;github.com&lt;/a&gt;。本文将回顾 Go 内存管理领域从 &lt;em&gt;arena&lt;/em&gt; 实验到 &lt;em&gt;memory region&lt;/em&gt; 构想，再到 &lt;em&gt;runtime.free&lt;/em&gt; 提案的探索之旅，并剖析这一新提案的技术细节、产生的意义、演化过程，以及对普通开发者的影响。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;runtime.free 将在 Golang1.26 中 以 GOEXPERIMENT 的方式提供实验性支持。&lt;/p&gt;&lt;/blockquote&gt;
&lt;h2 id=&#34;背景一场关于手动内存管理的漫长探索&#34;&gt;背景：一场关于“手动”内存管理的漫长探索
&lt;/h2&gt;&lt;p&gt;自 Go 语言诞生以来，自动垃圾回收（GC）就是其核心特性之一。然而在&lt;strong&gt;对性能极度敏感&lt;/strong&gt;的场景（如高吞吐的服务端程序）中，GC 带来的开销始终让开发者有所顾虑。为了进一步降低 GC 负担，Go 团队近年开始了一系列关于“手动”或“半自动”内存管理的探索尝试。&lt;/p&gt;
&lt;h3 id=&#34;arena-实验--强大却难以融合&#34;&gt;Arena 实验 —— 强大却难以融合
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Arena&lt;/strong&gt; 实验&lt;a class=&#34;link&#34; href=&#34;https://github.com/golang/go/issues/51317&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;#51317&lt;/a&gt;是 Go 团队在 2022 年迈出的大胆一步。它引入了一个新的 &lt;code&gt;arena&lt;/code&gt; 包和 &lt;code&gt;Arena&lt;/code&gt; 类型，允许开发者将一组生命周期相同的对象分配到一个独立的内存区域中，并在不需要时&lt;strong&gt;一次性释放整个区域&lt;/strong&gt; 这一做法类似其他语言的 &lt;em&gt;region-based memory management&lt;/em&gt; 思想：大量对象集中分配、集中释放，从而降低常规分配/回收的成本。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Arena 的优点&lt;/strong&gt;在某些场景下非常显著：所有对象统一释放，大幅减少了 GC 扫描和回收的工作量，谷歌内部测试显示对大型应用最高可节省约15%的 CPU 和内存开销。但是，Arena 随即暴露出严重的问题——&lt;strong&gt;API 侵入性&lt;/strong&gt;太强。为了使用 Arena，几乎每个相关函数都不得不增加一个 &lt;code&gt;arena.Arena&lt;/code&gt; 参数，这导致这种用法具有“&lt;strong&gt;病毒式&lt;/strong&gt;”传播效应，破坏了 Go 一贯强调的简洁与可组合性。另外，Arena 在与 Go 现有特性（如隐式接口、逃逸分析）配合时也出现了诸多不兼容之处。最终，由于 API 难以融入生态，Go 官方在 2023 年初宣布 &lt;strong&gt;无限期搁置 Arena 提案&lt;/strong&gt;，并明确表示 &lt;code&gt;GOEXPERIMENT=arena&lt;/code&gt; 仅供实验、不建议在生产中使用。&lt;/p&gt;
&lt;h3 id=&#34;memory-region-构想--优雅但实现复杂&#34;&gt;Memory Region 构想 —— 优雅但实现复杂
&lt;/h3&gt;&lt;p&gt;吸取了 Arena 的教训，Go 团队接着提出了更贴合 Go 哲学的概念：&lt;strong&gt;内存区域（Memory Region&lt;/strong&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/golang/go/issues/70257&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;#70257&lt;/a&gt;）它设想引入一种更&lt;strong&gt;透明&lt;/strong&gt;的机制——例如通过一个 &lt;code&gt;region.Do(func(){ ... })&lt;/code&gt; 调用，将某段函数作用域内的所有内存分配&lt;strong&gt;隐式绑定&lt;/strong&gt;到一个临时区域。当这段代码执行完毕时，该区域内分配的所有对象都可以一并释放。&lt;/p&gt;
&lt;p&gt;Memory Region 的&lt;strong&gt;优点&lt;/strong&gt;在于：对开发者而言几乎是透明的，无需修改函数签名或显式传递 Arena 对象。另外，通过运行时的巧妙设计，它依然能保持&lt;strong&gt;内存安全&lt;/strong&gt;。具体来说，如果区域中的某个对象被外部保留（“逃逸”出了区域作用域），运行时会&lt;strong&gt;自动将该对象挪回全局堆&lt;/strong&gt;由 GC 管理，从而避免类似 Arena 那样可能出现的 use-after-free 错误。这一设计既有手动内存管理的性能，又尽可能避免了手动管理常见的安全隐患。&lt;/p&gt;
&lt;p&gt;然而，Memory Region 的&lt;strong&gt;问题在于实现极其复杂&lt;/strong&gt;。要支持这种“区域化”的内存管理，需要对运行时和 GC 做重大改造。例如，开启区域时可能需要一个特殊的低开销写屏障来追踪对象逃逸情况，这增加了垃圾回收机制的复杂性和运行成本。虽然理论上可行，但要让这一方案高效稳健地落地，无疑是一项长期且充满不确定性的研究课题。迄今为止，Memory Region 仍停留在讨论和原型阶段，没有迅速融入 Go 主线。&lt;/p&gt;
&lt;h3 id=&#34;最终的焦点runtimefree&#34;&gt;最终的焦点：runtime.free
&lt;/h3&gt;&lt;p&gt;在 Arena 的侵入性和 Memory Region 的复杂性之间，Go 团队终于找到了一条&lt;strong&gt;更务实、工程上可行的中间路线&lt;/strong&gt;——这就是本次的 &lt;strong&gt;runtime.free 提案&lt;/strong&gt;。相比之前“大包大揽”的方案，&lt;code&gt;runtime.free&lt;/code&gt; 走的是&lt;strong&gt;精细化局部优化&lt;/strong&gt;的路子：与其让开发者手动管理整片内存，不如让更了解代码细节的&lt;strong&gt;编译器&lt;/strong&gt;和&lt;strong&gt;底层标准库&lt;/strong&gt;来决定何时安全地释放特定的堆内存。换言之，runtime.free 旨在像一把手术刀，&lt;strong&gt;精准切除&lt;/strong&gt;那些生命周期短暂且已确定不再使用的内存块，减少 GC 不必要的工作。&lt;/p&gt;
&lt;p&gt;这种方法极大地缓解了 Arena 的可组合性难题（因为开发者不需要改动代码、一切由编译器和运行时自动处理），也避开了 Memory Region 那种对 GC 全局机制的大改动。更重要的是，它为解决 Go 长期存在的性能**“鸡与蛋”困局&lt;strong&gt;提供了新的思路：许多优化（例如更激进的逃逸分析）过去之所以收效甚微，是因为即便消除了某个原因，内存对象仍可能由于&lt;/strong&gt;另一原因&lt;a class=&#34;link&#34; href=&#34;https://go.googlesource.com/proposal/&amp;#43;/refs/changes/55/700255/14/design/74299-runtime-free.md#:~:text=For%20example%2C%20,to%20eliminate%20the%20first%20reason&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;**逃逸到堆上，最终并未减少 GC 负担&lt;/a&gt;。而 runtime.free 的出现，相当于提供了一把钥匙，可以&lt;strong&gt;打破这种循环&lt;/strong&gt;——一旦对象在运行时被判定“确实不再需要”，就立即释放，从而&lt;a class=&#34;link&#34; href=&#34;https://go.googlesource.com/proposal/&amp;#43;/refs/changes/55/700255/14/design/74299-runtime-free.md#:~:text=In%20other%20words%2C%20a%20runtime,valuable%2C%20including%20in%20escape%20analysis&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;真正实现减少 GC 压力的初衷&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id=&#34;runtimefree-的实现机制编译器自动化--标准库配合&#34;&gt;runtime.free 的实现机制：编译器自动化 + 标准库配合
&lt;/h2&gt;&lt;p&gt;需要强调的是，runtime.free &lt;strong&gt;并不&lt;/strong&gt;打算提供给普通开发者一个手工调用 &lt;code&gt;free&lt;/code&gt; 的新玩具。相反，它采取高度受控的“双管齐下”策略，通过&lt;strong&gt;编译器&lt;/strong&gt;和&lt;strong&gt;标准库&lt;/strong&gt;的改进来实现内存释放优化，同时不向 Go 程序员暴露额外的复杂度。&lt;/p&gt;
&lt;h3 id=&#34;编译器自动释放-runtimefreetracked&#34;&gt;编译器自动释放 (runtime.freetracked)
&lt;/h3&gt;&lt;p&gt;首先，也是整个提案最令人兴奋的部分：&lt;strong&gt;编译器将自动插入内存释放逻辑&lt;/strong&gt;。具体而言，当编译器检测到某些场景下分配的内存可以安全提前回收时，就会在编译阶段悄悄地产生额外的代码来跟踪并释放这些内存：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;识别阶段：&lt;/strong&gt; 对于典型的 &lt;code&gt;make([]T, size)&lt;/code&gt; 切片分配，如果编译器发现该切片虽然因为长度或容量未知而必须逃逸到堆上，但它的使用范围不超过当前函数（例如不会被保存到全局或返回给调用者），那么编译器将把这次分配标记为&lt;a class=&#34;link&#34; href=&#34;https://go.googlesource.com/proposal/&amp;#43;/refs/changes/55/700255/14/design/74299-runtime-free.md#:~:text=2,in%20a%20loop&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;“&lt;strong&gt;可跟踪释放&lt;/strong&gt;”&lt;/a&gt;。这种情况下，会调用一个特殊的分配函数（如 &lt;code&gt;makeslicetracked64&lt;/code&gt;）来分配对象，并将该对象的指针记录到当前函数栈上的一个&lt;a class=&#34;link&#34; href=&#34;https://go.googlesource.com/proposal/&amp;#43;/refs/changes/55/700255/14/design/74299-runtime-free.md#:~:text=The%20second%20API%20,as%20a%20scope%20is%20exited&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;strong&gt;追踪列表&lt;/strong&gt;&lt;/a&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;跟踪阶段：&lt;/strong&gt; 编译器在栈上维护一个 &lt;code&gt;freeables&lt;/code&gt; 数组（或切片），收集所有被标记为可释放的堆对象。当有新的可释放对象分配时，其指针会被追加到这个列表中。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;释放阶段：&lt;/strong&gt; 在函数返回前，编译器会自动插入一行类似 &lt;code&gt;defer runtime.freeTracked(&amp;amp;freeables)&lt;/code&gt; 的调用&lt;a class=&#34;link&#34; href=&#34;https://tonybai.com/2025/09/18/go-runtime-free-proposal/#:~:text=1.%20%E8%AF%86%E5%88%AB%EF%BC%9A%E5%BD%93%E7%BC%96%E8%AF%91%E5%99%A8%E9%81%87%E5%88%B0%E4%B8%80%E4%B8%AA%20make%28,%E8%87%AA%E5%8A%A8%E9%87%8D%E5%86%99%20%E4%B8%BA%E4%B8%8D%E4%BA%A7%E7%94%9F%20GC%20%E5%8E%8B%E5%8A%9B%E7%9A%84%E7%89%88%E6%9C%AC%EF%BC%8C%E8%80%8C%E5%BC%80%E5%8F%91%E8%80%85%E5%AF%B9%E6%AD%A4%20%E5%AE%8C%E5%85%A8%E6%97%A0%E6%84%9F%E3%80%82&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;tonybai.com&lt;/a&gt;。这样，当函数退出时，这个延迟调用将执行，通知运行时回收 &lt;code&gt;freeables&lt;/code&gt; 列表中记录的所有堆对象。这种做法确保了在&lt;strong&gt;作用域结束&lt;/strong&gt;时，临时分配的对象立即被释放，而无需等待下一轮 GC。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&#34;https://images.hxzhouh.com/blog-images/2025/11/2b6c0d576255984f33c9aa53a40d8187.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;未命名&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;对于开发者来说，这一切都是&lt;strong&gt;透明&lt;/strong&gt;的：你完全可以像往常一样编写代码，而编译器在背后已经将其“悄悄优化”为一个更少堆分配、更少 GC 压力的版本。举个简单例子：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 开发者原始代码&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;buf&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;make&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;([]&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;byte&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;// 可能逃逸到堆上&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// ... 使用 buf&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 编译器优化后的等效代码（概念示意）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;freeables&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;unsafe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Pointer&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;buf&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;runtime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;makeslicetracked64&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;freeables&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;// 分配受跟踪的 slice&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// ... 使用 buf&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;defer&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;runtime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;freeTracked&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;freeables&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;// 函数退出时释放 buf 对应的内存&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;经过这种改写，原本可能需要 GC 扫描回收的 &lt;code&gt;buf&lt;/code&gt; 内存，将在函数结束时&lt;strong&gt;立即归还&lt;/strong&gt;给运行时可用的内存池。因此，未来我们编写的一些看似会产生大量堆分配的代码，有望在&lt;strong&gt;不改变任何源码&lt;/strong&gt;的情况下，由编译器替我们转换成“零 GC 压力”的高效版本——开发者对此&lt;strong&gt;毫无感知&lt;/strong&gt;，但程序性能却因此获益。&lt;/p&gt;
&lt;h3 id=&#34;标准库协助释放-runtimefreesized&#34;&gt;标准库协助释放 (runtime.freesized)
&lt;/h3&gt;&lt;p&gt;另一方面，对于 Go &lt;strong&gt;标准库中少数性能关键的组件&lt;/strong&gt;，开发团队也在尝试手动加入 &lt;code&gt;runtime.free&lt;/code&gt; 的调用。这并不是要把手动内存管理强加给所有库，而是利用标准库对自身情况的了解，在&lt;strong&gt;极有限&lt;/strong&gt;的热点场景显式地释放内存，以追求极致性能。提案中提到的主要目标包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;strings.Builder&lt;/code&gt; / &lt;code&gt;bytes.Buffer&lt;/code&gt;&lt;/strong&gt; 的扩容：当内部缓冲区需要增长时，旧的缓冲区实际上已经不再使用，完全可以当场释放，避免占用堆并减轻后续 GC 压力。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;map&lt;/code&gt; 的扩容&lt;/strong&gt;：Go 的 map 在扩容和重新哈希（rehash）时会分配新的底层数组，此时旧的 buckets 数组事实上已死，同样可以立即回收。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;slices.Collect&lt;/code&gt; 等&lt;/strong&gt;切片收集/拼接的操作：在构造最终结果过程中产生的大量中间切片，仅用于过渡，也可以及时释放。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;对于这些场景，runtime.free 提供了一个内部运行时函数 &lt;code&gt;runtime.freeSized(ptr, size, noscan)&lt;/code&gt;（提案原型中使用的是 &lt;code&gt;freesized&lt;/code&gt;），允许在知道一个对象指针 &lt;code&gt;ptr&lt;/code&gt; 及其大小后，立刻释放对应内存。这种调用仅限于&lt;strong&gt;非常底层&lt;/strong&gt;且对内存使用有精确认知的代码。例如 Go 作者们在实验中修改了 &lt;code&gt;strings.Builder&lt;/code&gt; 的代码，在扩容逻辑中加入对旧缓冲区的 &lt;code&gt;runtime.freeSized&lt;/code&gt; 调用。结果表明：&lt;strong&gt;对于执行多次扩容的场景，新版 &lt;code&gt;strings.Builder&lt;/code&gt; 性能提升了约 45%～55%&lt;/strong&gt;，几乎&lt;a class=&#34;link&#34; href=&#34;https://github.com/golang/go/issues/74299#:~:text=BuildString_Builder%2F10Write_36Bytes_NoGrow,55.71%25%20%28p%3D0.000%20n%3D20&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;strong&gt;快了一倍&lt;/strong&gt;&lt;/a&gt;！换句话说，通过在正确的时机手动释放内存，可以实打实地换来巨大性能收益。&lt;/p&gt;
&lt;p&gt;需要注意的是，这种手动调用只会出现在&lt;strong&gt;少数标准库&lt;/strong&gt;内部。Go 团队并不打算在诸如 &lt;code&gt;net/http&lt;/code&gt; 这样的高级库里遍地插入 &lt;code&gt;runtime.free&lt;/code&gt; —— 毕竟那样又回到了“到处手动管理内存”的老路上。这一步更多是为了验证：&lt;strong&gt;在哪些特殊场景下，提前释放内存能够带来明显收益&lt;/strong&gt;。如果证明效果显著，我们也许会在未来看到这些改进融入正式版本中；如果收益不大或风险高，也可以根据讨论再决定是否采纳。&lt;/p&gt;
&lt;h2 id=&#34;性能影响与收益&#34;&gt;性能影响与收益
&lt;/h2&gt;&lt;p&gt;让 GC “少管一些事”听起来很美好，但也要评估此举本身的性能代价。插入额外的跟踪和释放逻辑，会不会拖慢常规代码的速度？根据目前的原型测试结果，答案是&lt;strong&gt;几乎可以忽略&lt;/strong&gt;。对比启用 &lt;code&gt;runtimefree&lt;/code&gt; 实验前后的基准数据表明：*&lt;em&gt;在没有可释放对象的普通分配场景下，新机制对性能的影响在 -1.5% 到 +2.2% 之间，几何平均值几乎为零。也就是说，如果你的代码并不存在那些可以提前释放的内存对象，启用这个功能对性能既不会造成明显负担，也几乎不会带来益处——它基本是&lt;/em&gt;“零成本”（pay-for-what-you-use）*的。&lt;/p&gt;
&lt;p&gt;而在命中了优化路径的情况下，&lt;strong&gt;收益则是多方面的&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;减少 GC 的 CPU 消耗：&lt;/strong&gt; 这是最直接的好处。部分内存由运行时立即回收，意味着 GC 每次需要标记、扫描的对象变少，从而降低了 GC 自身的CPU占用&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;拉长 GC 间隔、缩短写屏障时间：&lt;/strong&gt; 垃圾变少了，GC 自然可以更久运行一次。对于 Go 程序来说，这意味着更多时间处于&lt;strong&gt;无 GC 干扰&lt;/strong&gt;的状态，写屏障（write barrier）启用的总时间减少，进而让应用代码本身跑得更快。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;提高缓存局部性：&lt;/strong&gt; 被 &lt;code&gt;runtime.free&lt;/code&gt; 释放的对象立即回收到对应大小类的空闲链表中，下一个相同大小的新对象分配&lt;strong&gt;很可能重用这块内存&lt;/strong&gt;。 这样一来，内存分配/释放形成类似&lt;strong&gt;栈式（LIFO）&lt;/strong&gt; 的模式，新分配的内存地址往往与刚释放的相同，对 CPU 缓存非常友好。相比任由 GC 随机回收、重新从堆中找内存，这种局部性有望进一步提升运行效率。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;减少 GC 停顿和辅助操作：&lt;/strong&gt; 总体上，GC 工作量变小后，STW（stop-the-world）暂停的时间和 GC &lt;strong&gt;辅助运行&lt;/strong&gt;（assist）的触发频率都会降低，让应用更平稳&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;除此之外，新的垃圾回收器 &lt;code&gt;Green Tea&lt;/code&gt; 也可能从这种优化中受益——例如更高的每个span内存利用率，等等。尽管这方面还是推测，但&lt;strong&gt;runtime.free&lt;/strong&gt; 提案的出现显然为未来 GC 和内存优化的融合创造了更多可能。&lt;/p&gt;
&lt;h2 id=&#34;意义与展望开发者获得什么&#34;&gt;意义与展望：开发者获得什么？
&lt;/h2&gt;&lt;p&gt;从开发者的角度来看，runtime.free &lt;em&gt;究竟意味着什么&lt;/em&gt;？一言以蔽之：&lt;strong&gt;性能提升，几乎无需额外付出&lt;/strong&gt;。对于普罗大众的 Go 开发者来说，这个提案&lt;strong&gt;不会改变&lt;/strong&gt;我们日常编码的方式——没有新语法、也无需调用新的 API。所有魔法都发生在幕后：&lt;strong&gt;编译器&lt;/strong&gt;变得更聪明，&lt;strong&gt;运行时/标准库&lt;/strong&gt;替我们多做了一些工作。然而，它的影响可能是深远的：&lt;/p&gt;
&lt;p&gt;首先，这标志着 Go 的内存管理正在探索 &lt;strong&gt;“自动 GC”之外的第三条道路&lt;/strong&gt;。传统上，我们有完全自动的 GC（简单易用但性能牺牲）和手工的内存管理（复杂易出错但性能可控）。而 Go 的 runtime.free 尝试证明，两者并非水火不容：语言运行时本身可以变得更智能，&lt;strong&gt;在保证内存安全的前提下&lt;/strong&gt;，帮我们完成一些人工才能做到的优化。从某种意义上说，Go 正在尝试“靠自己”变得更快，而不是把负担转嫁给开发者。&lt;/p&gt;
&lt;p&gt;其次，对性能敏感的Go程序将直接受益于此。在未来的版本（提案目前计划针对 Go 1.26），当这一实验正式上线后，你或许会发现&lt;strong&gt;某些场景下 GC 压力突然降低&lt;/strong&gt;了。例如，大量使用临时切片进行计算的函数，不再生成那么多短命的垃圾；频繁扩容的 &lt;code&gt;bytes.Buffer&lt;/code&gt;、构建巨型 slice 的代码，在新版标准库里跑得飞快。这些性能改进都是 &lt;strong&gt;“开箱即得”&lt;/strong&gt; 的，开发者甚至不需要知道 runtime.free 的存在，就已经享受到了它的好处。&lt;/p&gt;
&lt;p&gt;当然，runtime.free 仍处于试验和完善阶段。它目前通过 &lt;code&gt;GOEXPERIMENT=runtimefree&lt;/code&gt; 提供，说明官方也在审慎评估其效果和风险。接下来社区会继续打磨细节，确保不会引入难以预料的错误（比如要严格杜绝“提前释放仍在用的对象”这种灾难性情况）。好消息是，到目前为止初步验证并未发现不可逾越的技术障碍，核心团队成员也给予了正面反馈。&lt;/p&gt;
&lt;p&gt;总体而言，runtime.free 提案代表了 Go 内存管理上&lt;strong&gt;务实而具有前瞻性&lt;/strong&gt;的一步。它不追求颠覆性的架构重写，而是聚焦于具体的瓶颈问题，寻求切实的优化突破；它也不牺牲类型安全和简洁性，将复杂度限定在编译器和运行时内部。这种思路一旦被证明行之有效，未来完全可以推广到更多模式（例如&lt;a class=&#34;link&#34; href=&#34;https://go.googlesource.com/proposal/&amp;#43;/refs/changes/55/700255/14/design/74299-runtime-free.md#:~:text=4.%20Recognizing%20,does%20not%20otherwise%20become%20aliased&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;识别更多 &lt;code&gt;append&lt;/code&gt; 循环的场景等&lt;/a&gt;），进一步减少 Go 程序的内存开销和 GC 次数。&lt;/p&gt;
&lt;p&gt;对于普通开发者来说，这意味着&lt;strong&gt;更快的程序&lt;/strong&gt;和&lt;strong&gt;更少的垃圾回收停顿&lt;/strong&gt;，而你依然可以像过去一样专注于业务逻辑，无需为手动内存管理操碎心。随着编译器与运行时不断进化，Go 有望在保持“一键爽跑”的易用性的同时，在性能上再攀新高峰——这一切，值得我们拭目以待。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;引用资料：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Go Issue 【74299】“runtime, cmd/compile: add runtime.free, runtime.freetracked and GOEXPERIMENT=runtimefree”&lt;a class=&#34;link&#34; href=&#34;https://github.com/golang/go/issues/74299#:~:text=updated%20manually%20to%20start%2C%20which,to%20automatically%20recognize%20its%20pattern&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;github.com&lt;/a&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/golang/go/issues/74299#:~:text=%28In%20short%2C%20currently%20,more%20allocations%20per%20benchmark%20loop&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;github.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Go Proposal 设计文档 “Directly freeing user memory to reduce GC work”&lt;a class=&#34;link&#34; href=&#34;https://go.googlesource.com/proposal/&amp;#43;/refs/changes/55/700255/14/design/74299-runtime-free.md#:~:text=For%20example%2C%20,to%20eliminate%20the%20first%20reason&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;go.googlesource.com&lt;/a&gt;&lt;a class=&#34;link&#34; href=&#34;https://go.googlesource.com/proposal/&amp;#43;/refs/changes/55/700255/14/design/74299-runtime-free.md#:~:text=1,Challenges%20here%20include&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;go.googlesource.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Go Issue 【&lt;a class=&#34;link&#34; href=&#34;https://github.com/golang/go/issues/51317#:~:text=Note%2C%202023,recommend%20its%20use%20in%20production&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;#51317&lt;/a&gt;】“proposal: arena: new package providing memory arenas”（已被标记 hold)&lt;/li&gt;
&lt;li&gt;Go Discussions 【70257】“memory regions” (内存区域管理的讨论线程)&lt;/li&gt;
&lt;li&gt;Stack Overflow: &lt;a class=&#34;link&#34; href=&#34;https://stackoverflow.com/questions/72471052/how-to-free-memory-manually-in-golang&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;How to free memory manually in golang&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        </item>
        <item>
        <title>HTTP/3：看似无处不在，实则难觅踪影</title>
        <link>https://huizhou92.com/zh-cn/p/http3%E7%9C%8B%E4%BC%BC%E6%97%A0%E5%A4%84%E4%B8%8D%E5%9C%A8%E5%AE%9E%E5%88%99%E9%9A%BE%E8%A7%85%E8%B8%AA%E5%BD%B1/</link>
        <pubDate>Tue, 11 Nov 2025 18:57:08 +0800</pubDate>
        
        <guid>https://huizhou92.com/zh-cn/p/http3%E7%9C%8B%E4%BC%BC%E6%97%A0%E5%A4%84%E4%B8%8D%E5%9C%A8%E5%AE%9E%E5%88%99%E9%9A%BE%E8%A7%85%E8%B8%AA%E5%BD%B1/</guid>
        <description>&lt;img src="https://httptoolkit.com/images/header-images/http-toolkit-assets/h-sign-opt-640.WEBP" alt="Featured image of post HTTP/3：看似无处不在，实则难觅踪影" /&gt;&lt;p&gt;HTTP/3 的研发至少可追溯至 2016 年，而其底层传输协议 QUIC 更是由 Google 在 2013 年率先提出。如今这两项技术均已确立国际标准：&lt;a class=&#34;link&#34; href=&#34;https://caniuse.com/http3&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;strong&gt;获得 95% 浏览器的支持&lt;/strong&gt;&lt;/a&gt;，**Cloudflare 处理的 HTTP 请求中已有 &lt;a class=&#34;link&#34; href=&#34;https://radar.cloudflare.com/adoption-and-usage&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;32% 采用该协议**&lt;/a&gt;，并且在 HTTP Archive 数据集中，有 &lt;strong&gt;&lt;a class=&#34;link&#34; href=&#34;https://almanac.httparchive.org/en/2024/http#discovering-http3-support&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;35% 的网站&lt;/a&gt;&lt;/strong&gt; 宣称支持HTTP/3 (通过 alt-svc 或 DNS）。&lt;/p&gt;
&lt;p&gt;我们不仅成功开发出全新一代 HTTP 协议，更已将超三分之一的网络流量迁移至该协议——这堪称里程碑式的进展。&lt;/p&gt;
&lt;p&gt;然而矛盾的是，包括 Node.js、Go、Rust、Python 和 Ruby 在内的主流编程语言，其标准库均未内置对 QUIC 或 HTTP/3 的支持。Curl 虽然近期&lt;a class=&#34;link&#34; href=&#34;https://curl.se/docs/http3.html&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;新增了相关功能&lt;/a&gt;，但仍标记为实验性质且在大多数发行版中默认禁用。某些语言虽有第三方实现库，但均处于实验阶段，且无法与核心网络 API 协同工作。更值得注意的是，尽管移动网络是 HTTP/3 的关键应用场景，Android 主流 HTTP 库 OkHttp &lt;a class=&#34;link&#34; href=&#34;https://github.com/square/okhttp/blob/59cbf64f6ba98e2c8f95bf9db41dc47ad2232f94/okhttp/src/commonJvmAndroid/kotlin/okhttp3/Protocol.kt#L86-L94&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;仍明确不支持该协议&lt;/a&gt;。Nginx 仅提供&lt;a class=&#34;link&#34; href=&#34;https://nginx.org/en/docs/quic.html&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;实验性模块&lt;/a&gt;且默认关闭，Apache 既无支持计划也未公布路线图，而 Kubernetes 最流行的反向代理 Ingress-Nginx 更是&lt;a class=&#34;link&#34; href=&#34;https://github.com/kubernetes/ingress-nginx/issues/4760&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;完全放弃了支持计划&lt;/a&gt;，将相关功能移交至尚未发布的新一代项目。&lt;/p&gt;
&lt;p&gt;事实上，目前几乎找不到能完整支持 HTTP/3 的流行开源工具——这项技术的推广部署仍处于萌芽阶段。&lt;/p&gt;
&lt;p&gt;这种矛盾现象背后究竟隐藏着什么？&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;本文假设读者已了解 HTTP/1.1、HTTP/2 与 HTTP/3 的核心差异。如需入门资料，curl 创始人 Daniel Stenberg 撰写的 &lt;a class=&#34;link&#34; href=&#34;https://http2-explained.haxx.se/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;http2-explained&lt;/a&gt; 与 &lt;a class=&#34;link&#34; href=&#34;https://http3-explained.haxx.se/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;http3-explained&lt;/a&gt; 是绝佳参考。&lt;/em&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;让我们回溯根本：为什么这很重要？如果浏览器和大型 CDN 已支持 HTTP/3，其他客户端或服务端实现是否还有必要跟进？&lt;/p&gt;
&lt;p&gt;有观点认为，&lt;a class=&#34;link&#34; href=&#34;https://byroot.github.io/ruby/performance/2025/02/24/http2-past-the-load-balancer.html&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;在负载均衡器之后使用 HTTP/2 意义有限&lt;/a&gt;。其核心论点是：H&lt;strong&gt;TTP/2 的多路复用主要解决延迟与&lt;a class=&#34;link&#34; href=&#34;https://en.wikipedia.org/wiki/Head-of-line_blocking&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;队头阻塞 (Head-of-line blocking)&lt;/a&gt; 问题，但在延迟极低的内部网络中，通过长连接即可规避这些问题&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;这个论点同样适用于 HTTP/3：它对高延迟、多请求的 浏览器-CDN 场景有益，但对其他场景价值有限。但即使只考虑 HTTP/1.1 与 HTTP/2，多路复用优势的现实情况也更加复杂：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;响应延迟不仅来自网络传输：服务端处理缓慢同样会阻塞 TCP 连接&lt;/li&gt;
&lt;li&gt;负载均衡器常与后端服务异地部署（例如通过全球 CDN 提供服务时，动态请求仍需回源至独立后端）&lt;/li&gt;
&lt;li&gt;长连接 TCP 连接并不可靠：即使在数据中心内，网络故障也时有发生，“保持连接”只是理想状态。HTTP 协议本身也会强制中断连接（如响应体传输中途失败时）&lt;/li&gt;
&lt;li&gt;流量波动会导致 TCP 连接数失衡：要么长期维持冗余连接池，要么在峰值时建立新连接，面临慢启动、往返延迟与 TLS 握手开销&lt;/li&gt;
&lt;li&gt;非网站类流量（移动应用、API 服务、物联网设备）同样面临网络延迟与服务端阻塞问题，这些场景都能从 HTTP/2/3 中获益&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;除多路复用外，HTTP/2 还有更多跨场景优势：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;头部压缩（HTTP/2 的 &lt;a class=&#34;link&#34; href=&#34;https://blog.cloudflare.com/hpack-the-silent-killer-feature-of-http-2/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;HPACK&lt;/a&gt; 与 HTTP/3 的 &lt;a class=&#34;link&#34; href=&#34;https://datatracker.ietf.org/doc/rfc9204/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;QPACK&lt;/a&gt;）显著减少传输数据量，对内部长连接效果尤为明显&lt;/li&gt;
&lt;li&gt;双向流通信（仅 HTTP/2/3 支持）开启全新交互模式，gRPC 即基于此特性构建，类似 WebSocket 但完全兼容 HTTP 语义&lt;/li&gt;
&lt;li&gt;请求优先级控制允许服务端优化资源分配，这对负载均衡器与后端通信同样重要&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;HTTP/3 更在以下方面实现突破：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;通过取消 TCP 严格包序，使单个流独立传输，避免流间阻塞&lt;/li&gt;
&lt;li&gt;结合 TLS 1.3 与 QUIC 实现 &lt;a class=&#34;link&#34; href=&#34;https://blog.cloudflare.com/even-faster-connection-establishment-with-quic-0-rtt-resumption/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;0RTT 握手&lt;/a&gt;，首次请求无需等待 TLS 握手完成&lt;/li&gt;
&lt;li&gt;降低传输开销与连接数，减少客户端能耗与服务端资源消耗&lt;/li&gt;
&lt;li&gt;支持&lt;a class=&#34;link&#34; href=&#34;https://pulse.internetsociety.org/blog/how-quic-helps-you-seamlessly-connect-to-different-networks&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;连接迁移&lt;/a&gt;，IP 变更时保持会话连续性，未来甚至支持多路径传输&lt;/li&gt;
&lt;li&gt;采用 &lt;a class=&#34;link&#34; href=&#34;https://research.google/pubs/bbr-congestion-based-congestion-control-2/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;BBR 拥塞控制&lt;/a&gt;等先进算法，提升网络适应能力&lt;/li&gt;
&lt;li&gt;为 &lt;a class=&#34;link&#34; href=&#34;https://github.com/w3c/webtransport/blob/main/explainer.md&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;WebTransport&lt;/a&gt; 提供基础，实现低延迟双向通信的同时解决 WebSocket 的队头阻塞等问题&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;实际测试数据同样佐证其价值。RequestMetric 的&lt;a class=&#34;link&#34; href=&#34;https://requestmetrics.com/web-performance/http3-is-fast/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;基准测试&lt;/a&gt;显示：&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://httptoolkit.com/images/posts/http-toolkit-assets/requestmetrics-http3-opt-1080.WEBP&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;各类网站在 HTTP/1.1、2、3 下的加载时间对比，显示 HTTP/3 显著提速&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;Fastly 也在实际环境中观测到首字节时间的大幅优化：&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://httptoolkit.com/images/posts/http-toolkit-assets/fastly-http3-opt-1080.WEBP&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Fastly 实测 HTTP/3 降低首字节时间 18%&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;显然，这是一项&lt;strong&gt;具有实质价值&lt;/strong&gt;的技术。&lt;/p&gt;
&lt;p&gt;既然 HTTP/3 已完成标准化、获得广泛支持并经过实践检验，没理由不让所有开发者都能通过常用开发工具链享受这些技术红利。&lt;/p&gt;
&lt;h2 id=&#34;割裂的网络&#34;&gt;割裂的网络
&lt;/h2&gt;&lt;p&gt;现实却截然相反：尽管技术优势明显且网络流量占比显著，大多数开发者仍难以端到端部署 HTTP/3。这种现象折射出互联网长期存在的分层现状。如今的网络流量已分化为两种形态：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;超大规模流量&lt;/strong&gt;：主流浏览器与特定移动应用通过精心调优的客户端，与少数科技巨头的自有基础设施或大型 CDN 通信&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;长尾流量&lt;/strong&gt;：后端服务、中小型应用、物联网设备、学术研究等多样化场景，依赖开源生态与共享技术栈&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这两大阵营的核心差异包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;长尾流量规模更大： &lt;a class=&#34;link&#34; href=&#34;https://almanac.httparchive.org/en/2024/cdn#cdn-adoption&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;67%&lt;/a&gt; 的网页请求直连源站，Cloudflare 2024 年数据表明其 &lt;a class=&#34;link&#34; href=&#34;https://blog.cloudflare.com/application-security-report-2024-update/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;30% 流量来自自动化程序，60% 属于 API 调用&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;长尾生态天然碎片化，主要依赖志愿维护的开源项目&lt;/li&gt;
&lt;li&gt;超大规模阵营由少数利益相关方主导，能快速协调标准制定与落地&lt;/li&gt;
&lt;li&gt;商业动机高度集中：性能毫秒级提升直接关联企业收益&lt;/li&gt;
&lt;li&gt;长尾完全依赖开源实现，而超大规模玩家拥有自研定制的实力与资源&lt;/li&gt;
&lt;li&gt;版本迭代速度差异巨大：长尾工具注重稳定性，超大规模阵营追求快速迭代&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这种分化并非善恶对立——从工程角度，HTTP/3 正是跨组织协作的卓越成果。但问题在于：当下一代网络技术由少数群体定义并优先服务自身需求时，大多数开发者只能通过购买 CDN 服务间接获取技术红利，这无疑限制了创新生态的健康发展。&lt;/p&gt;
&lt;h2 id=&#34;openssl-与-quic-的兼容困局&#34;&gt;OpenSSL 与 QUIC 的兼容困局
&lt;/h2&gt;&lt;p&gt;这种分化最具体的体现就是 OpenSSL 对 QUIC 的支持策略。作为最基础的 TLS 库，OpenSSL 的态度直接影响整个开源生态。事件脉络如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;BoringSSL 2018 年即提供 QUIC API&lt;/li&gt;
&lt;li&gt;OpenSSL 长期缺失该功能，催生 QuicTLS 等兼容分支&lt;/li&gt;
&lt;li&gt;现有 HTTP/3 实现生态（Quiche、msh3、nghttp3 等）均基于 BoringSSL 或分支构建&lt;/li&gt;
&lt;li&gt;OpenSSL 3.2 起采用不兼容的实现方案，导致生态分裂&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;curl 的项目现状图清晰展现了这种割裂：&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://httptoolkit.com/images/posts/http-toolkit-assets/http3-components-in-curl-opt-1080.WEBP&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;curl 支持的 HTTP/3 组件体系，现有实现均构建于 OpenSSL 替代方案之上&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;对大多数项目而言，放弃 OpenSSL 转向其他方案成本过高，这导致它们至今无法原生支持 QUIC。Node.js 曾&lt;a class=&#34;link&#34; href=&#34;https://github.com/nodejs/node/issues/57379&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;讨论切换方案&lt;/a&gt;，但考虑到系统兼容性、长期支持等现实因素，最终难以实施。&lt;/p&gt;
&lt;p&gt;这正是双层网络差异的典型体现：&lt;strong&gt;开源工具必须保持向后兼容，而超大规模玩家可以为了技术先进性承担更大变更成本&lt;/strong&gt;。&lt;/p&gt;
&lt;h2 id=&#34;未来走向&#34;&gt;未来走向
&lt;/h2&gt;&lt;p&gt;组织架构差异正在导致互联网技术栈的分裂。虽然长尾场景未必急需 HTTP/3，但若放任不管，可能导致：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;性能差距扩大：超大规模网站在移动网络环境下体验优势加剧&lt;/li&gt;
&lt;li&gt;工具链分化：前端框架等基础设施逐渐以 HTTP/3 为默认假设&lt;/li&gt;
&lt;li&gt;技术壁垒形成：缺乏 HTTP/3 支持可能成为被限流或验证的依据&lt;/li&gt;
&lt;li&gt;生态恶性循环：长尾需求逐渐被技术演进忽略&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所有这些都还有一段距离，而且是相当假设性的！我怀疑其中一些假设会在某种程度上发生，但可能性范围很广。不过值得注意的是，这不仅仅适用于 HTTP/3：少数 CDN 和网络客户端的这种集中和协调很容易在许多其他类型的技术改进中也以类似的方式发生。&lt;/p&gt;
&lt;p&gt;至少对于 HTTP/3 而言，我希望这里能有一个愉快的解决方案来及时改善这种分裂，尽管我不知道它是否会足够快以避免明显的后果。许多 QUIC 和 HTTP/3 的外部库和实验性实现会随着时间的推移而成熟，而且我认为最终 (我真的非常希望) OpenSSL QUIC API 的分裂将得到解决，从而为 许多基于 OpenSSL 的环境中的 QUIC 支持打开大门，要么通过适配器支持这两种方法，要么通过直接支持 OpenSSL 模型的新 HTTP/3 和 QUIC 堆栈。&lt;br&gt;
然而，所有这一切都不会在今天发生，因此不幸的是，如果您想在您的应用程序中端到端地使用 HTTP/3，您可能还需要经历一段时间的艰难时期。敬请关注。&lt;/p&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://huizhou92.com/zh-cn/p/http3%E7%9C%8B%E4%BC%BC%E6%97%A0%E5%A4%84%E4%B8%8D%E5%9C%A8%E5%AE%9E%E5%88%99%E9%9A%BE%E8%A7%85%E8%B8%AA%E5%BD%B1/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;本文长期链接&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;如果您觉得我的博客对你有帮助，请通过 &lt;a class=&#34;link&#34; href=&#34;https://huizhou92.com/index.xml&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;RSS&lt;/a&gt;订阅我。&lt;/li&gt;
&lt;li&gt;或者在&lt;a class=&#34;link&#34; href=&#34;https://x.com/@piaopiaopig&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;X&lt;/a&gt;上关注我。&lt;/li&gt;
&lt;li&gt;如果您有&lt;a class=&#34;link&#34; href=&#34;https://medium.huizhou92.com/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Medium&lt;/a&gt;账号，能给我个关注嘛？我的文章第一时间都会发布在Medium。&lt;/li&gt;
&lt;/ul&gt;
</description>
        </item>
        <item>
        <title>浏览器的地址栏除了输入网址还能干什么？——玩贪吃蛇</title>
        <link>https://huizhou92.com/zh-cn/p/%E6%B5%8F%E8%A7%88%E5%99%A8%E7%9A%84%E5%9C%B0%E5%9D%80%E6%A0%8F%E9%99%A4%E4%BA%86%E8%BE%93%E5%85%A5%E7%BD%91%E5%9D%80%E8%BF%98%E8%83%BD%E5%B9%B2%E4%BB%80%E4%B9%88-%E7%8E%A9%E8%B4%AA%E5%90%83%E8%9B%87/</link>
        <pubDate>Thu, 02 Oct 2025 14:53:26 +0800</pubDate>
        
        <guid>https://huizhou92.com/zh-cn/p/%E6%B5%8F%E8%A7%88%E5%99%A8%E7%9A%84%E5%9C%B0%E5%9D%80%E6%A0%8F%E9%99%A4%E4%BA%86%E8%BE%93%E5%85%A5%E7%BD%91%E5%9D%80%E8%BF%98%E8%83%BD%E5%B9%B2%E4%BB%80%E4%B9%88-%E7%8E%A9%E8%B4%AA%E5%90%83%E8%9B%87/</guid>
        <description>&lt;img src="https://images.hxzhouh.com/blog-images/2025/10/fad79b711c6e6b7146a52d42897cf268.png" alt="Featured image of post 浏览器的地址栏除了输入网址还能干什么？——玩贪吃蛇" /&gt;&lt;h1 id=&#34;浏览器的地址栏除了输入网址还能干什么玩贪吃蛇&#34;&gt;浏览器的地址栏除了输入网址还能干什么？——玩贪吃蛇
&lt;/h1&gt;&lt;h2 id=&#34;当一个十年前的疯狂创意重新走红它揭示了互联网正在失去的东西&#34;&gt;当一个十年前的疯狂创意重新走红，它揭示了互联网正在失去的东西
&lt;/h2&gt;&lt;p&gt;打开Chrome浏览器，在地址栏输入 &lt;a class=&#34;link&#34; href=&#34;https://demian.ferrei.ro/snake#&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://demian.ferrei.ro/snake#&lt;/a&gt; ，然后 你就看到一串奇怪的符号。&lt;br&gt;
是的，你没看错。就盯着你的地址栏，在用箭头键或WASD控制方向。&lt;br&gt;
你会看到一条由奇怪符号组成的小蛇（░░░░░░░⠠⠤⠄⡀░░）在地址栏里爬行，吃掉食物，越长越长。整个游戏就在那个你平时只用来输入网址的细长文本框里运行。&lt;br&gt;
&lt;img src=&#34;https://images.hxzhouh.com/blog-images/2025/10/5ea3b9a9457de17c348e4a8631605888.gif&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Area&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;没错，这是一个完整的贪吃蛇游戏，运行在浏览器的URL地址栏里。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;上周，这个十年前的项目突然在Hacker News上爆红，获得840个点赞和数百条评论。人们的第一反应都是：&amp;ldquo;这太疯狂了，你们是怎么想出来的？&amp;rdquo;&lt;/p&gt;
&lt;p&gt;但更疯狂的是这个游戏背后的故事——以及它揭示的关于现代互联网的真相。&lt;/p&gt;
&lt;h2 id=&#34;一次偶然的好奇心引发了一场技术冒险&#34;&gt;一次偶然的好奇心，引发了一场技术冒险
&lt;/h2&gt;&lt;p&gt;创作者Francisco Uzo回忆起项目的起源时说：&amp;ldquo;我想知道盲文系统是怎么工作的。&amp;rdquo;&lt;/p&gt;
&lt;p&gt;这就是极客精神的起点——&lt;strong&gt;不是为了解决问题，而是纯粹的好奇。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;当他深入研究盲文时，发现了一个迷人的规律：每个盲文符号由2x4的点阵组成，8个点位，每个点有&amp;quot;凸起&amp;quot;或&amp;quot;平坦&amp;quot;两种状态，正好产生2^8=256种可能的组合。而Unicode系统完整收录了所有这256个盲文字符。&lt;/p&gt;
&lt;p&gt;某天的凌晨三点，他突然意识到：&lt;strong&gt;&amp;ldquo;等等，这不就是一个现成的8位像素字符集吗？&amp;rdquo;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这种顿悟的瞬间，正是每个极客最享受的时刻——当两个看似无关的知识点在脑海中碰撞，迸发出创意的火花。&lt;/p&gt;
&lt;p&gt;然后就是典型的极客行为模式：&lt;strong&gt;不管有没有用，先做出来再说&lt;/strong&gt;。&lt;br&gt;
整个实现充满了黑客思维：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;将游戏世界映射到盲文字符网格——每个字符就是一个2x4像素块&lt;/li&gt;
&lt;li&gt;蛇的身体用实心点（⣿）表示，空白区域用空盲文（⠀）表示&lt;/li&gt;
&lt;li&gt;使用JavaScript的&lt;code&gt;history.replaceState()&lt;/code&gt;API实时更新URL而不污染历史记录&lt;/li&gt;
&lt;li&gt;每帧刷新，整个游戏画面就在地址栏里流畅运行&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Francisco的透明度也很&amp;quot;极客&amp;quot;：&amp;ldquo;源代码没有任何压缩或混淆处理。Ctrl+U就能看到全部代码。&amp;ldquo;整个实现只用了几百行JavaScript，简洁而巧妙。&lt;strong&gt;在真正的极客眼中，代码本身就应该是可读的、可学习的、可启发的。&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&#34;等等我的游戏怎么看起来乱七八糟的&#34;&gt;等等，我的游戏怎么看起来乱七八糟的？
&lt;/h2&gt;&lt;p&gt;如果你现在打开这个游戏，很可能会看到一堆奇怪的&lt;code&gt;%20&lt;/code&gt;、反斜杠和乱码。游戏勉强能玩，但画面支离破碎。或者根本不能动起来。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;这不是你的问题，也不是代码的问题。问题在于：十年过去了，浏览器变了。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Francisco在讨论中坦言：&amp;ldquo;这个项目是为十年前的浏览器设计的。自那以后，浏览器进行了一些所谓的&amp;rsquo;安全改进&amp;rsquo;，严重削弱了基于地址栏的游戏能力。&amp;rdquo;&lt;/p&gt;
&lt;p&gt;最致命的改变是：&lt;strong&gt;现代浏览器开始强制转义URL中的所有空格字符。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在游戏的设计中，空盲文字符（⠀）代表空白区域。但现代浏览器把它们转义成&lt;code&gt;%20&lt;/code&gt;或其他字符，导致画面变形。Francisco甚至开发了一套基于Canvas的字体测量系统来检测和替换被转义的字符，但效果仍然有限。&lt;/p&gt;
&lt;p&gt;他在浏览器的问题追踪器中提出了申诉。开发者表示理解和同情，但最终的决定是：&lt;strong&gt;&amp;ldquo;安全性照例压倒了趣味性。&amp;rdquo;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;用户报告的游戏体验差异巨大：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;有人在Chrome上轻松得到2144分&lt;/li&gt;
&lt;li&gt;有人在Firefox上看到满屏乱码&lt;/li&gt;
&lt;li&gt;有人在移动Safari上根本看不到游戏&lt;/li&gt;
&lt;li&gt;有人的浏览历史被每一帧都污染了&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;同一个代码，在不同浏览器上的表现天差地别。这就是现代Web开发的现实。&lt;/p&gt;
&lt;h2 id=&#34;那些年我们在浏览器里玩过的花样&#34;&gt;那些年，我们在浏览器里玩过的花样
&lt;/h2&gt;&lt;p&gt;地址栏贪吃蛇不是唯一一个这类创意项目。Hacker News的讨论中，大家开始回忆起一整个&amp;quot;非常规浏览器游戏&amp;quot;的黄金时代。&lt;/p&gt;
&lt;h3 id=&#34;url里的2048游戏&#34;&gt;&lt;strong&gt;URL里的2048游戏&lt;/strong&gt;
&lt;/h3&gt;&lt;p&gt;有人分享了 &lt;a class=&#34;link&#34; href=&#34;https://aquova.net/games/2048/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://aquova.net/games/2048/&lt;/a&gt; ——将风靡一时的2048益智游戏移植到地址栏中。同样的盲文字符技巧，同样的实时URL更新。&lt;/p&gt;
&lt;h3 id=&#34;浏览器标题栏的贪吃蛇&#34;&gt;&lt;strong&gt;浏览器标题栏的贪吃蛇&lt;/strong&gt;
&lt;/h3&gt;&lt;p&gt;Francisco提到，Reddit上最近有人发布了新版本——把游戏渲染在浏览器标签页的标题里。虽然这是一种&amp;quot;认输方案&amp;rdquo;（放弃了在地址栏显示的坚持），但它确实避免了URL转义的问题。&lt;/p&gt;
&lt;h3 id=&#34;tinyjs贪吃蛇&#34;&gt;&lt;strong&gt;TinyJS贪吃蛇&lt;/strong&gt;
&lt;/h3&gt;&lt;p&gt;2023年，另一个开发者用不同的技术路径实现了类似的&lt;a class=&#34;link&#34; href=&#34;https://danielgjackson.github.io/tinyjs#snake&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;项目&lt;/a&gt;。那时浏览器的限制还没有这么严格。&lt;/p&gt;
&lt;h3 id=&#34;地址栏3d漫游&#34;&gt;&lt;strong&gt;地址栏3D漫游&lt;/strong&gt;
&lt;/h3&gt;&lt;p&gt;有人分享了一个更疯狂的项目：&lt;a class=&#34;link&#34; href=&#34;https://matthew.rayfield.world/articles/games-and-graphics-in-popup-url-bars/#:~:text=The%203D%20Room%ef%bc%89&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;用非常规方式在浏览器中实现交互式3D世界&lt;/a&gt;&lt;br&gt;
评论者说：&amp;ldquo;虽然不是Doom，但你可以在3D世界里四处走动。&amp;rdquo;&lt;/p&gt;
&lt;p&gt;这些项目有什么共同点？&lt;strong&gt;它们都是程序员纯粹出于好奇心和乐趣的创造。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;没有商业价值，没有实际用途，甚至算不上&amp;quot;最佳实践&amp;rdquo;。但它们代表了早期互联网的一种精神：&lt;strong&gt;这是一个可以玩耍、可以实验、可以突破界限的地方。&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&#34;为什么这些项目现在越来越难做了&#34;&gt;为什么这些项目现在越来越难做了？
&lt;/h2&gt;&lt;p&gt;问题不只是空格转义。过去十年，浏览器经历了大量&amp;quot;&lt;strong&gt;安全改进&lt;/strong&gt;&amp;quot;：&lt;/p&gt;
&lt;h3 id=&#34;历史api的限制&#34;&gt;&lt;strong&gt;历史API的限制&lt;/strong&gt;
&lt;/h3&gt;&lt;p&gt;Francisco最初使用&lt;code&gt;history.replaceState()&lt;/code&gt;来更新URL而不创建历史记录。但现代浏览器对这个API的调用频率有严格限制。当游戏帧率太高时，系统被迫降级到&lt;code&gt;location.hash&lt;/code&gt;方案——结果是你的浏览历史被每一帧游戏都污染了。&lt;/p&gt;
&lt;p&gt;一位用户吐槽：&amp;ldquo;我最烦的就是这个。访问某些网站后查看历史记录，发现被我输入的每个按键都创建了条目。这一切都是以&amp;rsquo;用户体验&amp;rsquo;的名义。&amp;rdquo;&lt;/p&gt;
&lt;h3 id=&#34;url长度限制&#34;&gt;&lt;strong&gt;URL长度限制&lt;/strong&gt;
&lt;/h3&gt;&lt;p&gt;不同浏览器对URL长度有不同的限制，从2000字符到几万字符不等。对于需要在URL中编码整个游戏状态的项目来说，这是个硬性天花板。&lt;/p&gt;
&lt;h3 id=&#34;字符集限制&#34;&gt;&lt;strong&gt;字符集限制&lt;/strong&gt;
&lt;/h3&gt;&lt;p&gt;越来越多的特殊Unicode字符被标记为&amp;quot;不安全&amp;quot;，在URL中被自动转义或过滤。这直接打击了所有基于Unicode艺术的创意项目。&lt;/p&gt;
&lt;h3 id=&#34;移动端兼容性&#34;&gt;&lt;strong&gt;移动端兼容性&lt;/strong&gt;
&lt;/h3&gt;&lt;p&gt;许多移动浏览器根本不显示完整的URL，或者在用户滚动时隐藏地址栏。这让地址栏游戏变得不可玩。&lt;/p&gt;
&lt;p&gt;Francisco总结道：&amp;ldquo;自那以后，浏览器的许多改变都让这类项目越来越难实现。我在浏览器问题追踪器中得到了一些同情，但安全性总是优先的。&amp;rdquo;&lt;/p&gt;
&lt;h2 id=&#34;当安全性扼杀了互联网的乐趣&#34;&gt;当安全性扼杀了互联网的乐趣
&lt;/h2&gt;&lt;p&gt;这里出现了一个根本性的矛盾。&lt;/p&gt;
&lt;p&gt;**没人会反对浏览器安全性的提升。**URL注入攻击、跨站脚本漏洞、钓鱼网站——这些是真实存在的威胁，影响着数十亿用户。浏览器开发者有责任保护用户安全。&lt;/p&gt;
&lt;p&gt;但与此同时，每一次&amp;quot;安全改进&amp;quot;都关闭了某些创造性实验的大门。&lt;/p&gt;
&lt;p&gt;Francisco的游戏十年前完美运行，今天却在大多数浏览器上支离破碎。不是因为代码有问题，而是因为浏览器&amp;quot;进化&amp;quot;了。这种&amp;quot;进化&amp;quot;让用户更安全，但也让互联网变得更加统一、更加受控、更加&amp;hellip;&amp;hellip;无聊。&lt;/p&gt;
&lt;h3 id=&#34;失去的不仅仅是游戏&#34;&gt;&lt;strong&gt;失去的不仅仅是游戏&lt;/strong&gt;
&lt;/h3&gt;&lt;p&gt;在Hacker News的讨论中，有人提出了尖锐的批评：&amp;ldquo;为什么人们不去做有用的事情？别滥用互联网。&amp;rdquo;&lt;/p&gt;
&lt;p&gt;这个评论遭到了社区的集体反驳：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;ldquo;制作这类项目的学习价值是巨大的&amp;rdquo;&lt;/li&gt;
&lt;li&gt;&amp;ldquo;创造性休息和技能开发同样重要&amp;rdquo;&lt;/li&gt;
&lt;li&gt;&amp;ldquo;这就是我来Hacker News的原因——100%的黑客精神&amp;rdquo;&lt;/li&gt;
&lt;li&gt;&amp;ldquo;ChatGPT发明不出这个。我爱这种创造力&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;最精辟的回应来自一位用户：&amp;ldquo;对普通人来说这可能不算什么，但对我来说这太疯狂了。你们是怎么想出这些东西的？&amp;rdquo;&lt;/p&gt;
&lt;p&gt;**这就是问题的核心。**这些项目的价值不在于&amp;quot;有用&amp;quot;，而在于它们代表了一种纯粹的创造冲动——因为好奇而探索，因为有趣而创造，因为&amp;quot;为什么不试试&amp;quot;而突破界限。&lt;/p&gt;
&lt;p&gt;当我们为了安全而设置越来越多的限制，我们不只是在关闭安全漏洞，我们也在关闭创新的可能性。&lt;/p&gt;
&lt;h3 id=&#34;权衡是必要的但我们应该意识到代价&#34;&gt;&lt;strong&gt;权衡是必要的，但我们应该意识到代价&lt;/strong&gt;
&lt;/h3&gt;&lt;p&gt;这不是在指责浏览器开发者。他们面对的是极其艰难的权衡决策：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;允许灵活的URL编码 vs. 防止注入攻击&lt;/li&gt;
&lt;li&gt;支持高频API调用 vs. 避免性能问题&lt;/li&gt;
&lt;li&gt;显示任意Unicode字符 vs. 防止钓鱼欺诈&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;没有完美的答案。&lt;/strong&gt; 每个决定都有其代价。&lt;/p&gt;
&lt;p&gt;但我们至少应该意识到并讨论这些代价。当我们说&amp;quot;安全第一&amp;quot;时，我们应该问：&amp;ldquo;我们因此失去了什么？&amp;ldquo;当我们说&amp;quot;用户体验优化&amp;quot;时，我们应该问：&amp;ldquo;这优化了谁的体验？&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Francisco在浏览器问题追踪器中得到的回复是典型的：&amp;ldquo;我理解你的困扰，但安全性必须优先。&amp;ldquo;这个回答在技术层面无可指摘，但它也代表了一种更广泛的趋势：&lt;strong&gt;在现代互联网上，控制和安全正在系统性地压倒创造和乐趣。&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&#34;在ai时代这种人类创造更加珍贵&#34;&gt;在AI时代，这种人类创造更加珍贵
&lt;/h2&gt;&lt;p&gt;当讨论转向AI时，Francisco说了一句发人深省的话：&lt;/p&gt;
&lt;p&gt;&amp;ldquo;这个游戏已经存在十年了，所以很可能在这些AI的训练数据中。机器人也许能复制它，但它们肯定无法享受它！（至少现在还不能）&amp;rdquo;&lt;/p&gt;
&lt;p&gt;这句话触及了当前技术文化的核心焦虑。在ChatGPT可以瞬间生成代码、Copilot可以自动补全函数的时代，什么样的编程项目还值得人类去做？&lt;/p&gt;
&lt;p&gt;答案可能就在这个地址栏贪吃蛇里。&lt;/p&gt;
&lt;p&gt;AI可以看到盲文字符，可以理解Unicode编码，可以生成JavaScript代码。但AI不能体验：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;凌晨三点突然意识到&amp;quot;盲文可以当像素&amp;quot;的那一刻顿悟&lt;/li&gt;
&lt;li&gt;第一次看到蛇在地址栏里动起来时的惊喜&lt;/li&gt;
&lt;li&gt;花几个小时调试字符转义问题时的执着&lt;/li&gt;
&lt;li&gt;十年后看到陌生人喜欢你的项目时的满足感&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这些&lt;strong&gt;纯粹的人类创造体验&lt;/strong&gt;，在AI可以复制几乎所有代码的时代，反而变得更加珍贵。&lt;/p&gt;
&lt;p&gt;Francisco的项目不是为了解决问题而创造，而是为了探索和乐趣而创造。这种动机，这种过程，这种纯粹的创造喜悦——&lt;strong&gt;这是AI永远无法真正拥有的。&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&#34;我们需要更多像这样的项目&#34;&gt;我们需要更多像这样的项目
&lt;/h2&gt;&lt;p&gt;这个地址栏贪吃蛇重新走红，不是因为它特别有用或特别赚钱。它走红是因为它提醒了我们一些重要的事情：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;互联网不应该只是一个高效、安全、标准化的工具。它也应该是一个充满惊喜、可以玩耍、可以实验的地方。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;当Francisco十年前问自己&amp;quot;盲文是怎么工作的&amp;quot;时，他不是在解决商业问题或优化用户体验。他只是好奇，然后他创造了一个让数千人微笑的项目。&lt;/p&gt;
&lt;p&gt;在这个越来越同质化、越来越&amp;quot;安全&amp;rdquo;、越来越被算法优化的互联网上，我们需要更多这样的项目。不是因为它们有用，而是因为它们提醒我们：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;技术的意义不仅仅是效率和安全——它也应该是充满惊奇和乐趣的。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;在这个游戏上你能得多少分？你的浏览器支持得好吗？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;也许更重要的问题是：&lt;strong&gt;这个项目让你想起了什么？你有没有做过类似的&amp;quot;无用但有趣&amp;quot;的创造？&lt;/strong&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Go 官方再谈错误处理：新语法为何迟迟无法落地？</title>
        <link>https://huizhou92.com/zh-cn/p/go-%E5%AE%98%E6%96%B9%E5%86%8D%E8%B0%88%E9%94%99%E8%AF%AF%E5%A4%84%E7%90%86%E6%96%B0%E8%AF%AD%E6%B3%95%E4%B8%BA%E4%BD%95%E8%BF%9F%E8%BF%9F%E6%97%A0%E6%B3%95%E8%90%BD%E5%9C%B0/</link>
        <pubDate>Wed, 04 Jun 2025 20:14:24 +0800</pubDate>
        
        <guid>https://huizhou92.com/zh-cn/p/go-%E5%AE%98%E6%96%B9%E5%86%8D%E8%B0%88%E9%94%99%E8%AF%AF%E5%A4%84%E7%90%86%E6%96%B0%E8%AF%AD%E6%B3%95%E4%B8%BA%E4%BD%95%E8%BF%9F%E8%BF%9F%E6%97%A0%E6%B3%95%E8%90%BD%E5%9C%B0/</guid>
        <description>&lt;p&gt;&lt;img src=&#34;https://images.hxzhouh.com/blog-images/2025/06/0d71cfc56d9aec68cc85fd38ca37e3a7.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Pasted image 20250605093005&#34;
	
	
&gt;&lt;/p&gt;
&lt;h1 id=&#34;go-官方再谈错误处理新语法为何迟迟无法落地&#34;&gt;Go 官方再谈错误处理：新语法为何迟迟无法落地？
&lt;/h1&gt;&lt;p&gt;近日，Go 官方博客发布了一篇名为《&lt;a class=&#34;link&#34; href=&#34;https://go.dev/blog/error-syntax&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;On | No syntactic support for error handling&lt;/a&gt;》的文章。这篇文章没有带来期待中的语法突破，反而再次回顾了 Go 语言过去在错误处理语法上多次尝试的失败经验。&lt;br&gt;
Go 团队为什么发表这么一篇文章？&lt;/p&gt;
&lt;h2 id=&#34;为什么-go-团队反复探讨错误处理&#34;&gt;为什么 Go 团队反复探讨“错误处理”？
&lt;/h2&gt;&lt;p&gt;自诞生以来，Go 语言以简洁明了著称，但却始终背负着一个无法回避的问题：错误处理语法过于冗长。&lt;br&gt;
我们熟悉的 Go 错误处理通常长这样：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;``&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Go&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;printSum&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;b&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;error&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;strconv&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Atoi&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;y&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;strconv&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Atoi&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Println&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;result:&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;x&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;y&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;反复出现的 &lt;code&gt;if err != nil&lt;/code&gt; 显得单调乏味，代码中大量的错误处理重复模式经常被批评为“&lt;strong&gt;机械且冗余&lt;/strong&gt;”。因此，过去数年，Go 团队多次尝试设计更为简洁的新语法，但最终都未能成功落地。&lt;/p&gt;
&lt;h2 id=&#34;曾经尝试却未成功的那些提案&#34;&gt;曾经尝试却未成功的那些提案
&lt;/h2&gt;&lt;p&gt;Go 团队在过去提出了几种备受关注但最终放弃的方案：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;2018年：&lt;a class=&#34;link&#34; href=&#34;https://go.googlesource.com/proposal/&amp;#43;/master/design/go2draft-error-handling.md&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;code&gt;check&lt;/code&gt; 与 &lt;code&gt;handle&lt;/code&gt;&lt;/a&gt; 提案&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;尝试用新关键字简化错误传播，但社区担忧复杂度增加，被否决。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;2019年：&lt;a class=&#34;link&#34; href=&#34;https://github.com/golang/go/issues/32437&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;code&gt;try&lt;/code&gt;&lt;/a&gt; 提案&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;引入内置函数 &lt;code&gt;try&lt;/code&gt;，期望简化语法。但社区认为该方案过于隐式，破坏了代码的明确性，也被搁置。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;2024 年：？&lt;br&gt;
Ian Lance Taylor 参考 Rust 的实现提出了 &lt;a class=&#34;link&#34; href=&#34;https://go.dev/issue/71203&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;“使用 &lt;code&gt;?&lt;/code&gt; ”减少错误处理样板&lt;/a&gt; 也遭遇到了大量的反对意见，  我也水了一篇博客&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这些尝试的失败不仅仅是技术上的，更体现了 Go 社区一种特殊的文化和理念——&lt;strong&gt;明确胜于隐式&lt;/strong&gt;。&lt;/p&gt;
&lt;h2 id=&#34;真正的动机是什么&#34;&gt;真正的动机是什么？
&lt;/h2&gt;&lt;p&gt;细读这篇博客，我们会发现 Go 团队并不是单纯地“回忆过去”，而是在认真回应社区持续发酵的讨论：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;究竟有没有必要为错误处理引入新语法？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;文章的字里行间传递了一个关键的讯号：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;“Go 社区对于新语法的需求，并非简单的‘yes or no’，而是深入涉及到语言设计哲学和社区价值观的问题。”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;换句话说，Go 团队的动机并不是要立即给出一个新提案，而是希望借此引导社区回到初心，认真审视“我们真正需要什么样的语言设计？”&lt;/p&gt;
&lt;h2 id=&#34;明确胜于隐式go-社区无法放弃的原则&#34;&gt;“明确胜于隐式”：Go 社区无法放弃的原则
&lt;/h2&gt;&lt;p&gt;Go 语言的成功，正是因为其清晰且明确的设计哲学。任何新语法都必须经过社区的严格审视：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;新语法能否保持明确性？&lt;/li&gt;
&lt;li&gt;新语法能否带来足够的收益，值得牺牲现有的简单性？&lt;/li&gt;
&lt;li&gt;新语法是否有可能带来其他负面影响？&lt;br&gt;
在这种原则下，每个提案都难免面临严格审视和质疑。这也是过去的尝试频频受挫的根本原因。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;五未来的路在哪里&#34;&gt;五、未来的路在哪里？
&lt;/h2&gt;&lt;p&gt;这篇官方博客所释放的另一个信息是：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Go 团队已经接受了短期内不会推出新错误处理语法的现实。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;他们似乎在暗示：错误处理的语法简化并非完全不可行，但至少现在还未找到完美的答案，社区需要更多时间的探索、实践与反思。&lt;br&gt;
因此，未来很长一段时间内，Go 程序员仍需忍受现有模式的重复。但同时也意味着，Go 语言暂时保持了它清晰、直接、没有魔法的风格。&lt;/p&gt;
&lt;p&gt;Go 团队主动发布这样一篇文章，看似回顾历史、承认失败，实则希望引发社区更深层次的讨论：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;我们究竟希望 Go 变成什么样子？&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;为了简洁，我们愿意牺牲多少明确性？&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;无论你是否赞同当前的处理方式，这样的反思和讨论对于语言生态的健康发展，都是极其重要的。&lt;br&gt;
也许下一次提案会再次失败，也许未来某一天，社区终究找到满意的平衡点。但至少，我们可以肯定的是：&lt;br&gt;
&lt;strong&gt;Go 社区一直在探索，一直在反思，也一直在进步。&lt;/strong&gt;&lt;br&gt;
&lt;em&gt;原文链接：&lt;a class=&#34;link&#34; href=&#34;https://go.dev/blog/error-syntax&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://go.dev/blog/error-syntax&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://huizhou92.com/zh-cn/p/go-%E5%AE%98%E6%96%B9%E5%86%8D%E8%B0%88%E9%94%99%E8%AF%AF%E5%A4%84%E7%90%86%E6%96%B0%E8%AF%AD%E6%B3%95%E4%B8%BA%E4%BD%95%E8%BF%9F%E8%BF%9F%E6%97%A0%E6%B3%95%E8%90%BD%E5%9C%B0/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;本文长期链接&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;如果您觉得我的博客对你有帮助，请通过 &lt;a class=&#34;link&#34; href=&#34;https://huizhou92.com/index.xml&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;RSS&lt;/a&gt;订阅我。&lt;/li&gt;
&lt;li&gt;或者在&lt;a class=&#34;link&#34; href=&#34;https://x.com/@piaopiaopig&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;X&lt;/a&gt;上关注我。&lt;/li&gt;
&lt;li&gt;如果您有&lt;a class=&#34;link&#34; href=&#34;https://medium.huizhou92.com/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Medium&lt;/a&gt;账号，能给我个关注嘛？我的文章第一时间都会发布在Medium。&lt;/li&gt;
&lt;/ul&gt;
</description>
        </item>
        <item>
        <title>Go 的 map 为什么变慢了?.zh-cn</title>
        <link>https://huizhou92.com/zh-cn/p/go-%E7%9A%84-map-%E4%B8%BA%E4%BB%80%E4%B9%88%E5%8F%98%E6%85%A2%E4%BA%86.zh-cn/</link>
        <pubDate>Tue, 20 May 2025 22:18:57 +0800</pubDate>
        
        <guid>https://huizhou92.com/zh-cn/p/go-%E7%9A%84-map-%E4%B8%BA%E4%BB%80%E4%B9%88%E5%8F%98%E6%85%A2%E4%BA%86.zh-cn/</guid>
        <description>&lt;img src="https://images.hxzhouh.com/blog-images/2025/05/a7748f1ab7e01c49c0f35f9591f6cfd3.png" alt="Featured image of post Go 的 map 为什么变慢了?.zh-cn" /&gt;&lt;blockquote&gt;
&lt;p&gt;这篇文章解释了为什么在 Go 1.24 版本中，你的程序可能因为 map 变慢了，以及 Go 团队是怎么计划修复这个问题的。&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Golang 1.24 中最吸引我的功能就是 &lt;strong&gt;SwissMap&lt;/strong&gt;，在以前的非官方实现中，有些场景能够提升50% 的性能，官方的实现中，也有不小的性能提升。&lt;br&gt;
详情参考我以前的文章:&lt;br&gt;
&lt;a class=&#34;link&#34; href=&#34;https://huizhou92.com/zh-cn/p/swisstable-%E4%BC%9A%E6%88%90%E4%B8%BA-golang-std-map%E5%98%9B/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;SwissTable 会成为 Golang std map嘛？&lt;/a&gt;&lt;br&gt;
&lt;a class=&#34;link&#34; href=&#34;https://huizhou92.com/zh-cn/p/go-124-%E7%9A%84-swiss-map%E5%85%BC%E5%AE%B9%E6%80%A7%E6%89%A9%E5%B1%95%E5%93%88%E5%B8%8C%E4%B8%8E%E9%81%97%E7%95%99%E9%97%AE%E9%A2%98/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Go 1.24 的 Swiss Map：兼容性、扩展哈希与遗留问题&lt;/a&gt;&lt;/p&gt;
&lt;!-- more--&gt;
&lt;p&gt;但是 如果你在使用 Go 1.24 时可能会发现&lt;strong&gt;SwissMap&lt;/strong&gt; 没有达到预期的表现，甚至程序运行变慢了，特别是在 Map很大的时候，这不是你的幻觉，确实存在这个问题。&lt;br&gt;
&lt;img src=&#34;https://images.hxzhouh.com/blog-images/2025/05/4b861f761a7ffbfbd6d6a5cad8010276.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Pasted image 20250520225045&#34;
	
	
&gt;&lt;br&gt;
&lt;a class=&#34;link&#34; href=&#34;https://x.com/valyala/status/1879988053076504761&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://x.com/valyala/status/1879988053076504761&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;这个问题记录在 &lt;a class=&#34;link&#34; href=&#34;https://github.com/golang/go/issues/70835&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Issue #70835&lt;/a&gt; 中，开发者们正在努力解决它。&lt;/p&gt;
&lt;h2 id=&#34;问题出在哪&#34;&gt;问题出在哪？
&lt;/h2&gt;&lt;p&gt;Go 的 map 在新版中使用了Swiss Table，它在小 map 和高并发的场景下非常快。但是当 map 很大、数据又不在 CPU 缓存里（也就是说，数据是“冷”的），就会变慢。&lt;/p&gt;
&lt;p&gt;为什么会这样呢？&lt;/p&gt;
&lt;p&gt;因为 SwissMap 的内部结构比较复杂，它会分几层来存储数据：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;首先是一个 map 的头部结构&lt;/li&gt;
&lt;li&gt;它指向一个目录，这个目录是多个 table 的指针组成的列表&lt;/li&gt;
&lt;li&gt;每个 table 里面有控制信息、key 和 value&lt;br&gt;
&lt;img src=&#34;https://images.hxzhouh.com/blog-images/2025/01/784e3f6a0ff90601480a2da80f3cb9d0.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
	
&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;当你查找一个 key 的时候，可能需要进行 &lt;strong&gt;4 到 6 次跳转&lt;/strong&gt;，每一次都可能遇到缓存未命中。这样就会导致 CPU 忙着从内存取数据，速度就慢了。&lt;/p&gt;
&lt;p&gt;像 Prometheus 这样的大型项目就发现了这个问题。他们升级到 Go 1.24 后，CPU 使用率上升了很多，经调查发现就是因为 map 的查找变慢了。&lt;/p&gt;
&lt;h2 id=&#34;是怎么发现的&#34;&gt;是怎么发现的？
&lt;/h2&gt;&lt;p&gt;问题并不是在测试用例里发现的，而是在真实的线上场景中出现的。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用了很大的 map（比如几兆字节大小）&lt;/li&gt;
&lt;li&gt;经常读取 map 里的数据&lt;/li&gt;
&lt;li&gt;数据不在 CPU 的高速缓存里&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Go 团队的工程师 Michael Pratt 通过做很多测试，找到了 map 访问变慢的原因，并在 &lt;a class=&#34;link&#34; href=&#34;https://github.com/golang/go/issues/70835&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Issue #70835&lt;/a&gt; 中详细说明。&lt;/p&gt;
&lt;h2 id=&#34;怎么修&#34;&gt;怎么修？
&lt;/h2&gt;&lt;p&gt;为了让 map 更快，他们计划做以下几件事：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;简化目录结构&lt;/strong&gt;：把原来存指针的列表改成直接存结构，减少一次跳转&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;控制信息更紧凑&lt;/strong&gt;：把控制信息安排得更集中，这样更容易被 CPU 一次加载&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;分离 key 和 value&lt;/strong&gt;：改成“key-key-key + value-value-value”的结构，这样可以优化加载顺序&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;对齐控制字节&lt;/strong&gt;：把控制信息按照 CPU 缓存对齐，减少未命中&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这些改动并不简单，因为会影响到 Go 的运行时核心：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;要确保垃圾回收能正常工作&lt;/li&gt;
&lt;li&gt;map 扩容和缩容逻辑要更新&lt;/li&gt;
&lt;li&gt;要确保对小 map 的性能没有影响&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;哪些地方在讨论这个&#34;&gt;哪些地方在讨论这个？
&lt;/h2&gt;&lt;p&gt;这个问题被广泛讨论和跟踪，可以通过&lt;a class=&#34;link&#34; href=&#34;https://github.com/golang/go/issues/70835&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Issue #70835&lt;/a&gt;  了解更多的 细节。 &lt;a class=&#34;link&#34; href=&#34;https://github.com/golang/go/milestone/122&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Go Release Dashboard&lt;/a&gt; 中已经标记这个问题将会在 Go1.25 中解决&lt;br&gt;
此外 &lt;a class=&#34;link&#34; href=&#34;https://github.com/golang/go/issues/71368&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Issue #71368&lt;/a&gt; 中也讨论了 另一个与内存布局的问题。&lt;/p&gt;
&lt;h2 id=&#34;总结&#34;&gt;总结
&lt;/h2&gt;&lt;p&gt;Go 团队一直在努力让语言运行得更快更稳。SwissMap 是个好改进，但它也带来了新挑战，比如这次的冷缓存性能下降。&lt;br&gt;
Issue #70835 展示了 Go 是如何通过社区反馈不断进步的。感谢像 Prometheus 这样的开源项目，他们的报告帮助 Go 做得更好。&lt;br&gt;
如果一切顺利，Go 1.25 就能把速度和稳定性都带回来。&lt;br&gt;
我们一起期待吧！&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Green Tea GC: Golang 的 ZGC？</title>
        <link>https://huizhou92.com/zh-cn/p/green-tea-gc-golang-%E7%9A%84-zgc/</link>
        <pubDate>Mon, 05 May 2025 16:30:26 +0800</pubDate>
        
        <guid>https://huizhou92.com/zh-cn/p/green-tea-gc-golang-%E7%9A%84-zgc/</guid>
        <description>&lt;img src="https://images.hxzhouh.com/blog-images/2025/05/fb6313678681ee8e97a05ee521bd0e20.png" alt="Featured image of post Green Tea GC: Golang 的 ZGC？" /&gt;&lt;p&gt;近年来，Go 语言的垃圾回收（GC）机制虽然经历了多个版本优化，但它的性能瓶颈，尤其在高并发与大规模内存场景下，依然是开发者关注的重点。最近，Go 官方在 GitHub 上提出的 &lt;strong&gt;Green Tea GC&lt;/strong&gt;（#73581）引发了热议：它能否进一步解决 Go GC 的耗时问题？本文将深入解析 Go GC 的设计、缺点、实测表现，并带你了解 Green Tea GC 的技术突破。&lt;/p&gt;
&lt;!-- more--&gt;
&lt;h2 id=&#34;-go-gc-的设计与实现&#34;&gt;📦 Go GC 的设计与实现
&lt;/h2&gt;&lt;p&gt;自 Go 1.5 起，Go 使用并发标记-清除（concurrent mark-sweep）算法，结合“三色标记”模型与 Yuasa 写屏障。&lt;/p&gt;
&lt;p&gt;简而言之，Go GC 会在后台并发地遍历堆内存，标记可达对象，并逐步清除未被引用的内存块。整个回收过程中，Go 追求&lt;strong&gt;低延迟、低停顿&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt;✅ 并发标记、并发清除&lt;br&gt;
✅ 不会移动对象（即 no compaction）&lt;br&gt;
✅ 按 span（内存块）分批清扫，减少单次 STW（Stop-the-World）时长&lt;/p&gt;
&lt;p&gt;这种设计的直接好处是：应用大部分时间能与 GC 并行工作，最大停顿时间通常低于毫秒级。&lt;/p&gt;
&lt;h2 id=&#34;-go-gc-的已知问题&#34;&gt;🚧 Go GC 的已知问题
&lt;/h2&gt;&lt;p&gt;虽然 Go GC 的延迟表现优秀，但它在耗时和扩展性上仍有几个硬伤，尤其体现在：&lt;/p&gt;
&lt;p&gt;1️⃣ &lt;strong&gt;内存访问低效&lt;/strong&gt;&lt;br&gt;
GC 的标记阶段会跨对象跳跃，导致 CPU 频繁 cache miss、等待内存，约 35% 的 GC CPU 周期被耗在“等内存”。这在 NUMA 架构或多核大内存机器上尤为明显。&lt;/p&gt;
&lt;p&gt;2️⃣ &lt;strong&gt;缺乏分代收集&lt;/strong&gt;&lt;br&gt;
Go GC 没有分代机制，所有对象一视同仁，这在高分配率场景下显得笨重。Pinterest 工程师曾指出，内存压力一旦增大，GC 就会暴增 CPU 消耗，引发延迟激增。&lt;/p&gt;
&lt;p&gt;3️⃣ &lt;strong&gt;频繁 GC 带来的 CPU 占用&lt;/strong&gt;&lt;br&gt;
Twitch 工程团队曾报告：即便在中小堆内存下（&amp;lt;450 MiB），系统稳态下每秒会触发 8–10 次 GC，每分钟累计 400–600 次，GC 占用约 &lt;strong&gt;30% 的 CPU 时间&lt;/strong&gt;。这直接挤压了业务线程的执行空间。&lt;/p&gt;
&lt;h2 id=&#34;-性能测试gc-对-go-程序的影响&#34;&gt;📊 性能测试：GC 对 Go 程序的影响
&lt;/h2&gt;&lt;p&gt;我们来看几个实际基准的变化：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Go 1.3/1.4（并发 GC 前）&lt;/strong&gt;&lt;br&gt;
大堆（10GB+）上的 GC 停顿：以秒计算。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Go 1.5（并发 GC 引入后）&lt;/strong&gt;&lt;br&gt;
相同条件下，GC 停顿压缩到 &amp;lt;1ms。&lt;br&gt;
&lt;img src=&#34;https://images.hxzhouh.com/blog-images/2025/05/8d6a9563ad431b76e34d31077cbcd82b.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Pasted image 20250504161704&#34;
	
	
&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Go 1.6–1.8&lt;/strong&gt;&lt;br&gt;
最大堆 200GB，GC 停顿控制在 20ms 以下，甚至常态 1ms。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这些进步非常亮眼，但注意：&lt;br&gt;
✅ 延迟控制好了&lt;br&gt;
⚠️ 总耗时和 CPU 消耗依然显著，特别是高负载或高分配场景。&lt;/p&gt;
&lt;h2 id=&#34;-green-tea-gc全新优化方案&#34;&gt;🌿 Green Tea GC：全新优化方案
&lt;/h2&gt;&lt;p&gt;面对这些问题，Go 官方提出了 Green Tea GC。它的核心优化点是：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;从单对象扫描，升级为按 span（内存块）批量扫描。&lt;/strong&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;具体来说：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;小对象（≤512B）标记由单个对象粒度提升为 span 粒度。&lt;/li&gt;
&lt;li&gt;每个 span 中，只有首次标记的对象会将整个 span 推入扫描队列。&lt;/li&gt;
&lt;li&gt;GC 扫描阶段批量处理整个 span，极大提升了内存访问局部性。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;此外，Green Tea 改进了并行队列管理，采用类似 Go 调度器的工作窃取机制，进一步提高了多核扩展性。&lt;/p&gt;
&lt;h2 id=&#34;-green-tea-gc-实测表现&#34;&gt;⚡ Green Tea GC 实测表现
&lt;/h2&gt;&lt;p&gt;从初步基准来看，Green Tea GC 带来了有选择性的性能提升：&lt;/p&gt;
&lt;p&gt;✅ &lt;strong&gt;Tile38 基准（高扇出树结构）&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;GC 开销降低约 35%&lt;/li&gt;
&lt;li&gt;吞吐、延迟、内存使用全面优化&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;⚠ &lt;strong&gt;bleve-index 基准（低扇出、频繁变异）&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;对象分布散乱，内存局部性差&lt;/li&gt;
&lt;li&gt;Green Tea 与常规 GC 性能相近，有时略低&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;总结：Green Tea 并非“银弹”，但在内存局部性良好、多核扩展场景下，它展现了明显优势，并为未来 SIMD 加速等硬件优化奠定了基础。&lt;/p&gt;
&lt;h2 id=&#34;-总结&#34;&gt;🏁 总结
&lt;/h2&gt;&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;比较项&lt;/th&gt;
          &lt;th&gt;当前 Go GC&lt;/th&gt;
          &lt;th&gt;Green Tea GC&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;标记粒度&lt;/td&gt;
          &lt;td&gt;单对象&lt;/td&gt;
          &lt;td&gt;span（批量）&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;内存局部性&lt;/td&gt;
          &lt;td&gt;差，随机跳跃&lt;/td&gt;
          &lt;td&gt;高，同 span 内批量&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;多核扩展性&lt;/td&gt;
          &lt;td&gt;受限&lt;/td&gt;
          &lt;td&gt;改进，采用工作窃取队列&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;性能提升&lt;/td&gt;
          &lt;td&gt;已接近低延迟上限&lt;/td&gt;
          &lt;td&gt;某些场景下 GC 耗时降 35%&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;应用适用范围&lt;/td&gt;
          &lt;td&gt;普通场景&lt;/td&gt;
          &lt;td&gt;内存局部性好、分配密集场景&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;对于追求极限性能的开发者，Green Tea GC 提供了一个值得关注的新方向。想要试验 Green Tea，可以在 Go 1.25+ 开启实验标志体验。&lt;/p&gt;
&lt;p&gt;📝 &lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/golang/go/issues/73581&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;GitHub Issue #73581&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://stackoverflow.com/questions/31684862/how-fast-is-the-go-1-5-gc-with-terabytes-of-ram&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://stackoverflow.com/questions/31684862/how-fast-is-the-go-1-5-gc-with-terabytes-of-ram&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://huizhou92.com/zh-cn/p/green-tea-gc-golang-%E7%9A%84-zgc/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;本文长期链接&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;如果您觉得我的博客对你有帮助，请通过 &lt;a class=&#34;link&#34; href=&#34;https://huizhou92.com/index.xml&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;RSS&lt;/a&gt;订阅我。&lt;/li&gt;
&lt;li&gt;或者在&lt;a class=&#34;link&#34; href=&#34;https://x.com/@piaopiaopig&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;X&lt;/a&gt;上关注我。&lt;/li&gt;
&lt;li&gt;如果您有&lt;a class=&#34;link&#34; href=&#34;https://medium.huizhou92.com/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Medium&lt;/a&gt;账号，能给我个关注嘛？我的文章第一时间都会发布在Medium。&lt;/li&gt;
&lt;/ul&gt;
</description>
        </item>
        
    </channel>
</rss>
