<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Git on 小盒子的技术分享</title><link>https://xiaobox.github.io/tags/git/</link><description>Recent content in Git on 小盒子的技术分享</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Mon, 13 Apr 2026 03:56:46 +0000</lastBuildDate><atom:link href="https://xiaobox.github.io/tags/git/index.xml" rel="self" type="application/rss+xml"/><item><title>Postman 越来越臃肿了，我换了个开源的，还能让 AI 帮我写测试</title><link>https://xiaobox.github.io/p/2026-04-13-postman-yue-lai-yue-yong-zhong-le-wo-huan-le-ge-kai-yuan-de/</link><pubDate>Mon, 13 Apr 2026 03:56:46 +0000</pubDate><guid>https://xiaobox.github.io/p/2026-04-13-postman-yue-lai-yue-yong-zhong-le-wo-huan-le-ge-kai-yuan-de/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-04-13-postman-yue-lai-yue-yong-zhong-le-wo-huan-le-ge-kai-yuan-de-/cover.jpg" alt="Featured image of post Postman 越来越臃肿了，我换了个开源的，还能让 AI 帮我写测试" /&gt;&lt;p&gt;Postman 我用了好几年了。&lt;/p&gt;
&lt;p&gt;从最早的 Chrome 插件时代开始用的，那时候它还是个轻量小工具，打开就能测接口，干干净净。但最近这两年，怎么说呢，它开始「端着」了。&lt;/p&gt;
&lt;p&gt;打开就让我登录。不登录不让用。&lt;/p&gt;
&lt;p&gt;好不容易登录了，又弹窗让我升级 Team 版。&lt;/p&gt;
&lt;p&gt;集合还给我存到云端去了。我就测个本地接口，你把我的请求数据往你服务器上传干嘛？&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-04-13-postman-yue-lai-yue-yong-zhong-le-wo-huan-le-ge-kai-yuan-de-/001-293772cd.png"&gt;&lt;/p&gt;
&lt;p&gt;我一直忍着，直到有一天，团队里两个人同时改了同一个集合，Postman 云同步直接给合并冲突了，还没法像 Git 那样 diff 看变更。那天我就想，不行了，得换。&lt;/p&gt;
&lt;p&gt;然后我遇到了 Bruno。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-04-13-postman-yue-lai-yue-yong-zhong-le-wo-huan-le-ge-kai-yuan-de-/002-67da7d91.png"&gt;&lt;/p&gt;
&lt;p&gt;怎么形容呢，就像你一直在用一个越来越臃肿的 IDE，突然有人递给你一个 Vim，告诉你「够用了，而且是你的」。&lt;/p&gt;
&lt;p&gt;Bruno 干了一件特别简单但特别对的事情，它把你的 API 请求存成 .bru 文件，放在你本地文件夹里。&lt;/p&gt;
&lt;p&gt;就是普通的文本文件，打开长这样。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;meta { name: 用户登录 type: http seq: 1}post { url: {{baseUrl}}/auth/login body: json auth: none}headers { Content-Type: application/json}body:json { { &amp;#34;username&amp;#34;: &amp;#34;{{username}}&amp;#34;, &amp;#34;password&amp;#34;: &amp;#34;{{password}}&amp;#34;, &amp;#34;expiresInMins&amp;#34;: 30 }}script:post-response { if (res.status === 200) { bru.setVar(&amp;#34;authToken&amp;#34;, res.body.accessToken); bru.setVar(&amp;#34;userId&amp;#34;, res.body.id); }}tests { test(&amp;#34;登录应该返回 200&amp;#34;, function() { expect(res.status).to.equal(200); }); test(&amp;#34;响应中应该包含 accessToken&amp;#34;, function() { expect(res.body.accessToken).to.be.a(&amp;#34;string&amp;#34;); expect(res.body.accessToken.length).to.be.greaterThan(0); }); test(&amp;#34;响应中应该包含用户基本信息&amp;#34;, function() { expect(res.body.id).to.be.a(&amp;#34;number&amp;#34;); expect(res.body.username).to.equal(&amp;#34;emilys&amp;#34;); expect(res.body.email).to.be.a(&amp;#34;string&amp;#34;); }); test(&amp;#34;响应时间应该小于 3 秒&amp;#34;, function() { expect(res.responseTime).to.be.lessThan(3000); });}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;你看，GET 请求、Header、Body、断言，全都是纯文本。你用任何编辑器都能打开它，改完保存就行。&lt;/p&gt;
&lt;p&gt;这玩意最不同的地方在哪？&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;它可以用 Git 管理。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;以前团队共享 Postman 集合，那叫一个痛苦。谁改了什么不知道，版本对不对不确定，冲突了还没法 resolve。&lt;/p&gt;
&lt;p&gt;现在用 Bruno，接口定义就是文件，扔进 Git 仓库，该 PR 就 PR，该 Code Review 就 Code Review。有人改了某个接口的 Header，diff 里看得一清二楚。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-04-13-postman-yue-lai-yue-yong-zhong-le-wo-huan-le-ge-kai-yuan-de-/003-c38b66fd.png"&gt;&lt;/p&gt;
&lt;p&gt;用程序员已经会的工具，解决程序员的问题。不用学新的协作方式，不用付费，不用担心数据被传到哪个云上。&lt;/p&gt;
&lt;p&gt;Bruno 官网上有一句话我印象特别深，大意是**「我们不会同步你的任何数据到云端，甚至连登录的概念都没有。我们看不到你在 Bruno 里输入了什么，也不会用你的数据训练任何 AI 模型」。**&lt;/p&gt;
&lt;p&gt;在这个年代，一个工具敢这么说，我觉得还是挺硬气的。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-04-13-postman-yue-lai-yue-yong-zhong-le-wo-huan-le-ge-kai-yuan-de-/004-2762e255.png"&gt;&lt;/p&gt;
&lt;p&gt;回到工具本身。说到这里，可能有朋友会想，开源 API 客户端一抓一大把，Insomnia、Hoppscotch、Thunder Client，凭什么是 Bruno？&lt;/p&gt;
&lt;p&gt;我自己用下来，让我有「这玩意不一样」的瞬间，是发现它有 CLI。&lt;/p&gt;
&lt;p&gt;但我说的不是「能在终端里跑测试」这种废话。Newman 也能跑，Postman 自己也有命令行。我说的是另一件事。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Bruno 的 CLI，让 Claude Code 和 Cursor 这种 AI 编程工具，第一次能真正帮你写和跑接口测试。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这话有点大，我用两件刚发生的真事讲一下。&lt;/p&gt;
&lt;p&gt;回到 Bruno 的 CLI 本身。装它就一行，&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;npm i -g @usebruno/cli
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;装完之后命令叫 &lt;code&gt;bru&lt;/code&gt;，跑整个集合的测试是这样，&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;bru run --env production
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;输出长这样。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-04-13-postman-yue-lai-yue-yong-zhong-le-wo-huan-le-ge-kai-yuan-de-/005-95a75ce3.png"&gt;&lt;/p&gt;
&lt;p&gt;干净、彩色、有 ✓ 和 ✗、有总耗时、有失败原因。最重要的是，&lt;strong&gt;这是一段普通的终端命令，输出是普通的文本&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;为什么这件事重要？因为这两个特征，正好是 AI 编程 agent 工作的边界。&lt;/p&gt;
&lt;p&gt;Claude Code、Cursor、Codex 这些工具，它们能干什么？它们能读你的代码文件，能写新的文件，能在终端里跑命令，能读命令的输出。它们不能干什么？它们不能点击 Postman 的按钮，不能在你 GUI 里输入 token，不能登录任何账号。(&lt;strong&gt;或者说不方便，成本高，效率低&lt;/strong&gt;)&lt;/p&gt;
&lt;p&gt;Postman 的核心数据存在云端、操作靠 GUI、协作靠登录，这三件事每一件都把 AI agent 挡在外面。&lt;/p&gt;
&lt;p&gt;而 Bruno 的核心数据是 .bru 文本文件、操作靠 CLI、协作靠 Git。每一件都正好是 AI 最擅长的那种事。&lt;/p&gt;
&lt;p&gt;抽象的说完了，说点具体的。&lt;/p&gt;
&lt;p&gt;我前两天就让 Claude Code 帮我加了一个测试，过程是这样的。&lt;/p&gt;
&lt;p&gt;我跟它说，「我刚加了一个搜索商品的接口，帮我在 bruno 集合里加个测试用例」。&lt;/p&gt;
&lt;p&gt;它干了三件事，全程没问我任何问题。&lt;/p&gt;
&lt;p&gt;第一步，读了我现有的一个 .bru 文件，就是为了搞清楚我用的格式。比如我习惯加哪些 test，断言风格是什么样的。&lt;/p&gt;
&lt;p&gt;第二步，照着这个格式写了一个新的 06-搜索商品.bru 文件，放在我集合的根目录里。请求方法、URL、query 参数、4 条断言，全都给我加上了。&lt;/p&gt;
&lt;p&gt;第三步，它直接执行 &lt;code&gt;bru run 06-搜索商品.bru --env production&lt;/code&gt;，亲眼看着 4 个测试全绿，然后才回我一句「写完了，4/4 通过」。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-04-13-postman-yue-lai-yue-yong-zhong-le-wo-huan-le-ge-kai-yuan-de-/006-f94498cf.png"&gt;&lt;/p&gt;
&lt;p&gt;整个过程我没碰 Bruno 的 GUI，没敲一个字符的代码，没解释什么是 .bru 格式。AI 自己读自己写自己跑自己验证。&lt;/p&gt;
&lt;p&gt;你可能会问，这个事换成 Postman 不行吗？&lt;/p&gt;
&lt;p&gt;我得先把话说清楚，Postman 不是没有 CLI。Newman 一直都在，Postman Collection 也是 JSON 文件，AI 理论上能读能写。这条路是通的。&lt;/p&gt;
&lt;p&gt;但你真去试一下就会发现，那条路上全是石头。&lt;/p&gt;
&lt;p&gt;Postman 的 Collection JSON 格式不是给人手写的。我做了个最朴素的对比，同一个 GET 请求加一条「状态码等于 200」的断言，写成 .bru 是 15 行，导出成 Postman Collection JSON 是 44 行。区别在哪？.bru 里 URL 就是一个字符串 &lt;code&gt;https://api.example.com/users&lt;/code&gt;，Postman JSON 里 URL 被拆成 &lt;code&gt;protocol&lt;/code&gt;、&lt;code&gt;host&lt;/code&gt; 数组、&lt;code&gt;path&lt;/code&gt; 数组三个字段。.bru 里测试代码就是普通 JS，Postman JSON 里测试代码以字符串数组的形式塞在 event 里，每一行 JS 都得加引号、转义、再 JSON 序列化一次。还有一堆 &lt;code&gt;_postman_id&lt;/code&gt;、&lt;code&gt;_exporter_id&lt;/code&gt;、&lt;code&gt;schema&lt;/code&gt; 这种内部元数据，跟你的业务接口毫无关系，但你不写就报错。&lt;/p&gt;
&lt;p&gt;让 AI 写 .bru，它跟写 markdown 一样轻松。让 AI 写 Postman JSON，它更像在翻译一份配置文件，token 烧得多，出错概率高，写完你自己 review 都费劲，git diff 出来三行业务变更夹在二十行格式噪音里。&lt;/p&gt;
&lt;p&gt;所以不是 AI 不能写 Postman 的测试，是 Postman 的格式从一开始就没打算让人手写，更没打算让 AI 直接读写。它假设你有一个 GUI 在中间帮你管理这些内部细节。AI 进来之后，那个假设就有点尴尬了。&lt;/p&gt;
&lt;p&gt;我说的还不是最炸的场景。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;真正让我觉得 Bruno + AI 是 1 + 1 大于 10 的，是调试场景。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;我又试了一次，让 Claude Code 帮我加一个购物车接口的测试。它写完跑了一遍，挂了。&lt;/p&gt;
&lt;p&gt;错误信息是这样的。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;✕ 购物车应该有 totalPrice 字段 expected undefined to be a number✕ 购物车应该有 products 数组 expected undefined to be an array
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;```bash
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;它猜错了字段名。这种事很正常，毕竟它没真的去看接口返回。但接下来才有意思。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;它没有问我、没有让我提供文档、也没有放弃。它自己执行了一条 curl 命令，把购物车接口的真实响应拉了下来。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;```bash
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;curl -s https://dummyjson.com/carts/1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;看到响应里字段是 &lt;code&gt;total&lt;/code&gt; 和 &lt;code&gt;products&lt;/code&gt;，不是 &lt;code&gt;totalPrice&lt;/code&gt; 和 &lt;code&gt;items&lt;/code&gt;。它马上把测试里的字段名改了，再跑一遍，全绿。&lt;/p&gt;
&lt;p&gt;整个调试过程，我做了什么？我什么也没做。我只是看着它一步一步跑完。&lt;/p&gt;
&lt;p&gt;这就是 Bruno + CLI + AI 的真正价值。你的 API 测试不再是一个孤立的、需要你手动维护的负担，而是变成了 AI 可以读、可以写、可以跑、可以调试的一种代码。&lt;/p&gt;
&lt;p&gt;它和你的源码、你的 Git 历史、你的 CI/CD、你的 AI agent，全都是一体的。&lt;/p&gt;
&lt;p&gt;写到这里我猜有人要拍桌子了。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;「等等，国内程序员谁还用 Postman 啊？Apifox 它不香吗？」&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;发，我们专门聊聊 Apifox。&lt;/p&gt;
&lt;p&gt;我必须先承认它真的很强。一体化平台，API 文档、调试、Mock、自动化测试、团队协作全在一个工具里搞定，相当于 Postman + Swagger + JMeter 三合一。界面是中文，文档是中文，国内团队几乎零学习成本。如果你的团队 20 人以上，需要权限管理、需要实时协作、需要统一的 API 文档管理，Apifox 是比 Bruno 更合适的选择。这点我不否认。&lt;/p&gt;
&lt;p&gt;而且 Apifox 不傻，它也在跟上 AI 浪潮。它有 apifox-cli，能在终端里跑测试场景。它甚至专门做了 Apifox MCP Server，可以让 Cursor 和 Claude Desktop 读取 Apifox 项目里的 API 文档，帮你写代码。&lt;/p&gt;
&lt;p&gt;那问题来了，Bruno 和 Apifox 在 AI 这件事上到底有啥不一样？&lt;/p&gt;
&lt;p&gt;我读了一圈 Apifox 的官方文档，发现一个挺有意思的差别。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Apifox 接 AI 的方式，是让 AI 来连接 Apifox。Bruno 接 AI 的方式，是 Bruno 根本就是 AI 已经会读的格式。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;具体说，Apifox 的 AI 工作流是这样的，你的测试数据躺在 Apifox 云端项目里，你装一个 Apifox MCP Server，配置 Cursor 去连这个 MCP，AI 通过 MCP 协议向 Apifox 服务发请求，把 API 文档拿下来，再帮你生成代码。&lt;/p&gt;
&lt;p&gt;Bruno 的 AI 工作流是这样的，你的测试就是项目目录里的 .bru 文件，AI 直接 cat、edit、bru run。完了。&lt;/p&gt;
&lt;p&gt;你看出区别了吗？Apifox 把 AI 当成一个外部工具来对接，所以需要 MCP 这一层中转。而 Bruno 根本不需要 MCP，因为它跟 AI 用的是同一种原生语言：&lt;strong&gt;文本文件加终端命令。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;还有一个更现实的差别。Apifox 的 MCP Server 现在的能力，是让 AI 读取 API 文档来生成代码，它并不能让 AI 直接去编辑你的测试用例。AI 在 Apifox 这边的角色基本上是只读的。&lt;/p&gt;
&lt;p&gt;而我前面演示的那两个场景，Claude Code 帮我写新测试、自动调试、修字段名，那是完整的读写跑改循环。AI 不光在读，还在写，还在跑，还在调试。这是因为 .bru 是普通文件，Edit 工具就能改，Bash 工具就能跑。它中间没有任何一层，所以也没有任何一层会卡住。&lt;/p&gt;
&lt;p&gt;这不是说 Apifox 不好，是两条不同的路。Apifox 选的是「我是平台，AI 来连我」，Bruno 选的是「我什么都不是，我就是文件」。前者适合企业级场景，后者适合代码即测试的开发者工作流。&lt;/p&gt;
&lt;p&gt;我自己是后者。我喜欢我所有的东西都能塞进 Git 仓库，喜欢 AI 直接读我硬盘上的文件，不喜欢任何账号、登录、云端中转。所以我选 Bruno。&lt;/p&gt;
&lt;p&gt;如果你的工作场景更接近企业级 SaaS 那一套，那 Apifox 完全没问题，甚至更合适。但如果你跟我一样，希望让 AI 一句话把接口测试搞定，那 Bruno 这条路确实更顺。&lt;/p&gt;
&lt;p&gt;聊完 Apifox 我们继续。顺便提一下 CI 集成，因为这块也很丝滑。Bruno CLI 支持输出 JUnit XML，GitHub Actions、GitLab CI、Jenkins 直接吃这个格式。我的工作流文件大概长这样。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;- name: 安装 Bruno CLI run: npm install -g @usebruno/cli- name: 跑接口测试 run: bru run --env production --reporter-junit junit.xml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;两步。你的 PR 一提交，所有接口测试自动跑一遍，挂了直接拒绝合并。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-04-13-postman-yue-lai-yue-yong-zhong-le-wo-huan-le-ge-kai-yuan-de-/007-56b53ed9.png"&gt;&lt;/p&gt;
&lt;p&gt;生成的测试执行也挺好看的&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-04-13-postman-yue-lai-yue-yong-zhong-le-wo-huan-le-ge-kai-yuan-de-/008-cc895180.png"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-04-13-postman-yue-lai-yue-yong-zhong-le-wo-huan-le-ge-kai-yuan-de-/009-90333e8c.png"&gt;&lt;/p&gt;
&lt;p&gt;整套东西免费、开源、本地、Git 友好、AI 友好，不用登录、不用付钱、不用担心数据上云。&lt;/p&gt;
&lt;p&gt;安装很简单。桌面版，&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;brew install bruno
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="sb"&gt;```&lt;/span&gt;bash
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;CLI，
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="sb"&gt;```&lt;/span&gt;bash
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;8&lt;/span&gt;&lt;span class="cl"&gt;npm i -g @usebruno/cli
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;用了一周之后我把 Postman 卸了。&lt;/p&gt;
&lt;p&gt;不是它不好，是我不再需要一个登录才能用、集合存在别人云上、免费版各种限制、AI 完全帮不上忙的接口测试工具了。&lt;/p&gt;
&lt;p&gt;我只需要一个文件夹、几个 .bru 文件、一条终端命令，和一个能读懂这一切的 AI。&lt;/p&gt;
&lt;p&gt;有时候工具的进步不是功能越加越多，而是把不该有的东西去掉。Bruno 去掉了登录、去掉了云端、去掉了 GUI 锁定，剩下的全是程序员真正需要的东西。&lt;/p&gt;
&lt;p&gt;而当你把这些不需要的东西去掉之后，你会发现一个意外的副作用，AI 进来了。&lt;/p&gt;
&lt;p&gt;好了就说这么多。&lt;/p&gt;
&lt;p&gt;如果你也受够了 Postman，试试 Bruno。说不定你也会像我一样，用完就回不去了。&lt;/p&gt;
&lt;p&gt;ps:我写的 demo 在这里，你可以拉下来试一下，可以 run 的 ：https://github.com/xiaobox/bruno-demo&lt;/p&gt;</description></item><item><title>全程0人工写代码！干掉低级码农的不是大模型</title><link>https://xiaobox.github.io/p/2026-02-24-quan-cheng-0-ren-gong-xie-dai-ma-gan-diao-di-ji-ma-nong-de-b/</link><pubDate>Tue, 24 Feb 2026 03:46:39 +0000</pubDate><guid>https://xiaobox.github.io/p/2026-02-24-quan-cheng-0-ren-gong-xie-dai-ma-gan-diao-di-ji-ma-nong-de-b/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-02-24-quan-cheng-0-ren-gong-xie-dai-ma-gan-diao-di-ji-ma-nong-de-b/cover.jpg" alt="Featured image of post 全程0人工写代码！干掉低级码农的不是大模型" /&gt;&lt;h1 id="全程0人工写代码干掉低级码农的不是大模型"&gt;&lt;a href="#%e5%85%a8%e7%a8%8b0%e4%ba%ba%e5%b7%a5%e5%86%99%e4%bb%a3%e7%a0%81%e5%b9%b2%e6%8e%89%e4%bd%8e%e7%ba%a7%e7%a0%81%e5%86%9c%e7%9a%84%e4%b8%8d%e6%98%af%e5%a4%a7%e6%a8%a1%e5%9e%8b" class="header-anchor"&gt;&lt;/a&gt;全程0人工写代码！干掉低级码农的不是大模型
&lt;/h1&gt;&lt;p&gt;在当前全行业的 AI 辅助编程浪潮中，大多数工具仍停留在“交互式伴游”阶段，而支付巨头 Stripe 却打造了一套完全无人值守的端到端代码智能体——“小黄人”（Minions）&lt;/p&gt;
&lt;p&gt;小黄人是一个独立打工的“数字员工”。目前的惊人数据是：在 Stripe 内部，&lt;strong&gt;每周有超过 1300 个由小黄人完全生成的 Pull Requests（合并请求）被成功合并。这些代码在最终阶段会经过人类审查，但其中不包含任何人类编写的代码。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;更具挑战的是，Stripe 的代码库高达数亿行，主要使用较冷门的带有 Sorbet 类型的 Ruby 语言，且包含大量 LLM 根本没见过的大型内部自研库。此外，这些代码每年要处理超过 1 万亿美元的支付量，合规与容错要求极高。&lt;/p&gt;
&lt;p&gt;Stripe 是如何让 LLM 驾驭如此庞大且复杂的企业级代码库的？核心答案在于极其强大的定制化工程脚手架。&lt;/p&gt;
&lt;p&gt;以下是小黄人能高效运转的四大核心技术拆解。&lt;/p&gt;
&lt;h3 id="1-极致标准化的预热沙盒devboxes"&gt;&lt;a href="#1-%e6%9e%81%e8%87%b4%e6%a0%87%e5%87%86%e5%8c%96%e7%9a%84%e9%a2%84%e7%83%ad%e6%b2%99%e7%9b%92devboxes" class="header-anchor"&gt;&lt;/a&gt;1 极致标准化的预热沙盒（Devboxes）
&lt;/h3&gt;&lt;p&gt;要让全自动 Agent 大规模并行工作，绝不能让它们跑在开发者杂乱的本地笔记本上。Stripe 的解法是直接复用为人类工程师打造的云端开发机（Devboxes）。&lt;/p&gt;
&lt;p&gt;●&lt;strong&gt;10 秒极速“热启动”&lt;/strong&gt;：这些 Devbox 是 AWS EC2 实例。Stripe 预先配置并预热了一个资源池，里面已经克隆好了巨大的 Git 仓库，预热了 Bazel 构建缓存和类型检查缓存，甚至启动了持续运行的代码生成服务。因此，只要 10 秒钟，小黄人就能拿到一台随时可以运行测试和修改代码的机器。&lt;/p&gt;
&lt;p&gt;●&lt;strong&gt;免弹窗的完全提权&lt;/strong&gt;：为了让小黄人在后台静默运行，它需要无缝执行各种 Shell 命令。因为 Devbox 运行在与生产资源和外部互联网隔离的 QA 环境中，爆炸半径被严格限制，所以系统敢于跳过人类权限确认弹窗，给予小黄人完整的执行自由。&lt;/p&gt;
&lt;p&gt;●&lt;strong&gt;解决并发冲突&lt;/strong&gt;：如果用本地环境，并发运行多个 Agent 需要处理复杂的 git worktrees（这在 Stripe 的庞大代码库中无法扩展）。而在云端，工程师可以轻易地同时为 6 个不同的任务启动 6 个分配了独立 Devbox 的小黄人，实现物理级别的完美隔离&lt;/p&gt;
&lt;h3 id="2--蓝图编排blueprints将大模型装进确定性的盒子里"&gt;&lt;a href="#2--%e8%93%9d%e5%9b%be%e7%bc%96%e6%8e%92blueprints%e5%b0%86%e5%a4%a7%e6%a8%a1%e5%9e%8b%e8%a3%85%e8%bf%9b%e7%a1%ae%e5%ae%9a%e6%80%a7%e7%9a%84%e7%9b%92%e5%ad%90%e9%87%8c" class="header-anchor"&gt;&lt;/a&gt;2 “蓝图”编排（Blueprints）：将大模型装进确定性的盒子里
&lt;/h3&gt;&lt;p&gt;常规的 Agent 往往采用开放的循环机制，任由 LLM 自己决定下一步调什么工具，这极易导致出错和浪费 Token。 Stripe 创造性地引入了**“蓝图”（Blueprints）**状态机机制。蓝图将整个工作流视为一张图，将 LLM 的创造力与确定性的系统代码交织在一起：&lt;/p&gt;
&lt;p&gt;●&lt;strong&gt;确定性节点 vs Agent 节点&lt;/strong&gt;：在蓝图中，像“实现具体任务”或“修复 CI 失败”是让 LLM 自由发挥的 Agent 节点；但是，像“运行配置好的 Linter”或“推送 Git 变更”则是完全不调用 LLM 的纯代码确定性节点。&lt;/p&gt;
&lt;p&gt;●&lt;strong&gt;底线兜底&lt;/strong&gt;：这意味着小黄人无法绕过代码格式化等硬性规范。把大模型“关进受控的盒子里”，不仅极大地节省了 Token，还从系统层面提高了整体可靠性。各团队甚至可以编写自定义的蓝图，来处理复杂的、LLM 辅助的代码库迁移任务&lt;/p&gt;
&lt;h3 id="3-极其克制的上下文投喂规则文件与-toolshed"&gt;&lt;a href="#3-%e6%9e%81%e5%85%b6%e5%85%8b%e5%88%b6%e7%9a%84%e4%b8%8a%e4%b8%8b%e6%96%87%e6%8a%95%e5%96%82%e8%a7%84%e5%88%99%e6%96%87%e4%bb%b6%e4%b8%8e-toolshed" class="header-anchor"&gt;&lt;/a&gt;3 极其克制的上下文投喂：规则文件与 Toolshed
&lt;/h3&gt;&lt;p&gt;面对上亿行代码，如果把所有全局规则都塞给大模型，上下文窗口瞬间就会被撑爆。&lt;/p&gt;
&lt;p&gt;●&lt;strong&gt;按目录生效的局部规则&lt;/strong&gt;：Stripe 几乎只使用作用于特定子目录或文件模式的规则文件。他们巧妙地复用了人类工程师为 Cursor 编写的规则格式。这样，工程师在日常开发中沉淀的最佳实践，小黄人（以及 Claude Code）在遍历文件系统时就能直接动态读取并学习。&lt;/p&gt;
&lt;p&gt;●&lt;strong&gt;MCP 工具棚（Toolshed）&lt;/strong&gt;：小黄人通过模型上下文协议（MCP）获取网络信息（工单、文档、代码搜索等）。Stripe 建立了一个包含近 500 个内部与 SaaS 工具的中央服务器 Toolshed。但为了防止 Agent 分心，系统每次只会为小黄人精心挑选一个“小巧而高度相关”&lt;/p&gt;
&lt;h3 id="4-反馈左移shifting-feedback-left极速纠错循环"&gt;&lt;a href="#4-%e5%8f%8d%e9%a6%88%e5%b7%a6%e7%a7%bbshifting-feedback-left%e6%9e%81%e9%80%9f%e7%ba%a0%e9%94%99%e5%be%aa%e7%8e%af" class="header-anchor"&gt;&lt;/a&gt;4 反馈左移（Shifting Feedback Left）：极速纠错循环
&lt;/h3&gt;&lt;p&gt;无人值守 Agent 成功的关键在于能否实现自我闭环修正。Stripe 为其构建了多层极速反馈循环：&lt;/p&gt;
&lt;p&gt;●&lt;strong&gt;5 秒内的本地验证&lt;/strong&gt;：在小黄人把代码推送到 CI 之前，Devbox 上的后台守护进程会通过启发式算法自动运行相关的 Linter 和类型检查。这个本地节点耗时不到 5 秒，让小黄人在本地极速完成语法纠错。&lt;/p&gt;
&lt;p&gt;●&lt;strong&gt;克制的 CI 测试轮数&lt;/strong&gt;：Stripe 的 CI 拥有超过 300 万个测试用例。推送到 CI 后，系统会运行相关测试，并自动应用已有的修复脚本（Autofixes）。如果还有未修复的错误，报错会发回给小黄人。但为了平衡算力成本、时间与边际收益，小黄人最多只被允许进行 1 到 2 次的 CI 循环试错。之后无论成败，都会将其移交给人类处理，防止其陷入昂贵的死循环&lt;/p&gt;
&lt;h2 id="给我的启示"&gt;&lt;a href="#%e7%bb%99%e6%88%91%e7%9a%84%e5%90%af%e7%a4%ba" class="header-anchor"&gt;&lt;/a&gt;给我的启示
&lt;/h2&gt;&lt;p&gt;基于 Stripe 公开的这些技术细节，我得出了以下几点关于 AI 研发提效的深刻感悟：&lt;/p&gt;
&lt;p&gt;1.“&lt;strong&gt;对人类工程师有益的基础设施，对 LLM 同样有益&lt;/strong&gt;” 这是 Stripe 整个小黄人项目最核心的哲学。Stripe 并没有为了做 AI Agent 去凭空造一套新基建，而是直接将 AI 接入了他们多年打磨的 Devbox 环境、Pre-push hooks 和自动化测试管线中。这给所有企业的启示是：AI Agent 的天花板，取决于你现有工程基础设施的底座。 如果你的人类工程师本地环境经常崩溃、缺乏单测覆盖率、文档陈旧，那么大模型也一样会在这些泥坑里寸步难行。过去在人类开发者体验（Developer Productivity）上的每一分投资，都会在 AI 时代转化为巨大的复利回报。&lt;/p&gt;
&lt;p&gt;2.&lt;strong&gt;放弃追求纯粹的“全能 Agent”&lt;/strong&gt;，用“蓝图”管控不确定性 目前业界过度迷恋让一个 Agent 自主解决所有问题。但 Stripe 的蓝图（Blueprints）设计极其务实：能用一行 Bash 脚本或 Linter 稳定解决的问题（如代码格式化、Git 提交流程），就绝对不让 LLM 消耗 Token 去“推理”。在企业级生产环境中，**混合架构（确定性代码逻辑 + 局部受控的 LLM 节点）**才是保证系统高可靠性（SLA）的唯一出路。&lt;/p&gt;
&lt;p&gt;3.&lt;strong&gt;工程师的日常工作流正在被重塑&lt;/strong&gt; ，在 Stripe，触发小黄人的方式极度符合人体工程学：工程师可以直接在 Slack 的讨论线程里@小黄人，或者在内部的“CI 间歇性失败（Flaky test）”工单中点击一个按钮启动它。我们可以预见，未来的高级工程师将越来越像一个“包工头”：他们在值班（On-call）时并行启动几十个小黄人去处理琐碎的 Bug，自己则专注于审查 PR、设计架构，以及维护和编写能够指导小黄人的局部规则（Cursor rules）。工程师不再逐行敲击代码，而是定义意图并管理基础设施。&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-02-24-quan-cheng-0-ren-gong-xie-dai-ma-gan-diao-di-ji-ma-nong-de-b/001-a01d1a80.png"&gt;&lt;/p&gt;
&lt;h2 id="参考"&gt;&lt;a href="#%e5%8f%82%e8%80%83" class="header-anchor"&gt;&lt;/a&gt;参考
&lt;/h2&gt;&lt;p&gt;●https://stripe.dev/blog/minions-stripes-one-shot-end-to-end-coding-agents&lt;/p&gt;
&lt;p&gt;●https://stripe.dev/blog/minions-stripes-one-shot-end-to-end-coding-agents-part-2&lt;/p&gt;</description></item><item><title>Claude 新王牌 “Skills” 深度解析：让你的 AI 秒变行业专家，告别重复劳动</title><link>https://xiaobox.github.io/p/2025-10-28-claude-xin-wang-pai-skills-shen-du-jie-xi-rang-ni-de-ai-miao/</link><pubDate>Tue, 28 Oct 2025 12:13:19 +0000</pubDate><guid>https://xiaobox.github.io/p/2025-10-28-claude-xin-wang-pai-skills-shen-du-jie-xi-rang-ni-de-ai-miao/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-10-28-claude-xin-wang-pai-skills-shen-du-jie-xi-rang-ni-de-ai-miao/cover.jpg" alt="Featured image of post Claude 新王牌 “Skills” 深度解析：让你的 AI 秒变行业专家，告别重复劳动" /&gt;&lt;h1 id="tldr"&gt;&lt;a href="#tldr" class="header-anchor"&gt;&lt;/a&gt;TLDR
&lt;/h1&gt;&lt;p&gt;Claude Skills 就像是给 Claude 安装的 “专家记忆包”，能将它从一个通用 AI 变为精准执行特定任务的专家。&lt;/p&gt;
&lt;p&gt;●&lt;strong&gt;核心功能&lt;/strong&gt;：将重复性工作流程（如公司品牌风格、代码规范、报告格式）打包成可复用的指令，让 Claude 能自动、可靠地完成任务，无需每次都重复提醒。&lt;/p&gt;
&lt;p&gt;●&lt;strong&gt;工作原理&lt;/strong&gt;：一个 Skill 就是一个带说明书 (SKILL.md) 的文件夹。Claude &lt;strong&gt;只在需要时才会加载完整的指令&lt;/strong&gt;，平时只记忆一个简短的描述。这种设计 &lt;strong&gt;极其节省 Token&lt;/strong&gt;，让你可以安装大量 Skills 而不影响性能。&lt;/p&gt;
&lt;p&gt;●&lt;strong&gt;与工具 (Tools / APIs) 的区别&lt;/strong&gt;：Skills 教会 Claude “如何做” 一件事（内部知识和流程），而工具让 Claude “去做” 一件事（调用外部数据或执行动作）。两者可以互补。&lt;/p&gt;
&lt;p&gt;●&lt;strong&gt;最大优势&lt;/strong&gt;：简单、高效、实用。它将复杂的 AI 定制过程简化为写文档，让 AI 能真正融入并标准化你的日常工作。&lt;/p&gt;
&lt;h1 id="claude-skills-的定位"&gt;&lt;a href="#claude-skills-%e7%9a%84%e5%ae%9a%e4%bd%8d" class="header-anchor"&gt;&lt;/a&gt;Claude Skills 的定位
&lt;/h1&gt;&lt;p&gt;&lt;strong&gt;核心理念：Skills 将通用模型转变为领域专家&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;它是面向具体任务的“技能包”，以文件夹形式存在，通过轻量的说明与可执行工具，让 Claude 在需要时加载并执行，从而实现可重复、可定制的工作流。它们既“简单”（就是 Markdown 和脚本）、又“复杂”（能驱动多步的智能代理任务）。&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-10-28-claude-xin-wang-pai-skills-shen-du-jie-xi-rang-ni-de-ai-miao/001-4f609fba.png"&gt;&lt;/p&gt;
&lt;h1 id="skills-工作原理"&gt;&lt;a href="#skills-%e5%b7%a5%e4%bd%9c%e5%8e%9f%e7%90%86" class="header-anchor"&gt;&lt;/a&gt;Skills 工作原理
&lt;/h1&gt;&lt;p&gt;Skills 的设计核心是简洁与高效，它通过一种名为 “渐进式披露”（Progressive Disclosure）的机制，在不牺牲性能的前提下，赋予 Claude 强大的扩展能力。&lt;/p&gt;
&lt;h2 id="一个-skill-就是一个包含指令的简单文件夹"&gt;&lt;a href="#%e4%b8%80%e4%b8%aa-skill-%e5%b0%b1%e6%98%af%e4%b8%80%e4%b8%aa%e5%8c%85%e5%90%ab%e6%8c%87%e4%bb%a4%e7%9a%84%e7%ae%80%e5%8d%95%e6%96%87%e4%bb%b6%e5%a4%b9" class="header-anchor"&gt;&lt;/a&gt;一个 Skill 就是一个包含指令的简单文件夹
&lt;/h2&gt;&lt;p&gt;一个 Skill 的核心是一个包含 SKILL.md 文件的文件夹。 这个 Markdown 文件使用 YAML Frontmatter 来定义元数据（如名称和描述），文件的主体部分则包含了清晰、分步的任务指南和示例。&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-10-28-claude-xin-wang-pai-skills-shen-du-jie-xi-rang-ni-de-ai-miao/002-c0d73ed9.png"&gt;&lt;/p&gt;
&lt;p&gt;举例： &lt;a class="link" href="https://github.com/anthropics/skills/blob/main/document-skills/pptx/SKILL.md" target="_blank" rel="noopener"
 &gt;https://github.com/anthropics/skills/blob/main/document-skills/pptx/SKILL.md&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="claude-会自动发现并加载相关的-skill"&gt;&lt;a href="#claude-%e4%bc%9a%e8%87%aa%e5%8a%a8%e5%8f%91%e7%8e%b0%e5%b9%b6%e5%8a%a0%e8%bd%bd%e7%9b%b8%e5%85%b3%e7%9a%84-skill" class="header-anchor"&gt;&lt;/a&gt;Claude 会自动发现并加载相关的 Skill
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;无需手动触发 Skill&lt;/strong&gt;。 在会话开始时，Claude 会扫描所有已安装 Skills 的元数据（名称和描述），并将这些简短信息加载到其系统提示中。 当你的请求与某个 Skill 的描述相匹配时，Claude 会自动读取并加载该 Skill 的完整指令。&lt;/p&gt;
&lt;h2 id="渐进式披露-机制使-skills-极为高效"&gt;&lt;a href="#%e6%b8%90%e8%bf%9b%e5%bc%8f%e6%8a%ab%e9%9c%b2-%e6%9c%ba%e5%88%b6%e4%bd%bf-skills-%e6%9e%81%e4%b8%ba%e9%ab%98%e6%95%88" class="header-anchor"&gt;&lt;/a&gt;“渐进式披露” 机制使 Skills 极为高效
&lt;/h2&gt;&lt;p&gt;Skill 通过三层结构（YAML 前言、正文、文件引用）逐步、按需地把信息送入模型上下文，避免一次性塞满，提升效率与 token 经济性。&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-10-28-claude-xin-wang-pai-skills-shen-du-jie-xi-rang-ni-de-ai-miao/003-1e575dbd.png"&gt;&lt;/p&gt;
&lt;p&gt;Skills 的设计极其注重 Token 效率。 初始加载时，每个 Skill 只占用几十个 Token 来存储其元数据。 只有在被触发时，Skill 的详细指令才会进入上下文窗口。 这种按需加载的机制意味着您可以安装大量的 Skills，而不会因为上下文窗口被占满而影响模型性能。 对于更复杂的 Skill，还可以将不同的指令拆分到多个文件中，Claude 只会读取当前任务所需的部分，进一步节省了 Token。&lt;/p&gt;
&lt;h2 id="skills-可包含代码以确保任务执行的可靠性"&gt;&lt;a href="#skills-%e5%8f%af%e5%8c%85%e5%90%ab%e4%bb%a3%e7%a0%81%e4%bb%a5%e7%a1%ae%e4%bf%9d%e4%bb%bb%e5%8a%a1%e6%89%a7%e8%a1%8c%e7%9a%84%e5%8f%af%e9%9d%a0%e6%80%a7" class="header-anchor"&gt;&lt;/a&gt;Skills 可包含代码以确保任务执行的可靠性
&lt;/h2&gt;&lt;p&gt;除了文本指令，Skills 还可以捆绑可执行的 Python 脚本。 对于需要确定性和高可靠性的任务（例如，数据排序或格式验证），让 Claude 直接运行预先写好的代码比实时生成代码更高效、更可靠。 例如，一个用于创建 Slack GIF 的 Skill 包含了一个 Python 脚本，该脚本不仅能生成动图，还能验证其文件大小是否符合 Slack 的上传限制。&lt;/p&gt;
&lt;h2 id="应用平台skills-可跨所有-claude-产品使用"&gt;&lt;a href="#%e5%ba%94%e7%94%a8%e5%b9%b3%e5%8f%b0skills-%e5%8f%af%e8%b7%a8%e6%89%80%e6%9c%89-claude-%e4%ba%a7%e5%93%81%e4%bd%bf%e7%94%a8" class="header-anchor"&gt;&lt;/a&gt;应用平台：Skills 可跨所有 Claude 产品使用
&lt;/h2&gt;&lt;p&gt;Skills 的设计具有良好的可移植性，一次构建，即可在 Claude 的多个产品中使用。&lt;/p&gt;
&lt;p&gt;●&lt;strong&gt;在 Claude.ai 网页版中使用&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;○付费用户（Pro, Max, Team, Enterprise）可以在设置中启用和上传自定义 Skills。 Anthropic 也提供了一些预置的 Skills 用于处理常见的文档格式（如 Word, Excel, PowerPoint, PDF）。&lt;/p&gt;
&lt;p&gt;●&lt;strong&gt;通过 Claude API 为开发者所用&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;○开发者可以通过 Skills API 上传和管理自定义 Skills，并在调用 Messages API 时指定使用。 这使得企业可以将封装了特定业务逻辑的 Skills 集成到自己的应用程序中。&lt;/p&gt;
&lt;p&gt;●&lt;strong&gt;在 Claude Code 中用于本地开发&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;○在 Claude Code（一个集成开发环境）中，Skills 以本地文件系统的形式存在。 开发者可以将 Skills 放在特定目录（~/.claude/skills）下，Claude 会自动发现并使用它们，这非常适合团队通过版本控制系统（如 Git）共享和协作开发 Skills。&lt;/p&gt;
&lt;h2 id="应用场景skills-擅长自动化内部工作流"&gt;&lt;a href="#%e5%ba%94%e7%94%a8%e5%9c%ba%e6%99%afskills-%e6%93%85%e9%95%bf%e8%87%aa%e5%8a%a8%e5%8c%96%e5%86%85%e9%83%a8%e5%b7%a5%e4%bd%9c%e6%b5%81" class="header-anchor"&gt;&lt;/a&gt;应用场景：Skills 擅长自动化内部工作流
&lt;/h2&gt;&lt;p&gt;Skills 的核心价值在于自动化那些有固定流程和规范的重复性任务，从而大幅提升效率。&lt;/p&gt;
&lt;p&gt;●&lt;strong&gt;案例一：确保文档的品牌风格一致&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;○你可以创建一个 Skill 来封装公司的品牌指南。 当要求 Claude 创建演示文稿或新闻稿时，它会自动调用这个 Skill，确保所用颜色、字体、徽标和行文语气都符合公司标准，无需人工检查和修改。&lt;/p&gt;
&lt;p&gt;●&lt;strong&gt;案例二：自动化财务报告流程&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;○日本电商巨头乐天（Rakuten）使用 Skills 将一项原先需要一整天时间的财务报告流程缩短到了一个小时。 该 Skill 封装了处理多份电子表格、发现异常数据以及根据公司内部流程生成报告的全部逻辑。&lt;/p&gt;
&lt;p&gt;●&lt;strong&gt;案例三：标准化软件开发任务&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;○一个开发团队可以创建 Skills 来统一代码审查标准、生成符合特定架构的样板代码，或指导如何与内部 API 交互。 例如，一个 mcp-builder Skill 可以指导 Claude 如何创建高质量的 MCP 服务器。&lt;/p&gt;
&lt;h2 id="skills-与模型上下文协议mcp的关键区别"&gt;&lt;a href="#skills-%e4%b8%8e%e6%a8%a1%e5%9e%8b%e4%b8%8a%e4%b8%8b%e6%96%87%e5%8d%8f%e8%ae%aemcp%e7%9a%84%e5%85%b3%e9%94%ae%e5%8c%ba%e5%88%ab" class="header-anchor"&gt;&lt;/a&gt;Skills 与模型上下文协议（MCP）的关键区别
&lt;/h2&gt;&lt;p&gt;虽然 Skills 和 MCP（Model Context Protocol）都是扩展 AI 能力的方式，但它们的设计哲学和适用场景截然不同。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;两者的本质区别是：Skills把“人类流程/SOP”转为可触发的模块；MCP把“外部工具/API/数据源”转为可调用的标准接口。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Skills擅长程序化写作、格式化、合规、数据整理等“内部可编码”的流程；MCP擅长访问GitHub、CI/CD、Slack、数据库等外部系统、实时数据与动作。&lt;/p&gt;
&lt;p&gt;●&lt;strong&gt;Skills 重点在于 “如何完成任务” 的内部指令&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;○Skills 更像是给 Claude 的一本 “操作手册”，它告诉模型完成某项任务的具体步骤、最佳实践和注意事项。 它关注的是过程和方法。&lt;/p&gt;
&lt;p&gt;●&lt;strong&gt;MCP 重点在于连接外部工具与数据源&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;○MCP 是一个开放协议，旨在标准化 AI 与外部世界（如数据库、SaaS 工具、API）的交互方式。 它关注的是访问和行动，让 Claude 能够调用外部工具来获取实时信息或执行操作。&lt;/p&gt;
&lt;p&gt;●&lt;strong&gt;Skills 轻量且高效；MCP 可能消耗大量 Tokens&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;○如前所述，Skills 通过 “渐进式披露” 机制实现了极高的 Token 效率。 相比之下，一些 MCP 的实现可能需要在提示中加载数万 Token 的 API 文档和定义，这会严重挤占模型处理实际任务的上下文空间。&lt;/p&gt;
&lt;p&gt;●&lt;strong&gt;Skills 和 MCP 可以协同工作&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;○&lt;strong&gt;Skills 和 MCP 并非相互排斥，而是互补的&lt;/strong&gt;。 在一个复杂的工作流中，您可以使用 MCP 从 GitHub 或公司的数据库中获取实时数据，然后利用 Skill 来分析这些数据并生成符合特定格式的报告。&lt;/p&gt;
&lt;h2 id="快速上手用一个简单的-markdown-文件创建你的第一个-skill"&gt;&lt;a href="#%e5%bf%ab%e9%80%9f%e4%b8%8a%e6%89%8b%e7%94%a8%e4%b8%80%e4%b8%aa%e7%ae%80%e5%8d%95%e7%9a%84-markdown-%e6%96%87%e4%bb%b6%e5%88%9b%e5%bb%ba%e4%bd%a0%e7%9a%84%e7%ac%ac%e4%b8%80%e4%b8%aa-skill" class="header-anchor"&gt;&lt;/a&gt;快速上手：用一个简单的 Markdown 文件创建你的第一个 Skill
&lt;/h2&gt;&lt;p&gt;创建一个基础 Skill 非常简单，您只需要创建一个包含 SKILL.md 文件的文件夹即可。&lt;/p&gt;
&lt;p&gt;以下是一个模板：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;⚡ markdown片段---
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;name: my-first-skill
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;description: 这是一个关于此 Skill 能做什么以及何时使用它的清晰描述。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;# 我的第一个 Skill
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;[在这里添加您的指令，Claude 在激活此 Skill 时会遵循这些指令]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;## 示例
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;- 用法示例 1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;- 用法示例 2
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;只需填写 name 和 description 字段，然后在 Markdown 主体中用自然语言描述操作步骤，一个可用的 Skill 就完成了。&lt;/p&gt;
&lt;h2 id="安全须知使用第三方-skills-时需保持谨慎"&gt;&lt;a href="#%e5%ae%89%e5%85%a8%e9%a1%bb%e7%9f%a5%e4%bd%bf%e7%94%a8%e7%ac%ac%e4%b8%89%e6%96%b9-skills-%e6%97%b6%e9%9c%80%e4%bf%9d%e6%8c%81%e8%b0%a8%e6%85%8e" class="header-anchor"&gt;&lt;/a&gt;安全须知：使用第三方 Skills 时需保持谨慎
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Skills 可以执行代码，这意味着它们拥有强大的能力，同时也带来了安全风险。&lt;/strong&gt; 一个恶意的 Skill 可能会被设计用来执行非预期的操作，例如访问敏感文件或泄露数据。 因此，强烈建议只使用来自可信来源的 Skills，例如由你自己或 Anthropic 创建的。 在使用任何第三方 Skill 之前，请务必仔细审查其包含的所有文件（包括脚本和说明）。&lt;/p&gt;
&lt;h2 id="skills-使-ai-更实用更具组合性"&gt;&lt;a href="#skills-%e4%bd%bf-ai-%e6%9b%b4%e5%ae%9e%e7%94%a8%e6%9b%b4%e5%85%b7%e7%bb%84%e5%90%88%e6%80%a7" class="header-anchor"&gt;&lt;/a&gt;Skills 使 AI 更实用、更具组合性
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Claude Skills 的核心价值在于其设计的简洁性和强大的实用性。&lt;/strong&gt; 它将复杂的 AI 定制过程简化为编写结构化的文本文档，极大地降低了使用门槛。 通过将程序性知识和工作流程封装为可组合、可重用、可共享的模块，Skills 让 Claude 能够真正融入个人和组织的日常工作中，成为一个高效、可靠的专家级助手。 正如一些开发者所言，Skills 这种简单而强大的模式，可能会比更复杂的协议（如 MCP）产生更深远的影响，因为它更贴近 LLM 的工作本质：给出文本，让模型去理解和执行。&lt;/p&gt;
&lt;h2 id="对产品与组织的启示"&gt;&lt;a href="#%e5%af%b9%e4%ba%a7%e5%93%81%e4%b8%8e%e7%bb%84%e7%bb%87%e7%9a%84%e5%90%af%e7%a4%ba" class="header-anchor"&gt;&lt;/a&gt;对产品与组织的启示
&lt;/h2&gt;&lt;p&gt;●&lt;strong&gt;把隐性知识产品化&lt;/strong&gt;：将“新人指南”“最佳实践”沉淀为可执行的技能包，而非散落的文档。这样可提升一致性、可复用性与审计可见性，减少人依赖与输出波动。&lt;/p&gt;
&lt;p&gt;●&lt;strong&gt;逐步披露是上下文治理的关键&lt;/strong&gt;：通过轻量前言、正文指令与按需文件引用，控制信息进入模型的节奏与粒度，显著提升效率与可预测性，降低 token 与推理成本。&lt;/p&gt;
&lt;p&gt;●&lt;strong&gt;流程模块化与组合式设计&lt;/strong&gt;：用 Skills 编排“怎么做”，用 MCP 连接“可用资源”，形成可插拔的自动化生产线。模块边界清晰，更易维护与演进。&lt;/p&gt;
&lt;p&gt;●&lt;strong&gt;标准化驱动品牌与指标口径统一&lt;/strong&gt;：把品牌规范、度量模型（如 LTV/CAC/流失率）和任务拆解统一到技能包，避免临场解读差异，确保跨团队输出一致。&lt;/p&gt;
&lt;p&gt;●&lt;strong&gt;从规格到实现的闭环&lt;/strong&gt;：将 PRD→用户故事→验收标准→开发任务→代码的链路自动化，压缩交付周期，同时让变更可追踪、可回滚、可复盘。&lt;/p&gt;
&lt;h2 id="思维方式的转变"&gt;&lt;a href="#%e6%80%9d%e7%bb%b4%e6%96%b9%e5%bc%8f%e7%9a%84%e8%bd%ac%e5%8f%98" class="header-anchor"&gt;&lt;/a&gt;思维方式的转变
&lt;/h2&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;从“智能补充”到“智能基建”&lt;/strong&gt;：将 Skills 视为组织的智能基础设施，承载标准、权限、日志与治理。&lt;/p&gt;
&lt;h1 id="附录"&gt;&lt;a href="#%e9%99%84%e5%bd%95" class="header-anchor"&gt;&lt;/a&gt;附录
&lt;/h1&gt;&lt;p&gt;●官方提示的 skills : &lt;a class="link" href="https://github.com/anthropics/skills" target="_blank" rel="noopener"
 &gt;https://github.com/anthropics/skills&lt;/a&gt;&lt;/p&gt;</description></item><item><title>快速安装 ClickHouse</title><link>https://xiaobox.github.io/p/2025-07-17-kuai-su-an-zhuang-clickhouse/</link><pubDate>Thu, 17 Jul 2025 09:33:05 +0000</pubDate><guid>https://xiaobox.github.io/p/2025-07-17-kuai-su-an-zhuang-clickhouse/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-07-17-kuai-su-an-zhuang-clickhouse/cover.jpg" alt="Featured image of post 快速安装 ClickHouse" /&gt;&lt;h2 id="概述"&gt;&lt;a href="#%e6%a6%82%e8%bf%b0" class="header-anchor"&gt;&lt;/a&gt;概述
&lt;/h2&gt;&lt;p&gt;我们使用 docker compose 来安装 ClickHouse&lt;/p&gt;
&lt;p&gt;但我们不是裸装 ClickHouse，实际上我们安装的是 ClickStack。有点儿像 elastic-stack 与 elastic search 的关系 ，但并不完全一样。&lt;/p&gt;
&lt;p&gt;ClickStack 是基于 ClickHouse 构建的完整观察性平台，集成了日志、指标、追踪和会话回放功能，提供统一的用户界面和查询能力。因此，ClickStack 是在 ClickHouse 的基础上，结合 HyperDX 提供的前端界面和 OpenTelemetry Collector 实现的完整解决方案。它不仅仅是一个数据库，而是一个集成的观察性平台。&lt;/p&gt;
&lt;p&gt;安装步骤参考官方文档：https://clickhouse.com/docs/zh/use-cases/observability/clickstack/getting-started?loc=use-case-observability&lt;/p&gt;
&lt;h2 id="安装"&gt;&lt;a href="#%e5%ae%89%e8%a3%85" class="header-anchor"&gt;&lt;/a&gt;安装
&lt;/h2&gt;&lt;h3 id="克隆-hyperdx-仓库"&gt;&lt;a href="#%e5%85%8b%e9%9a%86-hyperdx-%e4%bb%93%e5%ba%93" class="header-anchor"&gt;&lt;/a&gt;克隆 HyperDX 仓库
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;git clone https://github.com/hyperdxio/hyperdx.git
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; hyperdx
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# switch to the v2 branch&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;git checkout v2
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;根据自身情况修改配置文件 .env&lt;/p&gt;
&lt;p&gt;我将 &lt;code&gt;HDX_IMAGE_REPO=docker.hyperdx.io&lt;/code&gt; 修改为 &lt;code&gt;HDX_IMAGE_REPO=docker.io&lt;/code&gt; 不然镜像拉不下来&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;# Used by docker-compose.yml
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;HDX_IMAGE_REPO=docker.hyperdx.io
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;IMAGE_NAME=ghcr.io/hyperdxio/hyperdx
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;IMAGE_NAME_DOCKERHUB=hyperdx/hyperdx
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;LOCAL_IMAGE_NAME=ghcr.io/hyperdxio/hyperdx-local
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;LOCAL_IMAGE_NAME_DOCKERHUB=hyperdx/hyperdx-local
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;ALL_IN_ONE_IMAGE_NAME=ghcr.io/hyperdxio/hyperdx-all-in-one
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;ALL_IN_ONE_IMAGE_NAME_DOCKERHUB=hyperdx/hyperdx-all-in-one
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;OTEL_COLLECTOR_IMAGE_NAME=ghcr.io/hyperdxio/hyperdx-otel-collector
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;OTEL_COLLECTOR_IMAGE_NAME_DOCKERHUB=hyperdx/hyperdx-otel-collector
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;CODE_VERSION=2.0.5
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;IMAGE_VERSION_SUB_TAG=.0.5
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;IMAGE_VERSION=2
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;IMAGE_NIGHTLY_TAG=2-nightly
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;IMAGE_LATEST_TAG=latest
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;# Set up domain URLs
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;HYPERDX_API_PORT=8000 #optional (should not be taken by other services)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;HYPERDX_APP_PORT=8080
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;HYPERDX_APP_URL=http://localhost
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;HYPERDX_LOG_LEVEL=debug
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;HYPERDX_OPAMP_PORT=4320
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt;# Otel/Clickhouse config
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt;HYPERDX_OTEL_EXPORTER_CLICKHOUSE_DATABASE=default
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="docker-compose-启动"&gt;&lt;a href="#docker-compose-%e5%90%af%e5%8a%a8" class="header-anchor"&gt;&lt;/a&gt;docker compose 启动
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;docker-compose up -d
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Docker-compose 文件如下：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;hdx-oss&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;services&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# ONLY USED FOR DEMO SSL SETUP&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# nginx:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# image: nginx:1.27.3&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# volumes:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# - ./docker/nginx/nginx.conf:/etc/nginx/nginx.conf&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# - ./docker/nginx/ssl:/etc/nginx/ssl&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# - .volumes/nginx_logs:/var/log/nginx&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# ports:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# - 80:80&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# - 443:443&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# networks:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# - internal&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# depends_on:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# - app&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;db&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;mongo:5.0.14-focal&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;volumes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;.volumes/db:/data/db&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# WARNING: Exposing the database port will make it accessible from outside the container,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# potentially allowing unauthorized access. If you uncomment the ports below,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# ensure to secure your database (e.g., with strong authentication, proper network rules, and firewalls).&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# ports:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# - 27017:27017&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;26&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;networks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;27&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;internal&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;28&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;otel-collector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;29&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${HDX_IMAGE_REPO}/${OTEL_COLLECTOR_IMAGE_NAME_DOCKERHUB}:${IMAGE_VERSION}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;30&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;31&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;CLICKHOUSE_ENDPOINT&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;tcp://ch-server:9000?dial_timeout=10s&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;32&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;HYPERDX_OTEL_EXPORTER_CLICKHOUSE_DATABASE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${HYPERDX_OTEL_EXPORTER_CLICKHOUSE_DATABASE}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;33&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;HYPERDX_LOG_LEVEL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${HYPERDX_LOG_LEVEL}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;34&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;OPAMP_SERVER_URL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;http://app:${HYPERDX_OPAMP_PORT}&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;35&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;36&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="s1"&gt;&amp;#39;13133:13133&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# health_check extension&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;37&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="s1"&gt;&amp;#39;24225:24225&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# fluentd receiver&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;38&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="s1"&gt;&amp;#39;4317:4317&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# OTLP gRPC receiver&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;39&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="s1"&gt;&amp;#39;4318:4318&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# OTLP http receiver&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;40&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="s1"&gt;&amp;#39;8888:8888&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# metrics extension&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;41&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;restart&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;always&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;42&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;networks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;43&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;internal&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;44&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;depends_on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;45&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;ch-server&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;46&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;app&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;47&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${HDX_IMAGE_REPO}/${IMAGE_NAME_DOCKERHUB}:${IMAGE_VERSION}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;48&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;49&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;${HYPERDX_API_PORT}:${HYPERDX_API_PORT}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;50&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;${HYPERDX_APP_PORT}:${HYPERDX_APP_PORT}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;51&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;52&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;FRONTEND_URL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${HYPERDX_APP_URL}:${HYPERDX_APP_PORT}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;53&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;HYPERDX_API_KEY&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${HYPERDX_API_KEY}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;54&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;HYPERDX_API_PORT&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${HYPERDX_API_PORT}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;55&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;HYPERDX_APP_PORT&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${HYPERDX_APP_PORT}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;56&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;HYPERDX_APP_URL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${HYPERDX_APP_URL}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;57&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;HYPERDX_LOG_LEVEL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${HYPERDX_LOG_LEVEL}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;58&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;MINER_API_URL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;http://miner:5123&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;59&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;MONGO_URI&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;mongodb://db:27017/hyperdx&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;60&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;NEXT_PUBLIC_SERVER_URL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;http://127.0.0.1:${HYPERDX_API_PORT}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;61&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;OPAMP_PORT&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${HYPERDX_OPAMP_PORT}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;62&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;OTEL_SERVICE_NAME&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;hdx-oss-api&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;63&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;USAGE_STATS_ENABLED&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${USAGE_STATS_ENABLED:-true}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;64&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;DEFAULT_CONNECTIONS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;65&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;[{&amp;#34;name&amp;#34;:&amp;#34;Local
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;66&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; ClickHouse&amp;#34;,&amp;#34;host&amp;#34;:&amp;#34;http://ch-server:8123&amp;#34;,&amp;#34;username&amp;#34;:&amp;#34;default&amp;#34;,&amp;#34;password&amp;#34;:&amp;#34;&amp;#34;}]&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;67&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;DEFAULT_SOURCES&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;68&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;[{&amp;#34;from&amp;#34;:{&amp;#34;databaseName&amp;#34;:&amp;#34;default&amp;#34;,&amp;#34;tableName&amp;#34;:&amp;#34;otel_logs&amp;#34;},&amp;#34;kind&amp;#34;:&amp;#34;log&amp;#34;,&amp;#34;timestampValueExpression&amp;#34;:&amp;#34;TimestampTime&amp;#34;,&amp;#34;name&amp;#34;:&amp;#34;Logs&amp;#34;,&amp;#34;displayedTimestampValueExpression&amp;#34;:&amp;#34;Timestamp&amp;#34;,&amp;#34;implicitColumnExpression&amp;#34;:&amp;#34;Body&amp;#34;,&amp;#34;serviceNameExpression&amp;#34;:&amp;#34;ServiceName&amp;#34;,&amp;#34;bodyExpression&amp;#34;:&amp;#34;Body&amp;#34;,&amp;#34;eventAttributesExpression&amp;#34;:&amp;#34;LogAttributes&amp;#34;,&amp;#34;resourceAttributesExpression&amp;#34;:&amp;#34;ResourceAttributes&amp;#34;,&amp;#34;defaultTableSelectExpression&amp;#34;:&amp;#34;Timestamp,ServiceName,SeverityText,Body&amp;#34;,&amp;#34;severityTextExpression&amp;#34;:&amp;#34;SeverityText&amp;#34;,&amp;#34;traceIdExpression&amp;#34;:&amp;#34;TraceId&amp;#34;,&amp;#34;spanIdExpression&amp;#34;:&amp;#34;SpanId&amp;#34;,&amp;#34;connection&amp;#34;:&amp;#34;Local
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;69&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; ClickHouse&amp;#34;,&amp;#34;traceSourceId&amp;#34;:&amp;#34;Traces&amp;#34;,&amp;#34;sessionSourceId&amp;#34;:&amp;#34;Sessions&amp;#34;,&amp;#34;metricSourceId&amp;#34;:&amp;#34;Metrics&amp;#34;},{&amp;#34;from&amp;#34;:{&amp;#34;databaseName&amp;#34;:&amp;#34;default&amp;#34;,&amp;#34;tableName&amp;#34;:&amp;#34;otel_traces&amp;#34;},&amp;#34;kind&amp;#34;:&amp;#34;trace&amp;#34;,&amp;#34;timestampValueExpression&amp;#34;:&amp;#34;Timestamp&amp;#34;,&amp;#34;name&amp;#34;:&amp;#34;Traces&amp;#34;,&amp;#34;displayedTimestampValueExpression&amp;#34;:&amp;#34;Timestamp&amp;#34;,&amp;#34;implicitColumnExpression&amp;#34;:&amp;#34;SpanName&amp;#34;,&amp;#34;serviceNameExpression&amp;#34;:&amp;#34;ServiceName&amp;#34;,&amp;#34;bodyExpression&amp;#34;:&amp;#34;SpanName&amp;#34;,&amp;#34;eventAttributesExpression&amp;#34;:&amp;#34;SpanAttributes&amp;#34;,&amp;#34;resourceAttributesExpression&amp;#34;:&amp;#34;ResourceAttributes&amp;#34;,&amp;#34;defaultTableSelectExpression&amp;#34;:&amp;#34;Timestamp,ServiceName,StatusCode,round(Duration/1e6),SpanName&amp;#34;,&amp;#34;traceIdExpression&amp;#34;:&amp;#34;TraceId&amp;#34;,&amp;#34;spanIdExpression&amp;#34;:&amp;#34;SpanId&amp;#34;,&amp;#34;durationExpression&amp;#34;:&amp;#34;Duration&amp;#34;,&amp;#34;durationPrecision&amp;#34;:9,&amp;#34;parentSpanIdExpression&amp;#34;:&amp;#34;ParentSpanId&amp;#34;,&amp;#34;spanNameExpression&amp;#34;:&amp;#34;SpanName&amp;#34;,&amp;#34;spanKindExpression&amp;#34;:&amp;#34;SpanKind&amp;#34;,&amp;#34;statusCodeExpression&amp;#34;:&amp;#34;StatusCode&amp;#34;,&amp;#34;statusMessageExpression&amp;#34;:&amp;#34;StatusMessage&amp;#34;,&amp;#34;connection&amp;#34;:&amp;#34;Local
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;70&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; ClickHouse&amp;#34;,&amp;#34;logSourceId&amp;#34;:&amp;#34;Logs&amp;#34;,&amp;#34;sessionSourceId&amp;#34;:&amp;#34;Sessions&amp;#34;,&amp;#34;metricSourceId&amp;#34;:&amp;#34;Metrics&amp;#34;},{&amp;#34;from&amp;#34;:{&amp;#34;databaseName&amp;#34;:&amp;#34;default&amp;#34;,&amp;#34;tableName&amp;#34;:&amp;#34;&amp;#34;},&amp;#34;kind&amp;#34;:&amp;#34;metric&amp;#34;,&amp;#34;timestampValueExpression&amp;#34;:&amp;#34;TimeUnix&amp;#34;,&amp;#34;name&amp;#34;:&amp;#34;Metrics&amp;#34;,&amp;#34;resourceAttributesExpression&amp;#34;:&amp;#34;ResourceAttributes&amp;#34;,&amp;#34;metricTables&amp;#34;:{&amp;#34;gauge&amp;#34;:&amp;#34;otel_metrics_gauge&amp;#34;,&amp;#34;histogram&amp;#34;:&amp;#34;otel_metrics_histogram&amp;#34;,&amp;#34;sum&amp;#34;:&amp;#34;otel_metrics_sum&amp;#34;,&amp;#34;_id&amp;#34;:&amp;#34;682586a8b1f81924e628e808&amp;#34;,&amp;#34;id&amp;#34;:&amp;#34;682586a8b1f81924e628e808&amp;#34;},&amp;#34;connection&amp;#34;:&amp;#34;Local
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;71&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; ClickHouse&amp;#34;,&amp;#34;logSourceId&amp;#34;:&amp;#34;Logs&amp;#34;,&amp;#34;traceSourceId&amp;#34;:&amp;#34;Traces&amp;#34;,&amp;#34;sessionSourceId&amp;#34;:&amp;#34;Sessions&amp;#34;},{&amp;#34;from&amp;#34;:{&amp;#34;databaseName&amp;#34;:&amp;#34;default&amp;#34;,&amp;#34;tableName&amp;#34;:&amp;#34;hyperdx_sessions&amp;#34;},&amp;#34;kind&amp;#34;:&amp;#34;session&amp;#34;,&amp;#34;timestampValueExpression&amp;#34;:&amp;#34;TimestampTime&amp;#34;,&amp;#34;name&amp;#34;:&amp;#34;Sessions&amp;#34;,&amp;#34;displayedTimestampValueExpression&amp;#34;:&amp;#34;Timestamp&amp;#34;,&amp;#34;implicitColumnExpression&amp;#34;:&amp;#34;Body&amp;#34;,&amp;#34;serviceNameExpression&amp;#34;:&amp;#34;ServiceName&amp;#34;,&amp;#34;bodyExpression&amp;#34;:&amp;#34;Body&amp;#34;,&amp;#34;eventAttributesExpression&amp;#34;:&amp;#34;LogAttributes&amp;#34;,&amp;#34;resourceAttributesExpression&amp;#34;:&amp;#34;ResourceAttributes&amp;#34;,&amp;#34;defaultTableSelectExpression&amp;#34;:&amp;#34;Timestamp,ServiceName,SeverityText,Body&amp;#34;,&amp;#34;severityTextExpression&amp;#34;:&amp;#34;SeverityText&amp;#34;,&amp;#34;traceIdExpression&amp;#34;:&amp;#34;TraceId&amp;#34;,&amp;#34;spanIdExpression&amp;#34;:&amp;#34;SpanId&amp;#34;,&amp;#34;connection&amp;#34;:&amp;#34;Local
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;72&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; ClickHouse&amp;#34;,&amp;#34;logSourceId&amp;#34;:&amp;#34;Logs&amp;#34;,&amp;#34;traceSourceId&amp;#34;:&amp;#34;Traces&amp;#34;,&amp;#34;metricSourceId&amp;#34;:&amp;#34;Metrics&amp;#34;}]&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;73&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;networks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;74&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;internal&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;75&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;depends_on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;76&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;ch-server&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;77&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;db&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;78&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ch-server&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;79&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;clickhouse/clickhouse-server:24-alpine&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;80&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# WARNING: Exposing the database port will make it accessible from outside the container,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;81&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# potentially allowing unauthorized access. If you uncomment the ports below,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;82&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# ensure to secure your database (e.g., with strong authentication, proper network rules, and firewalls).&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;83&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;84&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="m"&gt;8123&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;8123&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# http api&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;85&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="m"&gt;9050&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;9000&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# native&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;86&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# environment:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;87&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# default settings&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;88&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT: 1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;89&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;volumes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;90&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;./docker/clickhouse/local/config.xml:/etc/clickhouse-server/config.xml&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;91&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;./docker/clickhouse/local/users.xml:/etc/clickhouse-server/users.xml&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;92&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;./empty.xml:/etc/clickhouse-server/users.d/default-password.xml&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;93&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;.volumes/ch_data:/var/lib/clickhouse&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;94&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;.volumes/ch_logs:/var/log/clickhouse-server&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;95&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;restart&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;on&lt;/span&gt;-&lt;span class="l"&gt;failure&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;96&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;networks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;97&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;internal&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;98&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;networks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;99&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;internal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;注意：environment 部分我注释掉了，另外 加了一行：&lt;code&gt;./empty.xml:/etc/clickhouse-server/users.d/default-password.xml&lt;/code&gt; 作用是解决 clickhouse 连接异常的问题。&lt;/p&gt;
&lt;p&gt;通过 &lt;code&gt;/data/clickhouse/hyperdx/docker/clickhouse/local/users.xml&lt;/code&gt; 可以看到 clickhouse 的账户信息：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;&amp;lt;?xml version=&amp;#34;1.0&amp;#34;?&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;clickhouse&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;profiles&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;default&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;max_memory_usage&amp;gt;&lt;/span&gt;10000000000&lt;span class="nt"&gt;&amp;lt;/max_memory_usage&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;use_uncompressed_cache&amp;gt;&lt;/span&gt;0&lt;span class="nt"&gt;&amp;lt;/use_uncompressed_cache&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;load_balancing&amp;gt;&lt;/span&gt;in_order&lt;span class="nt"&gt;&amp;lt;/load_balancing&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;log_queries&amp;gt;&lt;/span&gt;1&lt;span class="nt"&gt;&amp;lt;/log_queries&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/default&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/profiles&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;users&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;default&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;password_sha256_hex&amp;gt;&lt;/span&gt;2d964690ad5ac2d2f78bebadc30895bc519969ffcef4d3c9e7ff04ee1c765d96&lt;span class="nt"&gt;&amp;lt;/password_sha256_hex&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;profile&amp;gt;&lt;/span&gt;default&lt;span class="nt"&gt;&amp;lt;/profile&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;networks&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;ip&amp;gt;&lt;/span&gt;::/0&lt;span class="nt"&gt;&amp;lt;/ip&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/networks&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;quota&amp;gt;&lt;/span&gt;default&lt;span class="nt"&gt;&amp;lt;/quota&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/default&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;api&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;password&amp;gt;&lt;/span&gt;api&lt;span class="nt"&gt;&amp;lt;/password&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;profile&amp;gt;&lt;/span&gt;default&lt;span class="nt"&gt;&amp;lt;/profile&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;networks&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;ip&amp;gt;&lt;/span&gt;::/0&lt;span class="nt"&gt;&amp;lt;/ip&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;26&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/networks&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;27&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;quota&amp;gt;&lt;/span&gt;default&lt;span class="nt"&gt;&amp;lt;/quota&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;28&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/api&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;29&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;worker&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;30&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;password&amp;gt;&lt;/span&gt;worker&lt;span class="nt"&gt;&amp;lt;/password&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;31&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;profile&amp;gt;&lt;/span&gt;default&lt;span class="nt"&gt;&amp;lt;/profile&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;32&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;networks&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;33&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;ip&amp;gt;&lt;/span&gt;::/0&lt;span class="nt"&gt;&amp;lt;/ip&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;34&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/networks&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;35&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;quota&amp;gt;&lt;/span&gt;default&lt;span class="nt"&gt;&amp;lt;/quota&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;36&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/worker&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;37&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/users&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;38&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;39&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;quotas&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;40&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;default&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;41&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;interval&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;42&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;duration&amp;gt;&lt;/span&gt;3600&lt;span class="nt"&gt;&amp;lt;/duration&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;43&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;queries&amp;gt;&lt;/span&gt;0&lt;span class="nt"&gt;&amp;lt;/queries&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;44&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;errors&amp;gt;&lt;/span&gt;0&lt;span class="nt"&gt;&amp;lt;/errors&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;45&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;result_rows&amp;gt;&lt;/span&gt;0&lt;span class="nt"&gt;&amp;lt;/result_rows&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;46&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;read_rows&amp;gt;&lt;/span&gt;0&lt;span class="nt"&gt;&amp;lt;/read_rows&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;47&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;execution_time&amp;gt;&lt;/span&gt;0&lt;span class="nt"&gt;&amp;lt;/execution_time&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;48&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/interval&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;49&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/default&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;50&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/quotas&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;51&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;/clickhouse&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;密码用 sha256sum 处理过&lt;/p&gt;
&lt;p&gt;可以这样生成：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;echo -n &amp;#39;你的密码&amp;#39; | sha256sum
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="运行"&gt;&lt;a href="#%e8%bf%90%e8%a1%8c" class="header-anchor"&gt;&lt;/a&gt;运行
&lt;/h2&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-07-17-kuai-su-an-zhuang-clickhouse/001-1d333d30.png"&gt;点击登录跳转至首页&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-07-17-kuai-su-an-zhuang-clickhouse/002-f8c34343.png"&gt;&lt;/p&gt;
&lt;h2 id="客户端连接"&gt;&lt;a href="#%e5%ae%a2%e6%88%b7%e7%ab%af%e8%bf%9e%e6%8e%a5" class="header-anchor"&gt;&lt;/a&gt;客户端连接
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;用户名：default&lt;/li&gt;
&lt;li&gt;密码： 你的密码&lt;/li&gt;
&lt;li&gt;端口：8123&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-07-17-kuai-su-an-zhuang-clickhouse/003-39f76698.png"&gt;&lt;/p&gt;
&lt;h2 id="数据库初始化"&gt;&lt;a href="#%e6%95%b0%e6%8d%ae%e5%ba%93%e5%88%9d%e5%a7%8b%e5%8c%96" class="header-anchor"&gt;&lt;/a&gt;数据库初始化
&lt;/h2&gt;&lt;p&gt;初始化 sql 脚本&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt;/* 1. 创建数据库（如已存在可先 DROP DATABASE IF EXISTS testdb） */&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;DATABASE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NOT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;EXISTS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;testdb&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt;/* 2. 维度表：用户 */&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NOT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;EXISTS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;testdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;UInt32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user_name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;signup_date&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ENGINE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MergeTree&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;ORDER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt;/* 3. 维度表：页面 */&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NOT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;EXISTS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;testdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pages&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;page_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;UInt32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;page_url&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ENGINE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MergeTree&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;ORDER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;page_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt;/* 4. 事实表：页面访问日志 */&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NOT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;EXISTS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;testdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pageviews&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;event_date&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;event_time&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;UInt32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;page_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;UInt32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;26&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;UInt32&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;-- 停留秒数
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;27&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ENGINE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MergeTree&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;28&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;PARTITION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;toYYYYMM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event_date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;29&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;ORDER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;page_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;30&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;31&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt;/* 5. 物化视图：每日 PV / UV 聚合 */&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;32&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MATERIALIZED&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;VIEW&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NOT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;EXISTS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;testdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pv_uv_daily&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;33&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;ENGINE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SummingMergeTree&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;34&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;PARTITION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;toYYYYMM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event_date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;35&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;ORDER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;event_date&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;36&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;37&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;38&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;event_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;39&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;40&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;uniqExact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;uv&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;41&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;testdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pageviews&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;42&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;GROUP&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;event_date&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;43&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;44&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt;/* 6. 演示数据插入 ---------------------------------------- */&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;45&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;46&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt;/* 用户维度 */&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;47&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;INTO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;testdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;signup_date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;VALUES&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;48&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Alice&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;2024-06-01&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;49&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Bob&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;2024-07-15&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;50&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Cathy&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;2024-11-30&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;51&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;52&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt;/* 页面维度 */&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;53&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;INTO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;testdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pages&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;page_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;VALUES&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;54&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/home&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;landing&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;55&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/pricing&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;info&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;56&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/blog&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;content&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;57&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;58&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt;/* 页面访问日志 */&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;59&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;INTO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;testdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pageviews&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;event_time&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;page_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;VALUES&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;60&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;2025-07-13&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;2025-07-13 09:17:00&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;35&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;61&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;2025-07-13&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;2025-07-13 09:18:07&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;62&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;2025-07-13&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;2025-07-13 09:19:02&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;63&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;2025-07-14&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;2025-07-14 10:03:45&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;64&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;2025-07-14&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;2025-07-14 10:05:22&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;65&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;66&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt;/* 7. 快速验证 --------------------------------------------- */&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;67&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;68&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt;/* 查看当前数据库已创建的表 */&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;69&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SHOW&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TABLES&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;testdb&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;70&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;71&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt;/* 查询物化视图结果 */&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;72&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;testdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pv_uv_daily&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ORDER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;event_date&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;73&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;74&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt;/* 联表查询示例 */&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;75&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;76&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;77&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;page_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;78&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;event_time&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;79&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;80&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;testdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pageviews&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;81&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;LEFT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;JOIN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;testdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;82&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;LEFT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;JOIN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;testdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pages&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;page_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;page_id&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;83&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;ORDER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;event_time&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>想要提高命令行编写效率，快来试试这几个快捷键！</title><link>https://xiaobox.github.io/p/2024-11-16-xiang-yao-ti-gao-ming-ling-xing-bian-xie-xiao-l-kuai-lai-shi/</link><pubDate>Sat, 16 Nov 2024 11:18:48 +0000</pubDate><guid>https://xiaobox.github.io/p/2024-11-16-xiang-yao-ti-gao-ming-ling-xing-bian-xie-xiao-l-kuai-lai-shi/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-16-xiang-yao-ti-gao-ming-ling-xing-bian-xie-xiao-l-kuai-lai-shi/cover.jpg" alt="Featured image of post 想要提高命令行编写效率，快来试试这几个快捷键！" /&gt;&lt;p&gt;最近看到一些同学在命令行写命令时的一些低效行为，比如长按左右箭头定位光标、长按 delete 删除字符等。其实只需要掌握几个常用的快捷键就能够大幅提高你写命令的效率。今天我们介绍几个常用的快捷键。&lt;/p&gt;
&lt;h2 id="移动和删除"&gt;&lt;a href="#%e7%a7%bb%e5%8a%a8%e5%92%8c%e5%88%a0%e9%99%a4" class="header-anchor"&gt;&lt;/a&gt;移动和删除
&lt;/h2&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-16-xiang-yao-ti-gao-ming-ling-xing-bian-xie-xiao-l-kuai-lai-shi/001-3acc93cf.png"&gt;&lt;/p&gt;
&lt;h3 id="移动"&gt;&lt;a href="#%e7%a7%bb%e5%8a%a8" class="header-anchor"&gt;&lt;/a&gt;移动
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ctrl + a&lt;/code&gt; : 可以直接将移动光标到命令行首&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ctrl + e&lt;/code&gt; : 可以直接移动光标到命令行尾&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-16-xiang-yao-ti-gao-ming-ling-xing-bian-xie-xiao-l-kuai-lai-shi/002-ead710e1.gif"&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ctrl + b&lt;/code&gt; : 光标向后移动一个字符&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ctrl + f&lt;/code&gt; : 光标向前移动一个字符&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-16-xiang-yao-ti-gao-ming-ling-xing-bian-xie-xiao-l-kuai-lai-shi/003-20ef4005.gif"&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;alt + b&lt;/code&gt; : 光标向后移动一个单词&lt;/li&gt;
&lt;li&gt;&lt;code&gt;alt + f&lt;/code&gt; : 光标向前移动一个单词&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-16-xiang-yao-ti-gao-ming-ling-xing-bian-xie-xiao-l-kuai-lai-shi/004-5c92019b.gif"&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="删除"&gt;&lt;a href="#%e5%88%a0%e9%99%a4" class="header-anchor"&gt;&lt;/a&gt;删除
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ctrl + u&lt;/code&gt; : 清除光标前的内容&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ctrl + k&lt;/code&gt; : 清除光标后的内容&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="历史命令"&gt;&lt;a href="#%e5%8e%86%e5%8f%b2%e5%91%bd%e4%bb%a4" class="header-anchor"&gt;&lt;/a&gt;历史命令
&lt;/h2&gt;&lt;p&gt;只推荐一个：&lt;code&gt;ctrl + r&lt;/code&gt; : 不但可以查找历史命令，还可以按字符串寻找历史命令&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-16-xiang-yao-ti-gao-ming-ling-xing-bian-xie-xiao-l-kuai-lai-shi/005-efaaa431.gif"&gt;&lt;/p&gt;
&lt;p&gt;此外，历史命令查询还有一个更牛的命令行工具 &lt;strong&gt;fzf&lt;/strong&gt; (&lt;a class="link" href="https://github.com/junegunn/fzf" target="_blank" rel="noopener"
 &gt;https://github.com/junegunn/fzf&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-16-xiang-yao-ti-gao-ming-ling-xing-bian-xie-xiao-l-kuai-lai-shi/006-02af21ad.png"&gt;&lt;/p&gt;
&lt;p&gt;它是一个交互式过滤程序，可过滤文件、命令历史记录、进程、主机名、书签、git 提交等类型&lt;/p&gt;
&lt;p&gt;上文中关于 &lt;code&gt;ctrl + r&lt;/code&gt; 的 演示如果你在你的机器上尝试效果不一样，其实就是因为安装了 &lt;code&gt;fzf&lt;/code&gt; ，是 &lt;code&gt;fzf&lt;/code&gt; 带给我了更友好的交互体验。&lt;/p&gt;
&lt;p&gt;许多高手都在用，有许多集成和增强的历史命令查询功能，非常牛！强烈推荐！&lt;/p&gt;</description></item><item><title>RAG 实践- Ollama+RagFlow 部署本地知识库</title><link>https://xiaobox.github.io/p/2024-06-18-rag-shi-jian-ollama-ragflow-bu-shu-ben-di-zhi-shi-ku/</link><pubDate>Tue, 18 Jun 2024 15:15:04 +0000</pubDate><guid>https://xiaobox.github.io/p/2024-06-18-rag-shi-jian-ollama-ragflow-bu-shu-ben-di-zhi-shi-ku/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-06-18-rag-shi-jian-ollama-ragflow-bu-shu-ben-di-zhi-shi-ku/cover.jpg" alt="Featured image of post RAG 实践- Ollama+RagFlow 部署本地知识库" /&gt;&lt;h2 id="前言"&gt;&lt;a href="#%e5%89%8d%e8%a8%80" class="header-anchor"&gt;&lt;/a&gt;前言
&lt;/h2&gt;&lt;p&gt;上一篇&lt;a class="link" href="http://mp.weixin.qq.com/s?__biz=MzI3Njk5ODg4OQ==&amp;amp;mid=2247489151&amp;amp;idx=1&amp;amp;sn=2f2dc98798b88399322f5be8dd253057&amp;amp;chksm=eb6da9f9dc1a20efe6dc6ae4bf52eeb8c8521c4164f50941881fddf49093a5425810abbcf325&amp;amp;scene=21#wechat_redirect" target="_blank" rel="noopener"
 &gt;文章&lt;/a&gt;我们介绍了如何利用 Ollama+AnythingLLM 来实践 RAG ，在本地部署一个知识库。借助大模型和 RAG 技术让我可以与本地私有的知识库文件实现自然语言的交互。&lt;/p&gt;
&lt;p&gt;本文我们介绍另一种实现方式：利用 Ollama+RagFlow 来实现，其中 Ollama 中使用的模型仍然是&lt;code&gt;Qwen2&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;我们再来回顾一下 RAG 常见的应用架构&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-06-18-rag-shi-jian-ollama-ragflow-bu-shu-ben-di-zhi-shi-ku/001-d440b88c.png"&gt;&lt;/p&gt;
&lt;h2 id="ragflow的安装和部署"&gt;&lt;a href="#ragflow%e7%9a%84%e5%ae%89%e8%a3%85%e5%92%8c%e9%83%a8%e7%bd%b2" class="header-anchor"&gt;&lt;/a&gt;RagFlow的安装和部署
&lt;/h2&gt;&lt;h3 id="前置条件"&gt;&lt;a href="#%e5%89%8d%e7%bd%ae%e6%9d%a1%e4%bb%b6" class="header-anchor"&gt;&lt;/a&gt;前置条件
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;CPU &amp;gt;= 4 核&lt;/li&gt;
&lt;li&gt;RAM &amp;gt;= 16 GB&lt;/li&gt;
&lt;li&gt;Disk &amp;gt;= 50 GB&lt;/li&gt;
&lt;li&gt;Docker &amp;gt;= 24.0.0 &amp;amp; Docker Compose &amp;gt;= v2.26.1&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="安装"&gt;&lt;a href="#%e5%ae%89%e8%a3%85" class="header-anchor"&gt;&lt;/a&gt;安装
&lt;/h3&gt;&lt;p&gt;克隆仓库&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$ git clone https://github.com/infiniflow/ragflow.git &lt;/code&gt;&lt;/p&gt;
&lt;p&gt;进入 docker 文件夹，利用提前编译好的 Docker 镜像启动服务器：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$ cd ragflow/docker $ chmod +x ./entrypoint.sh $ docker compose -f docker-compose-CN.yml up -d &lt;/code&gt;&lt;/p&gt;
&lt;p&gt;这一步注意docker 下载的镜像比较大，要留有足够的存储空间，我这边观察下载了约 10 个 G 左右。&lt;/p&gt;
&lt;p&gt;服务器启动成功后再次确认服务器状态:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$ docker logs -f ragflow-server &lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-06-18-rag-shi-jian-ollama-ragflow-bu-shu-ben-di-zhi-shi-ku/002-d8a49a8d.png"&gt;&lt;/p&gt;
&lt;p&gt;这里注意，安装完成后并不是要进入 下面两个地址&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;http://127.0.0.1:9380&lt;/li&gt;
&lt;li&gt;http://172.18.0.6:9380&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;而是要进入：http://localhost:80 先注册账号，是下面这个页面&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-06-18-rag-shi-jian-ollama-ragflow-bu-shu-ben-di-zhi-shi-ku/003-a0f0abfc.png"&gt;&lt;/p&gt;
&lt;h3 id="注册登录"&gt;&lt;a href="#%e6%b3%a8%e5%86%8c%e7%99%bb%e5%bd%95" class="header-anchor"&gt;&lt;/a&gt;注册登录
&lt;/h3&gt;&lt;p&gt;在上图的界面中注册，然后登录就来到下面这个页面了&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-06-18-rag-shi-jian-ollama-ragflow-bu-shu-ben-di-zhi-shi-ku/004-972b0db7.png"&gt;&lt;/p&gt;
&lt;h2 id="配置-ollama-连接大模型"&gt;&lt;a href="#%e9%85%8d%e7%bd%ae-ollama-%e8%bf%9e%e6%8e%a5%e5%a4%a7%e6%a8%a1%e5%9e%8b" class="header-anchor"&gt;&lt;/a&gt;配置 Ollama 连接大模型
&lt;/h2&gt;&lt;p&gt;如下图我们先配置模型，点击右上角头像，再点击模型提供商&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-06-18-rag-shi-jian-ollama-ragflow-bu-shu-ben-di-zhi-shi-ku/005-a94eb5c2.png"&gt;&lt;/p&gt;
&lt;p&gt;这里我是想连接我本地已经安装部署好的 Ollama ，通过 Ollama 我安装了 Qwen2 大模型，具体的安装步骤在之前的那篇文章里，有需要的可以移步到那里看。&lt;/p&gt;
&lt;p&gt;打开Ollama 后， 我是通过服务器模式启动的大模型&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ollama serve &lt;/code&gt;&lt;/p&gt;
&lt;p&gt;当然你也可以选择其他平台和其他模型，需要提供 API key，API key 的获取就去你所选模型的网站，现在有很多模型的 API 是有免费额度的。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-06-18-rag-shi-jian-ollama-ragflow-bu-shu-ben-di-zhi-shi-ku/006-f6bddca3.png"&gt;&lt;/p&gt;
&lt;p&gt;接着我们在 RagFlow 中配置模型，注意由于 RagFlow 我是在 docker 中安装的，所以请求本地部署的 Ollama 地址要用 ：http://host.docker.internal:11434&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-06-18-rag-shi-jian-ollama-ragflow-bu-shu-ben-di-zhi-shi-ku/007-037c9c84.png"&gt;&lt;/p&gt;
&lt;h2 id="创建知识库"&gt;&lt;a href="#%e5%88%9b%e5%bb%ba%e7%9f%a5%e8%af%86%e5%ba%93" class="header-anchor"&gt;&lt;/a&gt;创建知识库
&lt;/h2&gt;&lt;p&gt;接下来我们就可以创建知识库了&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-06-18-rag-shi-jian-ollama-ragflow-bu-shu-ben-di-zhi-shi-ku/008-9cd5baa7.png"&gt;&lt;/p&gt;
&lt;p&gt;注意这里的文件类型没有 &lt;code&gt;markdown&lt;/code&gt;,但我实测 &lt;code&gt;markdown&lt;/code&gt; 是可以的。其他的选项，根据你的情况自行设置就好，很简单。&lt;/p&gt;
&lt;p&gt;接下来就是上传你的文件了，也比较简单，但我发现上传后文件处理的比较慢，应该是我电脑配置的原因&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-06-18-rag-shi-jian-ollama-ragflow-bu-shu-ben-di-zhi-shi-ku/009-3df4c69c.png"&gt;&lt;/p&gt;
&lt;p&gt;文件上传并处理完成后，可以通过检索测试看一下文件有没有被正确检索。&lt;/p&gt;
&lt;p&gt;至此，如果你上传完成全部的文件，知识库就算创建完毕了。&lt;/p&gt;
&lt;h2 id="聊天"&gt;&lt;a href="#%e8%81%8a%e5%a4%a9" class="header-anchor"&gt;&lt;/a&gt;聊天
&lt;/h2&gt;&lt;p&gt;接着就到了展示成果的时候了，我们可以根据自己的知识库与模型进行自然语言交互了。&lt;/p&gt;
&lt;p&gt;首先注意，在聊天配置中要把 token 设置的大一些，不然回复的内容会很少！我这里把它拉到最大值了。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-06-18-rag-shi-jian-ollama-ragflow-bu-shu-ben-di-zhi-shi-ku/010-a4073cf5.png"&gt;&lt;/p&gt;
&lt;p&gt;展示一下成果：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-06-18-rag-shi-jian-ollama-ragflow-bu-shu-ben-di-zhi-shi-ku/011-7652058a.png"&gt;&lt;/p&gt;
&lt;p&gt;我觉得还算满意。但是由于我笔记本配置一般，也没有显卡支持，所以跑的很慢，真的很慢。但如果部署在有 GPU 的服务器上，企业私有化部署供内部使用，应该会比较快的。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-06-18-rag-shi-jian-ollama-ragflow-bu-shu-ben-di-zhi-shi-ku/012-55c77845.png"&gt;&lt;/p&gt;
&lt;h2 id="思考"&gt;&lt;a href="#%e6%80%9d%e8%80%83" class="header-anchor"&gt;&lt;/a&gt;思考
&lt;/h2&gt;&lt;p&gt;我这里的例子是用个人笔记本电脑上的资料做的个人知识库，对于文档的提问，无论是围绕着摘要总结来做，还是围绕着全文检索，答案看起来还行，也基本能用。但是这是面向个人的或者说面向 C 端 ，如果面向 B 端面向企业单靠向量检索就力不从心了，一来无法对精确信息召回，二来无法与企业内部信息系统集成（大量结构化数据）。所以必须在检索阶段引入多路召回和重排序，保证数据查询的准确度。&lt;/p&gt;
&lt;p&gt;企业内部的数据包含各种格式，更复杂的还包含各类图表等，如果在没有理解这些语义的基础之上直接提供 RAG 方案，例如简单的根据文字空白就来切分段落，就会导致语义丢失从而让最终查询的结果也是混乱不堪。&lt;/p&gt;
&lt;p&gt;如果解决这个问题呢，除了之前说的多路召回（多跳）和重排序这种方案，目前业界还有其他思路，比如 infiniFlow提出的 Infinity AI原生数据库（https://github.com/infiniflow/infinity）&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-06-18-rag-shi-jian-ollama-ragflow-bu-shu-ben-di-zhi-shi-ku/013-50bc4af9.png"&gt;&lt;/p&gt;
&lt;p&gt;从上图可以看到，AI原生数据库 不仅涵盖非结构化的内容如文档和图片，也包括结构化的信息系统。对这些信息进行有效整合，并在此基础上实现多路召回机制和最终的融合排序解决方案。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-06-18-rag-shi-jian-ollama-ragflow-bu-shu-ben-di-zhi-shi-ku/014-02b441fd.png"&gt;&lt;/p&gt;
&lt;p&gt;此外，很多AI 产品的上下文现在是越来越长，可能有人会说现在上下文都这么长了，还用得着 RAG 吗？我认为，RAG在知识库问答场景依然是非常必要的。LLM 的长上下文能力，对于 RAG 来说应该是很大的促进。用 OpenAI 联创 Andrej Karpathy 的一张图做个类比，他把 LLM 比喻为一台计算机的 CPU， 把上下文类比为计算机的内存，那么以向量为代表的数据库，就可以看作是这台计算机的硬盘&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-06-18-rag-shi-jian-ollama-ragflow-bu-shu-ben-di-zhi-shi-ku/015-c940bbf7.png"&gt;&lt;/p&gt;
&lt;p&gt;显然你不可能买一台只有内存的电脑。内存可以很大，但也意味着很贵，并且短时间内替代不了硬盘的作用。&lt;/p&gt;
&lt;p&gt;最后是准确性问题，关于这个问题一般有两个方向的解决思路，一种是从 RAG 下手，比如做 Embedding 模型的微调。一种是从 LLM 下手，做 LLM 微调。虽然两种我都没真正做过，但从研读的资料上得知RAG系统在实时性和成本方面相较于LLM微调具有优势，因此更受青睐。这点跟我的直觉一致。&lt;/p&gt;
&lt;h2 id="参考"&gt;&lt;a href="#%e5%8f%82%e8%80%83" class="header-anchor"&gt;&lt;/a&gt;参考
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/infiniflow/ragflow/blob/main/README" target="_blank" rel="noopener"
 &gt;https://github.com/infiniflow/ragflow/blob/main/README&lt;/a&gt;_zh.md&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://infiniflow.org/blog/database-for-rag" target="_blank" rel="noopener"
 &gt;https://infiniflow.org/blog/database-for-rag&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>git flow</title><link>https://xiaobox.github.io/p/2024-03-22-git-flow/</link><pubDate>Fri, 22 Mar 2024 08:52:42 +0000</pubDate><guid>https://xiaobox.github.io/p/2024-03-22-git-flow/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-03-22-git-flow/cover.jpg" alt="Featured image of post git flow" /&gt;&lt;h2 id="一--什么是-git-flow"&gt;&lt;a href="#%e4%b8%80--%e4%bb%80%e4%b9%88%e6%98%af-git-flow" class="header-anchor"&gt;&lt;/a&gt;一 什么是 git flow
&lt;/h2&gt;&lt;p&gt;就像代码需要代码规范一样，代码管理同样需要一个清晰的流程和规范。Git 作为一个源码管理系统，不可避免涉及到多人协作。协作必须有一个规范的工作流程，让大家有效地合作，使得项目井井有条地发展下去。&amp;ldquo;工作流程&amp;quot;在英语里，叫做&amp;quot;workflow&amp;quot;或者&amp;quot;flow&amp;rdquo;，原意是水流，比喻项目像水流那样，顺畅、自然地向前流动，不会发生冲击、对撞、甚至漩涡。&lt;/p&gt;
&lt;p&gt;Gitflow 工作流定义了一个围绕项目发布的严格分支模型。 下图能说明整个流程，该模式来自 Nvie&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-03-22-git-flow/001-f3b9593e.png"&gt;&lt;/p&gt;
&lt;p&gt;解释上图&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;master 默认分支，只能用来包括产品代码（线上生产环境代码），不许提交，只能合并。&lt;/li&gt;
&lt;li&gt;develop 最新的开发状态 ，是你进行任何新的开发的基础分支。当你开始一个新的功能分支时，它将是_开发_的基础。另外，该分支也汇集所有已经完成的功能，并等待被整合到 master 分支中。develop 上的代码总是从 feature 上合并过来的。不许提交，只能合并。&lt;/li&gt;
&lt;li&gt;master develop 这两个分支被称作为 长期分支。它们会存活在项目的整个生命周期中，而其他的分支，例如针对功能的分支，针对发行的分支，仅仅只是临时存在的&lt;/li&gt;
&lt;li&gt;feature 开发新功能的分支，基于 develop, 完成后 merge 回 develop&lt;/li&gt;
&lt;li&gt;release: 准备要发布版本的分支，用来修复 bug. 基于 develop, 完成后 merge 回 develop 和 master&lt;/li&gt;
&lt;li&gt;hotfix: 修复 master 上的问题，等不及 release 版本就必须马上上线。基于 master, 完成后 merge 回 master 和 develop&lt;/li&gt;
&lt;li&gt;hotfix 分支是基于 “master” 分支。 这也是和 release 分支最明显的区别，release 分支都是基于 “develop” 分支的&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="二-为什么要使用-git-flow"&gt;&lt;a href="#%e4%ba%8c-%e4%b8%ba%e4%bb%80%e4%b9%88%e8%a6%81%e4%bd%bf%e7%94%a8-git-flow" class="header-anchor"&gt;&lt;/a&gt;二 为什么要使用 git flow
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;虽然大多数团队已由 svn 切换到 git，但代码控制方式仍然是“集中式”的，没有发挥 git 的强大功能。&lt;/li&gt;
&lt;li&gt;在团队开发中使用版本控制系统时，商定一个统一的工作流程是至关重要的 ，git flow 有着清晰的流程和规范。&lt;/li&gt;
&lt;li&gt;简单并可定义适合各自团队的 flow&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="三-如何使用"&gt;&lt;a href="#%e4%b8%89-%e5%a6%82%e4%bd%95%e4%bd%bf%e7%94%a8" class="header-anchor"&gt;&lt;/a&gt;三 如何使用
&lt;/h2&gt;&lt;p&gt;初始分支，所有在 Master 分支上的 Commit 应该 Tag，目前只有 master 角色可提交，其它角色提交不了，被保护了&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-03-22-git-flow/002-b3cbcfda.png"&gt;&lt;/p&gt;
&lt;p&gt;Feature 分支 Feature 分支做完后，必须合并回 Develop 分支，合并完分支后一般会删点这个 Feature 分支，但是我们也可以保留&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-03-22-git-flow/003-0bb54033.png"&gt;&lt;/p&gt;
&lt;p&gt;Release 分支 Release 分支基于 Develop 分支创建，打完 Release 分之后，我们可以在这个 Release 分支上测试，修改 Bug 等。同时，其它开发人员可以基于开发新的 Feature （记住：一旦打了 Release 分支之后不要从 Develop 分支上合并新的改动到 Release 分支） 发布 Release 分支时，合并 Release 到 Master 和 Develop， 同时在 Master 分支上打个 Tag 记住 Release 版本号，然后可以删除 Release 分支了。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-03-22-git-flow/004-1486da11.png"&gt;&lt;/p&gt;
&lt;p&gt;维护分支 Hotfix hotfix 分支基于 Master 分支创建，开发完后需要合并回 Master 和 Develop 分支，同时在 Master 上打一个 tag&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-03-22-git-flow/005-c9370ce8.png"&gt;&lt;/p&gt;
&lt;h2 id="四-命令规范"&gt;&lt;a href="#%e5%9b%9b-%e5%91%bd%e4%bb%a4%e8%a7%84%e8%8c%83" class="header-anchor"&gt;&lt;/a&gt;四 命令规范
&lt;/h2&gt;&lt;p&gt;|
|
|
|&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;&lt;/th&gt;
 &lt;th&gt;&lt;/th&gt;
 &lt;th&gt;&lt;/th&gt;
 &lt;th&gt;&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;示例&lt;/td&gt;
 &lt;td&gt;说明&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;master&lt;/td&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;master&lt;/td&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;develop&lt;/td&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;develop&lt;/td&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;feature&lt;/td&gt;
 &lt;td&gt;feature_*&lt;/td&gt;
 &lt;td&gt;feature_order-detail&lt;/td&gt;
 &lt;td&gt;* 为功能简述，如多个单词，中间用横线连接&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;release&lt;/td&gt;
 &lt;td&gt;release_*.rc&lt;/td&gt;
 &lt;td&gt;release_1.0.1.rc&lt;/td&gt;
 &lt;td&gt;* 为版本号， 版本号规则如下所述&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;hotfix&lt;/td&gt;
 &lt;td&gt;hotfix_*&lt;/td&gt;
 &lt;td&gt;hotfix_1.0.1&lt;/td&gt;
 &lt;td&gt;* 为版本号， 版本号规则如下所述&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;tag&lt;/td&gt;
 &lt;td&gt;（对应项目名，全部大写拼写）_ tag _ V *&lt;/td&gt;
 &lt;td&gt;JARVISPMS_tag_V1.0.1&lt;/td&gt;
 &lt;td&gt;* 为版本号， 版本号规则如下所述&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;ol&gt;
&lt;li&gt;大版本号更新表示一次里程碑开发的完成，包含了若干个 feature 的实现。&lt;/li&gt;
&lt;li&gt;小版本号更新表示一个 feature 的完成。&lt;/li&gt;
&lt;li&gt;补丁号更新表示发布的 feature 不变，只是修改 bug&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;举例：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;某次发布是里程碑开发的结束，版本号为 1.0.0&lt;/li&gt;
&lt;li&gt;很快，上次发布的版本发现了 bug，紧急修复，再次发布，版本号为 1.0.1&lt;/li&gt;
&lt;li&gt;再次发现 bug，修复，重新发布，版本号为 1.0.2&lt;/li&gt;
&lt;li&gt;几个星期后，新增了几个功能，再次发布，版本号为 1.1.0&lt;/li&gt;
&lt;li&gt;几天后发现新增的功能有 bug，紧急修复，发布，版本号为 1.1.1&lt;/li&gt;
&lt;li&gt;再次新增功能发布，版本号为 1.2.0&lt;/li&gt;
&lt;li&gt;发现 bug，修复并发布，版本号为 1.2.1&lt;/li&gt;
&lt;li&gt;再次完成一次里程碑开发，发布，版本号为 2.0.0&lt;/li&gt;
&lt;li&gt;……以此类推&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="五-code-review"&gt;&lt;a href="#%e4%ba%94-code-review" class="header-anchor"&gt;&lt;/a&gt;五 code review
&lt;/h2&gt;&lt;p&gt;由于使用了 git flow ，可以将 code review 机制很好的结合起来，当我们合并代码的时候，是需要 MR（merge request）的，这时候需要向代码审核人提交你的合并请求。具体流程可参考 GitLab Flow 里的最后一部分。&lt;/p&gt;
&lt;h2 id="六-使用-git-flow-开发流程简述"&gt;&lt;a href="#%e5%85%ad-%e4%bd%bf%e7%94%a8-git-flow-%e5%bc%80%e5%8f%91%e6%b5%81%e7%a8%8b%e7%ae%80%e8%bf%b0" class="header-anchor"&gt;&lt;/a&gt;六 使用 git flow 开发流程简述
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;从 develop 打 feature 分支，确定 feature 分支负责人（今后这个分支分出去的其它分支都由他负责，他负责 MR 时的代码评审）。&lt;/li&gt;
&lt;li&gt;在 feature 分支上开发，如其它同事的其它 feature 分支合并回了 develop, 会要求通过大家。如需要（一般是公共影响部分）在你自己的 feature 分支上合并 develop 上的代码。&lt;/li&gt;
&lt;li&gt;开发完 feature 分支代码，提交合并请求，合并至 develop, 由代码评审人评审通过后合并。合并完成后，删除 feature 分支。&lt;/li&gt;
&lt;li&gt;提测前，从 develop 中打出 release 分支给测试使用，如测试通过不需要修改，则合并回 master 和 develop 并删除；如有问题，可以在 release 分支上修改，修改完毕后，再合并回 master,develop, 并删除 release 分支。&lt;/li&gt;
&lt;li&gt;上线前，在 maser 分支上打出 tag&lt;/li&gt;
&lt;li&gt;线上有 bug，从 master 打出 hotfix 分支修复，完成后，合并回 master 和 develop , 删除 hotfix 分支。&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>令人困惑的 git 术语</title><link>https://xiaobox.github.io/p/2023-11-30-ling-ren-kun-huo-de-git-shu-yu/</link><pubDate>Thu, 30 Nov 2023 09:14:40 +0000</pubDate><guid>https://xiaobox.github.io/p/2023-11-30-ling-ren-kun-huo-de-git-shu-yu/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2023-11-30-ling-ren-kun-huo-de-git-shu-yu/cover.jpg" alt="Featured image of post 令人困惑的 git 术语" /&gt;&lt;h2 id="head-and-heads"&gt;&lt;a href="#head-and-heads" class="header-anchor"&gt;&lt;/a&gt;HEAD and &amp;ldquo;heads&amp;rdquo;
&lt;/h2&gt;&lt;p&gt;一些人表示他们对术语 &lt;code&gt;HEAD&lt;/code&gt; 和 &lt;code&gt;refs/heads/main&lt;/code&gt; 感到困惑&lt;/p&gt;
&lt;p&gt;其实 “头”就是“枝”。在 git 内部，分支存储在名为 &lt;code&gt;.git/refs/heads&lt;/code&gt; 的目录中&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2023-11-30-ling-ren-kun-huo-de-git-shu-yu/001-602234b5.png"&gt;&lt;/p&gt;
&lt;p&gt;而 &lt;code&gt;HEAD&lt;/code&gt; 是当前分支。它存储在 &lt;code&gt;.git/HEAD&lt;/code&gt; 中。&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2023-11-30-ling-ren-kun-huo-de-git-shu-yu/002-fef337dc.png"&gt;&lt;/p&gt;
&lt;p&gt;&amp;ldquo;a head 是一个分支， HEAD 是当前分支&amp;rdquo; 这绝对是 git 中最奇怪的术语之一。&lt;/p&gt;
&lt;h2 id="your-branch-is-up-to-date-with-originmain"&gt;&lt;a href="#your-branch-is-up-to-date-with-originmain" class="header-anchor"&gt;&lt;/a&gt;Your branch is up to date with &amp;lsquo;origin/main&amp;rsquo;
&lt;/h2&gt;&lt;p&gt;你的分支已更新为 &amp;lsquo;origin/main&amp;rsquo; ?&lt;/p&gt;
&lt;p&gt;此消息看起来很简单 - 它表示你的 main 分支已与源保持同步！&lt;/p&gt;
&lt;p&gt;但这实际上有点误导。你可能认为这意味着你的 main 分支是最新的。事实并非如此。&lt;/p&gt;
&lt;p&gt;它实际上意味着 – 如果你上次运行 git fetch 或 git pull 是在 5 天前，那么 5 天前你的 main 分支是最新的（包含 5 天前的所有更改）&lt;/p&gt;
&lt;p&gt;说白了，只是上次同步过，但上一次是什么时候可不好说了，如果你没有意识到这一点，它可能会给您一种错误的安全感。&lt;/p&gt;
&lt;p&gt;我认为 git 理论上可以给你一条更有用的消息，比如“截至 5 天前最后一次获取，源的 main 是最新的”&lt;/p&gt;
&lt;h2 id="head-head-head-head-head2-head2"&gt;&lt;a href="#head-head-head-head-head2-head2" class="header-anchor"&gt;&lt;/a&gt;HEAD^, HEAD~ HEAD^^, HEAD~~, HEAD^2, HEAD~2
&lt;/h2&gt;&lt;p&gt;我知道 &lt;code&gt;HEAD^&lt;/code&gt; 指的是之前的提交，但我对 &lt;code&gt;HEAD~&lt;/code&gt; 和 &lt;code&gt;HEAD^&lt;/code&gt; 的区别困惑了很久。&lt;/p&gt;
&lt;p&gt;我查了一下，以下是它们之间的关系：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;HEAD^&lt;/code&gt; 和 &lt;code&gt;HEAD~&lt;/code&gt; 是一样的（1 次提交前）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HEAD^^^&lt;/code&gt; 和 &lt;code&gt;HEAD~~~&lt;/code&gt; 和 &lt;code&gt;HEAD~3&lt;/code&gt; 是一样的（3 次提交前）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HEAD^3&lt;/code&gt; 指提交的第三个父级，与 &lt;code&gt;HEAD~3&lt;/code&gt; 不同&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这看起来很奇怪——为什么 &lt;code&gt;HEAD~&lt;/code&gt; 和 &lt;code&gt;HEAD^&lt;/code&gt; 是一样的？&lt;/p&gt;
&lt;p&gt;“第三父级” 又是什么呢？&lt;/p&gt;
&lt;p&gt;&lt;code&gt;^&lt;/code&gt; 和 &lt;code&gt;~&lt;/code&gt; 在 Git 中用于导航提交树。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;^&lt;/code&gt; 用于访问合并提交的不同父提交&lt;/li&gt;
&lt;li&gt;&lt;code&gt;~&lt;/code&gt; 用于访问当前提交的祖先提交&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;大多数提交只有一个父提交。在 Git 中 &lt;code&gt;HEAD^&lt;/code&gt; 表示“HEAD 提交的父级”。&lt;/p&gt;
&lt;p&gt;但是如果 HEAD 是合并提交， &lt;code&gt;HEAD^&lt;/code&gt; 指的是合并的第一个父级， &lt;code&gt;HEAD^2&lt;/code&gt; 是第二个父级， &lt;code&gt;HEAD^3&lt;/code&gt; 是第三个父级，依此类推。&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;“&lt;/p&gt;
&lt;p&gt;通常，这种语法在合并提交（merge commit）时使用，其中有多个父提交。例如，一个合并提交可能有两个父提交，通过 HEAD^1 和 HEAD^2 可以访问这两个父提交。HEAD^3 就表示第三个父提交。&lt;/p&gt;
&lt;p&gt;”&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;HEAD~3&lt;/code&gt;：表示从 HEAD 开始往上数的第三个祖先提交。它指的是当前分支的前第三个提交，不考虑合并提交。这在查看历史记录时很有用。&lt;/p&gt;
&lt;h2 id="reset-revert-restore"&gt;&lt;a href="#reset-revert-restore" class="header-anchor"&gt;&lt;/a&gt;&amp;ldquo;reset&amp;rdquo;, &amp;ldquo;revert&amp;rdquo;, &amp;ldquo;restore&amp;rdquo;
&lt;/h2&gt;&lt;p&gt;很多人提到 &amp;ldquo;reset&amp;rdquo;、&amp;ldquo;revert&amp;rdquo; 和 &amp;ldquo;restore&amp;rdquo; 是非常相似的词，很难区分它们。&lt;/p&gt;
&lt;p&gt;手册也没有提供非常有用的信息：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;git reset&lt;/code&gt; : “重置当前 HEAD 到指定状态”&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git revert&lt;/code&gt; ：“恢复一些现有的提交”&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git restore&lt;/code&gt; ：“恢复工作树文件”&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;以下是它们各自的作用的一些简短描述：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;git revert COMMIT&lt;/code&gt; ：在当前分支上创建一个与 COMMIT “相反”的新提交（如果 COMMIT 添加了 3 行，则新提交将删除这 3 行）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;git reset --hard COMMIT&lt;/code&gt; ：强制当前分支返回到 COMMIT 时的状态，删除自 COMMIT 以来的所有新更改。非常危险的操作。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;git restore --source=COMMIT PATH&lt;/code&gt; ：将 PATH 中的所有文件恢复到 COMMIT 时的状态，而不更改任何其他文件或提交历史记录。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="main-和-originmain"&gt;&lt;a href="#main-%e5%92%8c-originmain" class="header-anchor"&gt;&lt;/a&gt;main 和 origin/main
&lt;/h2&gt;&lt;p&gt;在 Git 中，&lt;code&gt;main&lt;/code&gt; 和 &lt;code&gt;origin/main&lt;/code&gt; 分别表示不同的引用。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;main&lt;/code&gt;：这是本地分支的名称，通常表示你当前所在的本地分支，例如，如果你切换到主分支，那么 &lt;code&gt;main&lt;/code&gt; 就代表本地主分支。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;git checkout main &lt;/code&gt;&lt;/p&gt;
&lt;p&gt;这表示你正在引用本地仓库中的 &lt;code&gt;main&lt;/code&gt; 分支。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;origin/main&lt;/code&gt;：这是远程仓库的引用，通常表示远程主分支。&lt;code&gt;origin&lt;/code&gt; 是默认的远程仓库名称，而 &lt;code&gt;main&lt;/code&gt; 则表示在该远程仓库上的主分支。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;git fetch origin git checkout origin/main &lt;/code&gt;&lt;/p&gt;
&lt;p&gt;这表示你正在引用远程仓库 &lt;code&gt;origin&lt;/code&gt; 上的 &lt;code&gt;main&lt;/code&gt; 分支。请注意，你不能在 &lt;code&gt;origin/main&lt;/code&gt; 上直接进行修改，因为它是只读的，你需要在本地分支上进行工作。&lt;/p&gt;
&lt;p&gt;在典型的 Git 工作流中，你通常会从远程仓库克隆（clone）代码，本地会自动创建一个名为 &lt;code&gt;main&lt;/code&gt; 的主分支，并在远程仓库上创建一个名为 &lt;code&gt;origin/main&lt;/code&gt; 的引用。在进行协作开发时，你可能会通过 &lt;code&gt;git pull&lt;/code&gt; 或 &lt;code&gt;git fetch&lt;/code&gt; 来同步远程仓库的最新更改，这时就会涉及到本地的 main 分支和远程的 &lt;code&gt;origin/main&lt;/code&gt; 引用。&lt;/p&gt;
&lt;h2 id="checkout"&gt;&lt;a href="#checkout" class="header-anchor"&gt;&lt;/a&gt;checkout
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;git checkout BRANCH&lt;/code&gt; 切换分支&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git checkout file.txt&lt;/code&gt; 放弃对 file.txt 的更改，恢复为上一次提交（最新提交）的版本。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;checkout 后面跟分支名和文件名作用是不一样的，呵呵。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;git checkout file.txt&lt;/code&gt; 这条命令的效果相当于取消对 file.txt 的本地修改，将其还原为最新提交的状态。不过，git checkout 在 Git 2.23 版本之后的版本中被 git restore 和 git switch 命令替代，因此你也可以使用以下等效的命令：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# 使用 git restore git restore file.txt # 或者使用 git switch git switch --discard-changes file.txt &lt;/code&gt;&lt;/p&gt;</description></item><item><title>Make Jar, Not War.</title><link>https://xiaobox.github.io/p/2022-07-05-make-jar-not-war/</link><pubDate>Tue, 05 Jul 2022 09:51:29 +0000</pubDate><guid>https://xiaobox.github.io/p/2022-07-05-make-jar-not-war/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2022-07-05-make-jar-not-war/cover.jpg" alt="Featured image of post Make Jar, Not War." /&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2022-07-05-make-jar-not-war/001-dd69b2c2.jpg"&gt;&lt;/p&gt;
&lt;p&gt;大约在 4 年前，关于 java 应用最终打成 &lt;code&gt;jar&lt;/code&gt; 包还是 &lt;code&gt;war&lt;/code&gt; 包的选择令我比较疑惑。&lt;/p&gt;
&lt;p&gt;那时候更多的应用是打成 &lt;code&gt;war&lt;/code&gt; 包的，即使我们知道可以打成 &lt;code&gt;jar&lt;/code&gt; 包，但之前都是打成 &lt;code&gt;war&lt;/code&gt; 包，并且好像打成 &lt;code&gt;jar&lt;/code&gt; 包并没有什么特别明显的好处。&lt;/p&gt;
&lt;p&gt;但当时令我困惑的是越来越多的实践正在不怎么说明理由的情况下转而打 &lt;code&gt;jar&lt;/code&gt; 包，于是我开始思考&amp;hellip;&amp;hellip;&lt;/p&gt;
&lt;h2 id="war-包的理由"&gt;&lt;a href="#war-%e5%8c%85%e7%9a%84%e7%90%86%e7%94%b1" class="header-anchor"&gt;&lt;/a&gt;war 包的理由
&lt;/h2&gt;&lt;p&gt;在某大型 OTA 企业内部，应用仍然打成 &lt;code&gt;war&lt;/code&gt; 包, PaaS 平台会自动安装并配置好 tomcat，我知道这对于 &lt;strong&gt;web server 的统一配置和运维来说是有好处的&lt;/strong&gt;。基于 &lt;code&gt;war&lt;/code&gt; 背后的一系列 CI/CD 、DevOps 流程都一定有相应的适配，且就算 &lt;code&gt;jar&lt;/code&gt; 包有我不知道的某些优势也不可能一夜之间在大型企业内部使用，需要平台和系统做出调整。&lt;/p&gt;
&lt;p&gt;SpringBoot 在当时并未像现在这样流行，这并不意味着大家不用它，我的意思是相对新的项目来说，企业内部会有非常多 “老系统” 需要维护，我们不能指望一下子把这些老系统都用新的技术栈替换掉，就像你知道现在 &lt;code&gt;web application&lt;/code&gt; 一般是前后端分离开发，但如果接手一个使用 &lt;code&gt;jsp&lt;/code&gt; 的老家伙，你还得维护不是？&lt;/p&gt;
&lt;h2 id="jar-的时代"&gt;&lt;a href="#jar-%e7%9a%84%e6%97%b6%e4%bb%a3" class="header-anchor"&gt;&lt;/a&gt;jar 的时代
&lt;/h2&gt;&lt;p&gt;时代不同了，说得好像过了几十年的样子，但其实也就几年光阴而已。不过仅仅是这几年的光阴却足以改变一些事情的面貌。&lt;/p&gt;
&lt;p&gt;如今，云原生、微服务大行其道，大家好像已经非常适应这种开发模式，没有人纠结要不要用 SpringBoot，只会讨论使用的版本高还是低。更不用说打包的事情，很自然的会使用 &lt;code&gt;jar&lt;/code&gt;,虽然这种看起来的 “最佳实践”，在长期开发的过程中会形成 “肌肉记忆”，但我们还是要讨论一下为什么。&lt;/p&gt;
&lt;h3 id="方便"&gt;&lt;a href="#%e6%96%b9%e4%be%bf" class="header-anchor"&gt;&lt;/a&gt;方便
&lt;/h3&gt;&lt;p&gt;可运行 Jar 是打包自包含可运行应用程序的便捷方法。这样，我们可以最大限度地减少依赖关系。可以通过 Spring boot Maven 和 Gradle plugin 来管理依赖。&lt;/p&gt;
&lt;h3 id="云原生友好"&gt;&lt;a href="#%e4%ba%91%e5%8e%9f%e7%94%9f%e5%8f%8b%e5%a5%bd" class="header-anchor"&gt;&lt;/a&gt;云原生友好
&lt;/h3&gt;&lt;p&gt;在自备容器的情况下（docker,k8s), &lt;code&gt;jar&lt;/code&gt; 包可以直接作为一个 &lt;code&gt;single application&lt;/code&gt; 来管理。&lt;/p&gt;
&lt;p&gt;在过去我们使用 &lt;code&gt;war&lt;/code&gt; 是为了让多应用共享 web server，现在是容器的天下，在容器内，一般情况只跑一个应用进程。由于只有一个进程，我们就可以轻松地管理它，比如重启（不会影响其他的应用，因为没有其他应用）。&lt;/p&gt;
&lt;h3 id="版本控制"&gt;&lt;a href="#%e7%89%88%e6%9c%ac%e6%8e%a7%e5%88%b6" class="header-anchor"&gt;&lt;/a&gt;版本控制
&lt;/h3&gt;&lt;p&gt;利用 &lt;code&gt;git&lt;/code&gt; 等版本控制软件，可以控制程序运行所需要的一切（比如配置文件）&lt;/p&gt;
&lt;h3 id="易于扩展"&gt;&lt;a href="#%e6%98%93%e4%ba%8e%e6%89%a9%e5%b1%95" class="header-anchor"&gt;&lt;/a&gt;易于扩展
&lt;/h3&gt;&lt;p&gt;例如，将其复制到另一台服务器，然后“ just run it!” 无需安装和/或配置容器&lt;/p&gt;
&lt;h2 id="参考"&gt;&lt;a href="#%e5%8f%82%e8%80%83" class="header-anchor"&gt;&lt;/a&gt;参考
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://medium.com/@satyajit.nalavade/make-jar-not-war-josh-long-d6ce5fbb8a23" target="_blank" rel="noopener"
 &gt;https://medium.com/@satyajit.nalavade/make-jar-not-war-josh-long-d6ce5fbb8a23&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>扒一下一直不求甚解的 iptables</title><link>https://xiaobox.github.io/p/2021-09-01-ba-yi-xia-yi-zhi-bu-qiu-shen-jie-de-iptables/</link><pubDate>Wed, 01 Sep 2021 10:00:28 +0000</pubDate><guid>https://xiaobox.github.io/p/2021-09-01-ba-yi-xia-yi-zhi-bu-qiu-shen-jie-de-iptables/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2021-09-01-ba-yi-xia-yi-zhi-bu-qiu-shen-jie-de-iptables/cover.jpg" alt="Featured image of post 扒一下一直不求甚解的 iptables" /&gt;&lt;h2 id="基本概念"&gt;&lt;a href="#%e5%9f%ba%e6%9c%ac%e6%a6%82%e5%bf%b5" class="header-anchor"&gt;&lt;/a&gt;基本概念
&lt;/h2&gt;&lt;h3 id="iptables-是什么"&gt;&lt;a href="#iptables-%e6%98%af%e4%bb%80%e4%b9%88" class="header-anchor"&gt;&lt;/a&gt;iptables 是什么？
&lt;/h3&gt;&lt;p&gt;在 netfilter 的 官网 找到的如下解释：&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;“&lt;/p&gt;
&lt;p&gt;iptables is the userspace command line program used to configure the Linux 2.4.x and later packet filtering ruleset. It is targeted towards system administrators.&lt;/p&gt;
&lt;p&gt;Since Network Address Translation is also configured from the packet filter ruleset, iptables is used for this, too.&lt;/p&gt;
&lt;p&gt;The iptables package also includes ip6tables. ip6tables is used for configuring the IPv6 packet filter.&lt;/p&gt;
&lt;p&gt;”&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;iptables 是用于配置 Linux 2.4.x 及更高版本包过滤规则集的用户空间命令行程序。它针对系统管理员。&lt;/li&gt;
&lt;li&gt;由于网络地址转换 (NAT) 也是从包过滤规则集配置的，iptables 也用于此。&lt;/li&gt;
&lt;li&gt;iptables 包还包括 ip6tables。ip6tables 用于配置 IPv6 包过滤器。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;iptables 源码地址：https://git.netfilter.org/iptables&lt;/p&gt;
&lt;h3 id="netfilter-是什么"&gt;&lt;a href="#netfilter-%e6%98%af%e4%bb%80%e4%b9%88" class="header-anchor"&gt;&lt;/a&gt;netfilter 是什么？
&lt;/h3&gt;&lt;p&gt;来自维基百科的解释：&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;“&lt;/p&gt;
&lt;p&gt;netfilter，在 Linux 内核中的一个软件框架，用于管理网络数据包。不仅具有网络地址转换（NAT）的功能，也具备数据包内容修改、以及数据包过滤等防火墙功能。利用运作于用户空间的应用软件，如 iptables、nftables、ebtables 和 arptables 等，来控制 netfilter，系统管理者可以管理通过 Linux 操作系统的各种网络数据包。1990 年代，netfilter 在 Linux 2.3.15 版时进入 Linux 内核，正式应用于 Linux 2.4 版。&lt;/p&gt;
&lt;p&gt;”&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;netfilter 的主要功能包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;网络地址转换 (Network Address Translate)&lt;/li&gt;
&lt;li&gt;数据包内容修改&lt;/li&gt;
&lt;li&gt;以及数据包过滤的防火墙功能&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;linux 的绝大多数功能都是以模块的形式扩充出来的，netfilter 也是以模块的形式存在于 linux 中，当 linux 多了一个 netfilter 模块，linux 防火墙功能也就多了一项。&lt;/p&gt;
&lt;p&gt;netfilter 本身并不对数据包进行过滤，它只是允许过滤的数据包的函数挂接到内核中合适的位置。netfilter 项目在内核中还提供了一些基础设施，比如链接跟踪和日志记录，任何 iptables 策略都可以使用这些设施来执行特定数据包的处理。&lt;/p&gt;
&lt;p&gt;netfilter 模块存放的目录：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;/lib/modules/&amp;lt;uname -r&amp;gt;/kernel/net/ipv4/netfilter/&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;/lib/modules/&amp;lt;uname -r&amp;gt;/kernel/net/ipv6/netfilter/&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;不仅是 netfilter 有模块，iptables 也有模块，这些模块就位于&lt;code&gt;/lib64/xtables/&lt;/code&gt;(32bit 系统在&lt;code&gt;/lib/xtables/&lt;/code&gt;) 目录下，其中以 libxt 开头的是 iptables 模块，这些模块与 netfilter 模块是一一相对应的。例如&lt;code&gt;/lib/modules/&amp;lt;uname -r&amp;gt;/kernel/net/netfilter/xt_conntrack.ko&lt;/code&gt;模块，在&lt;code&gt;/lib64/xtables/libxt_conntrack.so&lt;/code&gt;与之相对应。当下达与 xt_conntrack.ko 相关的指令时，iptables 会根据 libxt_conntrack.so 模块的指示去检查语法是否正确。并将 netfilter 相应模块载入到系统内存，iptables 最后将规则写入到规则数据库中。&lt;/p&gt;
&lt;h3 id="netfilter-和-iptables-是什么关系"&gt;&lt;a href="#netfilter-%e5%92%8c-iptables-%e6%98%af%e4%bb%80%e4%b9%88%e5%85%b3%e7%b3%bb" class="header-anchor"&gt;&lt;/a&gt;netfilter 和 iptables 是什么关系？
&lt;/h3&gt;&lt;p&gt;在很多场景下，大家用 iptabes 配置防火墙规则，而实际上 iptables 其实不是真正的防火墙，我们可以把它理解成一个客户端代理，用户通过 iptables 这个代理，将用户的安全设定执行到对应的”安全框架”中，这个”安全框架”才是真正的防火墙，这个框架的名字叫 netfilter&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;netfilter 才是防火墙真正的安全框架（framework），netfilter 位于内核空间。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;iptables 其实是一个命令行工具，位于用户空间，我们用这个工具操作真正的框架。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="iptables-基础概念"&gt;&lt;a href="#iptables-%e5%9f%ba%e7%a1%80%e6%a6%82%e5%bf%b5" class="header-anchor"&gt;&lt;/a&gt;iptables 基础概念
&lt;/h2&gt;&lt;h3 id="链"&gt;&lt;a href="#%e9%93%be" class="header-anchor"&gt;&lt;/a&gt;链
&lt;/h3&gt;&lt;p&gt;iptables 在普遍的应用场景中被用作配置防火墙，如果我们想要防火墙能够达到”防火”的目的，则需要在内核中设置关卡，所有进出的报文都要通过这些关卡，经过检查后，符合放行条件的才能放行，符合阻拦条件的则需要被阻止，于是，就出现了 input 关卡和 output 关卡，但是，这个关卡上可能不止有一条规则，而是有很多条规则，当我们把这些规则串到一个链条上的时候，就形成了”链”。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2021-09-01-ba-yi-xia-yi-zhi-bu-qiu-shen-jie-de-iptables/001-ee32ad1e.jpg"&gt;&lt;/p&gt;
&lt;p&gt;总结下 5 链：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;PREROUTING 数据包刚进入网络层 , 路由之前&lt;/li&gt;
&lt;li&gt;INPUT 路由判断，流入用户空间&lt;/li&gt;
&lt;li&gt;OUTPUT 用户空间发出，后接路由判断出口的网络接口&lt;/li&gt;
&lt;li&gt;FORWARD 路由判断不进入用户空间，只进行转发&lt;/li&gt;
&lt;li&gt;POSTROUTING 数据包通过网络接口出去&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;根据实际情况的不同，报文经过”链”可能不同。如果报文需要转发，那么报文则不会经过 input 链发往用户空间，而是直接在内核空间中经过 forward 链和 postrouting 链转发出去的。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2021-09-01-ba-yi-xia-yi-zhi-bu-qiu-shen-jie-de-iptables/002-62d91c7f.jpg"&gt;&lt;/p&gt;
&lt;p&gt;所以，根据上图，我们能够想象出某些常用场景中，报文的流向：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;到本机某进程的报文：PREROUTING –&amp;gt; INPUT&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;由本机转发的报文：PREROUTING –&amp;gt; FORWARD –&amp;gt; POSTROUTING&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;由本机的某进程发出报文（通常为响应报文）：OUTPUT –&amp;gt; POSTROUTING&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;每个经过这个”关卡”的报文，都要将这条”链”上的所有规则匹配一遍，如果有符合条件的规则，则执行规则对应的动作。&lt;/p&gt;
&lt;h3 id="表"&gt;&lt;a href="#%e8%a1%a8" class="header-anchor"&gt;&lt;/a&gt;表
&lt;/h3&gt;&lt;p&gt;为什么称为 ip&amp;quot;tables&amp;quot; 呢？因为这个防火墙软件里面有多个表格 (table) ，每个表格都定义出自己的默认政策与规则， 且每个表格的用途都不相同。每个“表”指的是不同类型的数据包处理流程。&lt;/p&gt;
&lt;p&gt;预设的情况下，Linux 的 iptables 至少就有三个表。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2021-09-01-ba-yi-xia-yi-zhi-bu-qiu-shen-jie-de-iptables/003-7458bb09.jpg"&gt;&lt;/p&gt;
&lt;h3 id="表链关系"&gt;&lt;a href="#%e8%a1%a8%e9%93%be%e5%85%b3%e7%b3%bb" class="header-anchor"&gt;&lt;/a&gt;表链关系
&lt;/h3&gt;&lt;p&gt;每个”链”上都放置了一串规则，但是这些规则有些很相似，比如，A 类规则都是对 IP 或者端口的过滤，B 类规则是修改报文。我们是不是能把实现相同功能的规则放在一起呢？可以的，我们把具有相同功能的规则的组成一个集合，也就是上文说的“表”。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2021-09-01-ba-yi-xia-yi-zhi-bu-qiu-shen-jie-de-iptables/004-e65346af.jpg"&gt;&lt;/p&gt;
&lt;p&gt;iptables 为我们提供了如下规则的分类，或者说，iptables 为我们提供了如下”表”&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;filter 表：负责过滤功能，防火墙；内核模块：iptables_filter&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;nat 表：network address translation，网络地址转换功能；内核模块：iptable_nat&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;mangle 表：拆解报文，做出修改，并重新封装 的功能；iptable_mangle&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;raw 表：关闭 nat 表上启用的连接追踪机制；iptable_raw&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我们自定义的所有规则，都是这四种分类中的规则，或者说，所有规则都存在于这 4 张”表”中&lt;/p&gt;
&lt;p&gt;具体来说：&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;链&lt;/th&gt;
 &lt;th&gt;表&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;PREROUTING 的规则可以存在于&lt;/td&gt;
 &lt;td&gt;raw 表，mangle 表，nat 表。&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;INPUT 的规则可以存在于&lt;/td&gt;
 &lt;td&gt;mangle 表，filter 表，（centos7 中还有 nat 表，centos6 中没有）&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;FORWARD 的规则可以存在于&lt;/td&gt;
 &lt;td&gt;mangle 表，filter 表。&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;OUTPUT 的规则可以存在于&lt;/td&gt;
 &lt;td&gt;raw 表 mangle 表，nat 表，filter 表。&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;POSTROUTING 的规则可以存在于&lt;/td&gt;
 &lt;td&gt;mangle 表，nat 表。&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;我们在实际的使用过程中，往往是通过”表”作为操作入口，对规则进行定义的，之所以按照上述过程介绍 iptables，是因为从”关卡”的角度更容易从入门的角度理解，但是为了以便在实际使用的时候，更加顺畅的理解它们，此处我们还要将各”表”与”链”的关系罗列出来：&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;表（功能）&lt;/th&gt;
 &lt;th&gt;链（钩子）：&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;raw&lt;/td&gt;
 &lt;td&gt;表中的规则可以被哪些链使用：PREROUTING，OUTPUT&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;mangle&lt;/td&gt;
 &lt;td&gt;表中的规则可以被哪些链使用：PREROUTING，INPUT，FORWARD，OUTPUT，POSTROUTING&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;nat&lt;/td&gt;
 &lt;td&gt;表中的规则可以被哪些链使用：PREROUTING，OUTPUT，POSTROUTING（centos7 中还有 INPUT，centos6 中没有）&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;filter&lt;/td&gt;
 &lt;td&gt;表中的规则可以被哪些链使用：INPUT，FORWARD，OUTPUT&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="优先级"&gt;&lt;a href="#%e4%bc%98%e5%85%88%e7%ba%a7" class="header-anchor"&gt;&lt;/a&gt;优先级
&lt;/h3&gt;&lt;p&gt;数据包经过一个”链”的时候，会将当前链的所有规则都匹配一遍，但是匹配时总归要有顺序，我们应该一条一条的去匹配，而且相同功能类型的规则会汇聚在一张”表”中，哪些”表”中的规则会放在”链”的最前面执行呢？这时候就需要有一个优先级的问题&lt;/p&gt;
&lt;p&gt;优先级次序（由高而低）：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;raw –&amp;gt; mangle –&amp;gt; nat –&amp;gt; filter&lt;/code&gt;&lt;/p&gt;
&lt;h3 id="数据经过防火墙的流程"&gt;&lt;a href="#%e6%95%b0%e6%8d%ae%e7%bb%8f%e8%bf%87%e9%98%b2%e7%81%ab%e5%a2%99%e7%9a%84%e6%b5%81%e7%a8%8b" class="header-anchor"&gt;&lt;/a&gt;数据经过防火墙的流程
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2021-09-01-ba-yi-xia-yi-zhi-bu-qiu-shen-jie-de-iptables/005-e6ba1f6a.jpg"&gt;&lt;/p&gt;
&lt;h3 id="规则"&gt;&lt;a href="#%e8%a7%84%e5%88%99" class="header-anchor"&gt;&lt;/a&gt;规则
&lt;/h3&gt;&lt;p&gt;规则：根据指定的匹配条件来尝试匹配每个流经此处的报文，一旦匹配成功，则由规则后面指定的处理动作进行处理；&lt;/p&gt;
&lt;p&gt;规则由匹配条件和处理动作组成。&lt;/p&gt;
&lt;p&gt;匹配条件&lt;/p&gt;
&lt;p&gt;匹配条件分为基本匹配条件与扩展匹配条件&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;基本匹配条件：源地址 Source IP，目标地址 Destination IP 上述内容都可以作为基本匹配条件。&lt;/li&gt;
&lt;li&gt;扩展匹配条件：除了上述的条件可以用于匹配，还有很多其他的条件可以用于匹配，这些条件泛称为扩展条件，这些扩展条件其实也是 netfilter 中的一部分，只是以模块的形式存在，如果想要使用这些条件，则需要依赖对应的扩展模块。源端口 Source Port, 目标端口 Destination Port 可以作为扩展匹配条件&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;处理动作&lt;/p&gt;
&lt;p&gt;处理动作在 iptables 中被称为 target（这样说并不准确，我们暂且这样称呼），动作也可以分为基本动作和扩展动作。此处列出一些常用的动作，之后的文章会对它们进行详细的示例与总结：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ACCEPT：允许数据包通过。&lt;/li&gt;
&lt;li&gt;DROP：直接丢弃数据包，不给任何回应信息，这时候客户端会感觉自己的请求泥牛入海了，过了超时时间才会有反应。&lt;/li&gt;
&lt;li&gt;REJECT：拒绝数据包通过，必要时会给数据发送端一个响应的信息，客户端刚请求就会收到拒绝的信息。&lt;/li&gt;
&lt;li&gt;SNAT：源地址转换，解决内网用户用同一个公网地址上网的问题。&lt;/li&gt;
&lt;li&gt;MASQUERADE：是 SNAT 的一种特殊形式，适用于动态的、临时会变的 ip 上。&lt;/li&gt;
&lt;li&gt;DNAT：目标地址转换。&lt;/li&gt;
&lt;li&gt;REDIRECT：在本机做端口映射。&lt;/li&gt;
&lt;li&gt;LOG：在/var/log/messages 文件中记录日志信息，然后将数据包传递给下一条规则，也就是说除了记录以外不对数据包做任何其他操作，仍然让下一条规则去匹配。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;DROP 和 REJECT 的区别：DROP 是直接把匹配到的报文丢弃，REJECT 除了把报文丢弃还会给该报文中的源 IP 发一个 ICMP 报文说明目的不可达（直接回复不可达，更强硬）。前者报文发送方只能等超时，而后者发送方因为收到了 ICMP 不可达所以马上就给出了提示。&lt;/p&gt;
&lt;h2 id="参考"&gt;&lt;a href="#%e5%8f%82%e8%80%83" class="header-anchor"&gt;&lt;/a&gt;参考
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://www.zsythink.net/archives/1199" target="_blank" rel="noopener"
 &gt;https://www.zsythink.net/archives/1199&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://borosan.gitbook.io/lpic2-exam-guide/2121-configuring-a-router" target="_blank" rel="noopener"
 &gt;https://borosan.gitbook.io/lpic2-exam-guide/2121-configuring-a-router&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://cn.linux.vbird.org/linux" target="_blank" rel="noopener"
 &gt;http://cn.linux.vbird.org/linux&lt;/a&gt;_server/0250simple_firewall_3.php&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.xiebruce.top/1071.html" target="_blank" rel="noopener"
 &gt;https://www.xiebruce.top/1071.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://kuring.me/post/iptables/" target="_blank" rel="noopener"
 &gt;http://kuring.me/post/iptables/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>