<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Wener Live &amp; Life Blog</title>
        <link>https://wener.me/story</link>
        <description>Wener Live &amp; Life Blog</description>
        <lastBuildDate>Wed, 01 Oct 2025 00:00:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <item>
            <title><![CDATA[工具 - 工 与 具]]></title>
            <link>https://wener.me/story/function-and-interface</link>
            <guid>https://wener.me/story/function-and-interface</guid>
            <pubDate>Wed, 01 Oct 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[如果把工具看作是工与具。]]></description>
            <content:encoded><![CDATA[<p>如果把工具看作是工与具。</p>
<p>将它拆开来看，工具是 “工” 与 “具” 的结合。这是一个理解我们所创造的一切的强大模型。</p>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="工">工<a href="https://wener.me/story/function-and-interface#%E5%B7%A5" class="hash-link" aria-label="Direct link to 工" title="Direct link to 工" translate="no">​</a></h2>
<p>工 - 工作、劳动、技巧、能力、标准</p>
<p>它不是指那个物件本身，而是指使用物件所要达成的“事”或所蕴含的“巧”。</p>
<p>它强调 <strong>功能</strong>。</p>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="具">具<a href="https://wener.me/story/function-and-interface#%E5%85%B7" class="hash-link" aria-label="Direct link to 具" title="Direct link to 具" translate="no">​</a></h2>
<p>具 - 器物、器具</p>
<p>它强调的是一个实际存在的、有形的、可以被掌握和使用的物件。</p>
<p>它强调 <strong>具象化</strong>。</p>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="工具">工具<a href="https://wener.me/story/function-and-interface#%E5%B7%A5%E5%85%B7" class="hash-link" aria-label="Direct link to 工具" title="Direct link to 工具" translate="no">​</a></h2>
<p>没有“工”，只有“具”：那就是一堆没有意义的原材料。一块石头、一根木头，在没有被赋予“砸开坚果”或“支撑重物”这些“功能”之前，它们只是自然物，而不是工具。</p>
<p>没有“具”，只有“工”：那就是一个无法实现的想法或概念。我想“更快地移动”，这个“功能”需求是存在的，但直到有人发明了轮子、马车、自行车、汽车这些“具象化”的载体，这个功能才得以实现。</p>
<p>工具 = 工 + 具 = 用 + 体</p>
<ul>
<li>工 - 功能 - 灵魂</li>
<li>具 - 载体 - 肉体</li>
</ul>
<p>当我们说“创造一个工具”时，我们不仅仅是在制造一个物件，更是在为某个特定的功能寻找最佳的具象化方案。</p>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="研发与工具">研发与工具<a href="https://wener.me/story/function-and-interface#%E7%A0%94%E5%8F%91%E4%B8%8E%E5%B7%A5%E5%85%B7" class="hash-link" aria-label="Direct link to 研发与工具" title="Direct link to 研发与工具" translate="no">​</a></h2>
<p>后端是“工” - 应用程序的引擎、大脑和骨架。它负责</p>
<ul>
<li>核心逻辑</li>
<li>数据管理</li>
<li>安全认证</li>
<li>算法与服务</li>
</ul>
<p>但这一切对于终端用户来说，都是不可见、不可直接感知的。它拥有强大的潜在能力，就像一个装备了顶级发动机和精密传动系统的裸露底盘。它能跑，但你没法开。</p>
<hr>
<p>前端是“具” - 应用程序的外观、界面和交互。</p>
<ul>
<li>信息呈现</li>
<li>用户交互</li>
<li>用户体验</li>
</ul>
<p>前端将后端那些抽象的、基于数据的“功能”，转化为了用户可以看见、点击、触摸的具体形态。它为那个强大的引擎装上了方向盘、仪表盘、座椅和漂亮的外壳，让一个普通人也能驾驭它。</p>
<hr>
<p>API - 连接“工”与“具”的契约 - 血脉</p>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="用户价值实现">用户价值实现<a href="https://wener.me/story/function-and-interface#%E7%94%A8%E6%88%B7%E4%BB%B7%E5%80%BC%E5%AE%9E%E7%8E%B0" class="hash-link" aria-label="Direct link to 用户价值实现" title="Direct link to 用户价值实现" translate="no">​</a></h2>
<p>“Accessable”（可触达/可用性）在很多时候比功能本身更重要。</p>
<p>“Accessible”本身不是结果，但它是通往结果的唯一桥梁。一个无法被用户触达、理解和使用的功能，其结果等于零。</p>
<div class="language-text codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-text codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token plain">用户获得的最终结果 = 核心功能(工)的潜在价值 × 可用性(具)的实现程度</span><br></span></code></pre></div></div>
<p>“Accessable”</p>
<ul>
<li>它是价值的“最后一公里”</li>
<li>它定义了目标用户</li>
<li>在功能同质化的世界里，它本身就是核心竞争力</li>
</ul>
<p>只有被用户“Accessable”地体验到的那部分功能，才构成了真正的“有效结果”。</p>
<p>举例来说</p>
<ul>
<li>潜在结果： 后端实现了100个功能。这是理论上的、潜在的价值。</li>
<li>有效结果： 用户因为界面友好、引导清晰，最终只使用了其中最重要的10个功能，并解决了他们的问题。这10个功能带来的价值，才是“有效结果”。</li>
</ul>
<hr>
<ul>
<li>“工”决定了一个工具的价值上限，它能走多高、多远。</li>
<li>“具”则决定了这个价值上限中有多少能够被实际交付给用户，它决定了工具能走多宽、多广。</li>
</ul>
<p>在一个追求极致功能和性能的“工程师思维”里，“工”是核心。
但在一个以用户为中心的“产品思维”里，确保“工”的价值能通过“具”顺畅地传递出去，才是通往成功的关键。</p>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="工与具--function-and-interface">工与具 = Function and Interface<a href="https://wener.me/story/function-and-interface#%E5%B7%A5%E4%B8%8E%E5%85%B7--function-and-interface" class="hash-link" aria-label="Direct link to 工与具 = Function and Interface" title="Direct link to 工与具 = Function and Interface" translate="no">​</a></h2>
<ul>
<li>工 = Function
<ul>
<li>它关乎“为什么”和“做什么” (Why &amp; What it does)。</li>
<li>它是内在的逻辑、目的、能力、过程。</li>
<li>在软件中，它就是后端逻辑、算法、数据处理、业务规则。</li>
<li>它是工具的灵魂和价值核心，但本身是抽象的、不可见的。</li>
</ul>
</li>
<li>具 = Interface
<ul>
<li>它关乎“是什么”和“怎么用” (What it is &amp; How to use it)。</li>
<li>它是外在的形态、载体、交互点、呈现方式。</li>
<li>在软件中，它就是图形用户界面(GUI)、应用程序编程接口(API)、命令行(CLI)。</li>
<li>在硬件中，它就是产品的外观、按钮、屏幕。</li>
<li>它是功能的<strong>身体和表达形式</strong>，是连接用户和功能的<strong>唯一桥梁</strong>。</li>
</ul>
</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="function-and-interface">Function and Interface<a href="https://wener.me/story/function-and-interface#function-and-interface" class="hash-link" aria-label="Direct link to Function and Interface" title="Direct link to Function and Interface" translate="no">​</a></h2>
<ul>
<li>好界面 + 好功能 = 理想的产品。 价值最大化。</li>
<li>坏界面 + 好功能 = 璞玉，但难以触及。</li>
<li>好界面 + 坏功能 = 金玉其外，败絮其中。</li>
<li>坏界面 + 坏功能 = 电子垃圾。</li>
</ul>
<p>“用户界面”不是结果</p>
<ul>
<li>用户要的<strong>结果</strong>不是“点击一个支付按钮”，而是“成功买到一件商品”。</li>
<li>用户要的<strong>结果</strong>不是“看到一个漂亮的图表”，而是“理解了这个月开支的变化趋势”。</li>
</ul>
<p>但从用户的感知和体验来看，“用户界面”就是他们能体验到的那个“结果”的全部。</p>
<p>例如一个餐厅</p>
<ul>
<li>最终结果是： 一次满意而饱足的用餐体验。</li>
<li>核心功能 (工) 是： 厨师烹饪出的美味菜肴。</li>
<li>用户界面 (具) 是： 餐厅的装修、菜单的设计、服务员的态度、餐具的摆放、菜品的呈现方式（摆盘）。</li>
</ul>
<p>你不能“吃”菜单和服务员，你最终吃的是菜。但如果菜单模糊不清、服务员态度恶劣、环境嘈杂脏乱，那么即使菜本身再好吃，你获得的“结果”（用餐体验）也是糟糕的。</p>
<p>反过来说，如果整个“界面”体验极佳，它甚至能提升你对菜品（功能）的评价。</p>
<hr>
<p>用户界面不是结果本身，但它是对结果的封装 (Packaging) 和呈现 (Presentation)。
在用户的主观世界里，这个封装和呈现的好坏，直接定义了他们所获得的最终结果的质量。</p>
<p>一个功能（工）无论多好，都必须通过界面（具）这个唯一的通道才能抵达用户。在这个传递过程中，界面既是守门人，又是放大器。</p>
<ul>
<li>守门人： 决定了用户能不能、愿不愿进来体验核心功能。</li>
<li>放大器： 决定了核心功能的价值是被增强了，还是被削弱了。</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="结论">结论<a href="https://wener.me/story/function-and-interface#%E7%BB%93%E8%AE%BA" class="hash-link" aria-label="Direct link to 结论" title="Direct link to 结论" translate="no">​</a></h2>
<ul>
<li>创造一个好的工具，本质上就是为卓越的“功能”寻找最恰当的“界面”。</li>
<li>无论是代码、产品，还是一个团队、一套流程，这个“工与具”的思维模型都同样适用。它是创造与设计的核心。</li>
</ul>]]></content:encoded>
            <category>Thoughts</category>
        </item>
        <item>
            <title><![CDATA[《我把我的矜持都给了她》：第二章 - 故事的重新开始]]></title>
            <link>https://wener.me/story/here-my-love-ch2</link>
            <guid>https://wener.me/story/here-my-love-ch2</guid>
            <pubDate>Wed, 01 Oct 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[记忆的片段最是挠人。]]></description>
            <content:encoded><![CDATA[<p>记忆的片段最是挠人。</p>
<p>回忆里模糊的片段，没有前因的后果，没有起承的转合，甚至连当初跌宕的起伏，心潮的澎湃都已经平静。</p>
<p>只剩下片段，仍旧有一些脸庞。</p>
<p>剩下的，就像是故事的注脚，没有主角但似乎又是牵连的一系列故事。</p>
<p>一直以来的我主张是活在当下。</p>
<p>但回忆美妙的是，能从一个第三者的视角，去重新审视那些曾经发生的故事。</p>
<p>但早已忘记是怎么开始的，不过，索性是有个合适的结尾。</p>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="开始的故事">开始的故事<a href="https://wener.me/story/here-my-love-ch2#%E5%BC%80%E5%A7%8B%E7%9A%84%E6%95%85%E4%BA%8B" class="hash-link" aria-label="Direct link to 开始的故事" title="Direct link to 开始的故事" translate="no">​</a></h2>
<p>时间回到初中时代，新鲜和无聊的信息充斥着每天重复的生活。</p>
<p>而这个男孩有个简单的期盼：每天能看到她。</p>
<p>女孩很喜欢笑，这应该是男孩记忆最深刻的部分。她的笑容像是阳光，能照亮整个教室。</p>
<p>男孩很傻，只希望能尽量靠近她，即便是什么都没能说。</p>
<p>男孩很傻，只希望能和她坐在一起，即便是什么都不可能做。</p>
<p>男孩很傻，只希望能和她一起放学，即便是什么都不可能聊。</p>
<p>但唯一能做的，只能是每天看到她，则已经很满足了。</p>
<p>男孩总是喜欢自持矜持，也或许她并不会主动。</p>
<p>一天天这样过这，从未想过故事会有转折，只需要这样的美好生活一直延续，不，这对他来说就是美好的。</p>
<p>原来，他从来没在脑子里出现过喜欢两个字，因为他从来没想过要拥有她。</p>
<p>原来，他从来没意识到，或许他曾经喜欢过她。</p>
<p>原来，他真的很傻，但这却很美好。</p>
<p>他说他从来没有后悔，美好是需要回头看才能发现的。</p>
<p>原来，他真的很傻。</p>]]></content:encoded>
            <category>我把我的矜持都给了她</category>
            <category>故事</category>
        </item>
        <item>
            <title><![CDATA[解密 ClassFinal 加密的 Java Jar 包]]></title>
            <link>https://wener.me/story/decrypt-classfinal-jar</link>
            <guid>https://wener.me/story/decrypt-classfinal-jar</guid>
            <pubDate>Wed, 18 Sep 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[ClassFinal 是一款java class文件安全加密工具，支持直接加密jar包或war包，无需修改任何项目代码，兼容spring-framework；可避免源码泄漏或字节码被反编译。]]></description>
            <content:encoded><![CDATA[<p><a href="https://github.com/roseboy/classfinal" target="_blank" rel="noopener noreferrer">ClassFinal</a> 是一款java class文件安全加密工具，支持直接加密jar包或war包，无需修改任何项目代码，兼容spring-framework；可避免源码泄漏或字节码被反编译。</p>
<p><strong>要点</strong></p>
<ul>
<li>拿到 password
<ul>
<li>可能内置了
<ul>
<li>META-INF/.classes/org.springframework.config.Pass</li>
</ul>
</li>
<li>可能需要通过外部方式获取
<ul>
<li>命令行参数或者环境变量或者拦截 Class 加载</li>
</ul>
</li>
<li>总的来说比较容易获取</li>
</ul>
</li>
<li>将 jar 添加到 classpath - 方便直接调用 net.roseboy.classfinal 内内容
<ul>
<li>通过 IDE 或者通过命令行参数</li>
</ul>
</li>
<li>解压 jar 到当前 目录 tmp</li>
<li>解密 class</li>
<li>反编译得到 java</li>
<li>添加 lib 目录到 classpath</li>
<li>通过 IDEA 可直接调用原始 jar 里内容或直接启动 Application
<ul>
<li>可能需要修改反编译后的 java 文件 - 部分反编译语法错误</li>
</ul>
</li>
</ul>
<div class="language-java codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_Lwh4">DecryptClassFinal.java</div><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-java codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">package</span><span class="token plain"> </span><span class="token namespace" style="color:rgb(178, 204, 214)">main</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">import</span><span class="token plain"> </span><span class="token import namespace" style="color:rgb(178, 204, 214)">net</span><span class="token import namespace punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token import namespace" style="color:rgb(178, 204, 214)">roseboy</span><span class="token import namespace punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token import namespace" style="color:rgb(178, 204, 214)">classfinal</span><span class="token import namespace punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token import class-name" style="color:rgb(255, 203, 107)">JarDecryptor</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">import</span><span class="token plain"> </span><span class="token import namespace" style="color:rgb(178, 204, 214)">net</span><span class="token import namespace punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token import namespace" style="color:rgb(178, 204, 214)">roseboy</span><span class="token import namespace punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token import namespace" style="color:rgb(178, 204, 214)">classfinal</span><span class="token import namespace punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token import namespace" style="color:rgb(178, 204, 214)">util</span><span class="token import namespace punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token import class-name" style="color:rgb(255, 203, 107)">EncryptUtils</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">import</span><span class="token plain"> </span><span class="token import namespace" style="color:rgb(178, 204, 214)">net</span><span class="token import namespace punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token import namespace" style="color:rgb(178, 204, 214)">roseboy</span><span class="token import namespace punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token import namespace" style="color:rgb(178, 204, 214)">classfinal</span><span class="token import namespace punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token import namespace" style="color:rgb(178, 204, 214)">util</span><span class="token import namespace punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token import class-name" style="color:rgb(255, 203, 107)">StrUtils</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">import</span><span class="token plain"> </span><span class="token import namespace" style="color:rgb(178, 204, 214)">java</span><span class="token import namespace punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token import namespace" style="color:rgb(178, 204, 214)">io</span><span class="token import namespace punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token import class-name" style="color:rgb(255, 203, 107)">File</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">import</span><span class="token plain"> </span><span class="token import namespace" style="color:rgb(178, 204, 214)">java</span><span class="token import namespace punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token import namespace" style="color:rgb(178, 204, 214)">io</span><span class="token import namespace punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token import class-name" style="color:rgb(255, 203, 107)">IOException</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">import</span><span class="token plain"> </span><span class="token import namespace" style="color:rgb(178, 204, 214)">java</span><span class="token import namespace punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token import namespace" style="color:rgb(178, 204, 214)">nio</span><span class="token import namespace punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token import namespace" style="color:rgb(178, 204, 214)">file</span><span class="token import namespace punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token import class-name" style="color:rgb(255, 203, 107)">Files</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">import</span><span class="token plain"> </span><span class="token import namespace" style="color:rgb(178, 204, 214)">java</span><span class="token import namespace punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token import namespace" style="color:rgb(178, 204, 214)">nio</span><span class="token import namespace punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token import namespace" style="color:rgb(178, 204, 214)">file</span><span class="token import namespace punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token import class-name" style="color:rgb(255, 203, 107)">Path</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">class</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">DecryptClassFinal</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">main</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">String</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"> args</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">throws</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">IOException</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token class-name" style="color:rgb(255, 203, 107)">String</span><span class="token plain"> src </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token class-name" style="color:rgb(255, 203, 107)">System</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token function" style="color:rgb(130, 170, 255)">getProperty</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">"user.dir"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">+</span><span class="token plain">  </span><span class="token string" style="color:rgb(195, 232, 141)">"/tmp/META-INF/.classes"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token class-name" style="color:rgb(255, 203, 107)">String</span><span class="token plain"> dst </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">System</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token function" style="color:rgb(130, 170, 255)">getProperty</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">"user.dir"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">+</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"/src/main/class"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token class-name" style="color:rgb(255, 203, 107)">File</span><span class="token plain"> srcDir </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">new</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">File</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">src</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token class-name" style="color:rgb(255, 203, 107)">JarDecryptor</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token function" style="color:rgb(130, 170, 255)">getInstance</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// 默认 password 位置</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token class-name" style="color:rgb(255, 203, 107)">String</span><span class="token plain"> pass </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">Files</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token function" style="color:rgb(130, 170, 255)">readString</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">Path</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token function" style="color:rgb(130, 170, 255)">of</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">src</span><span class="token operator" style="color:rgb(137, 221, 255)">+</span><span class="token string" style="color:rgb(195, 232, 141)">"/org.springframework.config.Pass"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token keyword" style="font-style:italic">char</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"> password </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain">  </span><span class="token class-name" style="color:rgb(255, 203, 107)">EncryptUtils</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token function" style="color:rgb(130, 170, 255)">md5</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">pass</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token function" style="color:rgb(130, 170, 255)">toCharArray</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token class-name" style="color:rgb(255, 203, 107)">System</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">out</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token function" style="color:rgb(130, 170, 255)">printf</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">"src:%s\n"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> src</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token class-name" style="color:rgb(255, 203, 107)">System</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">out</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token function" style="color:rgb(130, 170, 255)">printf</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">"dst:%s\n"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> dst</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token class-name" style="color:rgb(255, 203, 107)">System</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">out</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token function" style="color:rgb(130, 170, 255)">printf</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">"password:%s\n"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> pass</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token keyword" style="font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">srcDir</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token function" style="color:rgb(130, 170, 255)">isDirectory</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">            </span><span class="token keyword" style="font-style:italic">for</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token class-name" style="color:rgb(255, 203, 107)">File</span><span class="token plain"> file </span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> srcDir</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token function" style="color:rgb(130, 170, 255)">listFiles</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">                </span><span class="token class-name" style="color:rgb(255, 203, 107)">String</span><span class="token plain"> fp </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> file</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token function" style="color:rgb(130, 170, 255)">getName</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">                </span><span class="token keyword" style="font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">fp</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token function" style="color:rgb(130, 170, 255)">startsWith</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">"org.springframework"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">                    </span><span class="token keyword" style="font-style:italic">continue</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">                </span><span class="token keyword" style="font-style:italic">byte</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"> fileBytes </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">Files</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token function" style="color:rgb(130, 170, 255)">readAllBytes</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">file</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token function" style="color:rgb(130, 170, 255)">toPath</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">                </span><span class="token keyword" style="font-style:italic">byte</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"> out </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">dec</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">password</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> fp</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> fileBytes</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">                </span><span class="token class-name" style="color:rgb(255, 203, 107)">String</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"> split </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> fp</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token function" style="color:rgb(130, 170, 255)">split</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">"[.]"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">                </span><span class="token class-name" style="color:rgb(255, 203, 107)">String</span><span class="token plain"> fn </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> split</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token plain">split</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">length</span><span class="token operator" style="color:rgb(137, 221, 255)">-</span><span class="token number" style="color:rgb(247, 140, 108)">1</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">                </span><span class="token class-name" style="color:rgb(255, 203, 107)">String</span><span class="token plain"> p </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> dst</span><span class="token operator" style="color:rgb(137, 221, 255)">+</span><span class="token string" style="color:rgb(195, 232, 141)">"/"</span><span class="token operator" style="color:rgb(137, 221, 255)">+</span><span class="token plain"> fp</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token function" style="color:rgb(130, 170, 255)">substring</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> fp</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token function" style="color:rgb(130, 170, 255)">lastIndexOf</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token char" style="color:rgb(130, 170, 255)">'.'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token function" style="color:rgb(130, 170, 255)">replaceAll</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">"[.]"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"/"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">                </span><span class="token keyword" style="font-style:italic">new</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">File</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">p</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token function" style="color:rgb(130, 170, 255)">mkdirs</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">                </span><span class="token class-name" style="color:rgb(255, 203, 107)">String</span><span class="token plain"> f </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> p</span><span class="token operator" style="color:rgb(137, 221, 255)">+</span><span class="token string" style="color:rgb(195, 232, 141)">"/"</span><span class="token operator" style="color:rgb(137, 221, 255)">+</span><span class="token plain"> fn </span><span class="token operator" style="color:rgb(137, 221, 255)">+</span><span class="token string" style="color:rgb(195, 232, 141)">".class"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">                </span><span class="token class-name" style="color:rgb(255, 203, 107)">System</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">out</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token function" style="color:rgb(130, 170, 255)">println</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">"Write to: "</span><span class="token operator" style="color:rgb(137, 221, 255)">+</span><span class="token plain">f</span><span class="token operator" style="color:rgb(137, 221, 255)">+</span><span class="token string" style="color:rgb(195, 232, 141)">" Len:"</span><span class="token operator" style="color:rgb(137, 221, 255)">+</span><span class="token plain">out</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">length</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">                </span><span class="token class-name" style="color:rgb(255, 203, 107)">Files</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token function" style="color:rgb(130, 170, 255)">write</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">new</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">File</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">f</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token function" style="color:rgb(130, 170, 255)">toPath</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> out</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">            </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">byte</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">dec</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">char</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"> password</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">String</span><span class="token plain"> fileName</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">byte</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"> bytes</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token keyword" style="font-style:italic">char</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token plain"> pass</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">        pass </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">StrUtils</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token function" style="color:rgb(130, 170, 255)">merger</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">new</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">char</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain">password</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> fileName</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token function" style="color:rgb(130, 170, 255)">toCharArray</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain">  </span><span class="token class-name" style="color:rgb(255, 203, 107)">EncryptUtils</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token function" style="color:rgb(130, 170, 255)">de</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">bytes</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> pass</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">1</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></span></code></pre></div></div>
<p>运行 main 后 src/main/class 目录下会生成解密后的 class 文件。</p>
<div class="language-bash codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_Lwh4">反编译 class 为 java</div><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-bash codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token plain"># 假设是 macOS 安装的 IDEA</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># IDEA 自带的反编译工具解密即可</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">java -cp ~/Applications/IntelliJ\ IDEA\ Ultimate.app/Contents/plugins/java-decompiler/lib/java-decompiler.jar \</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  org.jetbrains.java.decompiler.main.decompiler.ConsoleDecompiler \</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  -dgs=true \</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  src/main/class/ src/main/java/</span><br></span></code></pre></div></div>
<p>执行后 src/main/java 目录下会生成反编译后的 java 文件。</p>
<div class="theme-admonition theme-admonition-tip admonition_phik alert alert--success"><div class="admonitionHeading_zxjO"><span class="admonitionIcon_bSNx"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>tip</div><div class="admonitionContent_R6P7"><p>解压得到的 lib 目录(sprint-boot 的 jar)，可以直接加入到 classpath 中，然后即可直接在代码中调用 jar 或者直接运行 Application。</p></div></div>]]></content:encoded>
            <category>Java</category>
            <category>Decrypt</category>
        </item>
        <item>
            <title><![CDATA[我记录思考的方式简单总结]]></title>
            <link>https://wener.me/story/how-i-note</link>
            <guid>https://wener.me/story/how-i-note</guid>
            <pubDate>Thu, 18 Jul 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[1. README 涉足或者了解一个新的领域，需要先对一个领域有基础的理解]]></description>
            <content:encoded><![CDATA[<ol>
<li>README 涉足或者了解一个新的领域，需要先对一个领域有基础的理解
<ul>
<li>每种领域类型的框架结构可能会不一样</li>
<li>例如 疾病相关 <a href="https://wener.me/notes/healthcare/disease/adhesive-capsulitis" target="_blank" rel="noopener noreferrer">https://wener.me/notes/healthcare/disease/adhesive-capsulitis</a></li>
<li>例如 技术相关 <a href="https://wener.me/notes/voip/asterisk" target="_blank" rel="noopener noreferrer">https://wener.me/notes/voip/asterisk</a></li>
</ul>
</li>
<li>GLOSSARY 记录过程中遇到的各种术语，通过术语能够快速了解一个领域
<ul>
<li>术语通常涉及英语&amp;中文词汇</li>
<li>很多时候英文词汇更能体现本意</li>
<li>例如 健康相关 <a href="https://wener.me/notes/healthcare/glossary" target="_blank" rel="noopener noreferrer">https://wener.me/notes/healthcare/glossary</a></li>
<li>例如 AI机器学习相关 <a href="https://wener.me/notes/ai/ml/glossary" target="_blank" rel="noopener noreferrer">https://wener.me/notes/ai/ml/glossary</a></li>
</ul>
</li>
<li>AWESOME 了解和整理一个领域的资源，能够快速找到一个领域的优秀资源
<ul>
<li>如果是企业，则需要了解竞争对手</li>
<li>例如 Kubernetes 相关 <a href="https://wener.me/notes/devops/kubernetes/awesome" target="_blank" rel="noopener noreferrer">https://wener.me/notes/devops/kubernetes/awesome</a></li>
<li>例如 AI 相关 <a href="https://wener.me/notes/ai/ml/awesome" target="_blank" rel="noopener noreferrer">https://wener.me/notes/ai/ml/awesome</a></li>
</ul>
</li>
<li>FAQ 记录过程中的各种常见问题，常见问题能带来非常的价值，展现了一定的思考
<ul>
<li>例如 销售相关 <a href="https://wener.me/notes/economics/sales/faq" target="_blank" rel="noopener noreferrer">https://wener.me/notes/economics/sales/faq</a></li>
<li>例如 财务相关 <a href="https://wener.me/notes/economics/accounting/faq" target="_blank" rel="noopener noreferrer">https://wener.me/notes/economics/accounting/faq</a></li>
</ul>
</li>
<li>DESIGN 总结结论，统一自己的思考方式，得出自己的设计模式
<ul>
<li>沉淀价值和思考过程</li>
<li>迭代自己的思路</li>
<li>例如 ERP 相关 <a href="https://wener.me/notes/dev/design/erp" target="_blank" rel="noopener noreferrer">https://wener.me/notes/dev/design/erp</a></li>
</ul>
</li>
<li>Write in Public
<ul>
<li>理解和表达自己的思考、沉淀自己的思考、分享自己的思考</li>
<li>如果记录过程是 Public 的状态，那么就会更加注重质量</li>
</ul>
</li>
</ol>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="how-i-indexing-my-knowdge-by-taking-notes">如何用记笔记来索引知识<a href="https://wener.me/story/how-i-note#how-i-indexing-my-knowdge-by-taking-notes" class="hash-link" aria-label="Direct link to 如何用记笔记来索引知识" title="Direct link to 如何用记笔记来索引知识" translate="no">​</a></h2>
<blockquote>
<p>2022-07-21 的一些思考</p>
</blockquote>
<ol>
<li><strong>工作</strong> 是重复的任务 - Job is recurring work</li>
<li><strong>经验</strong> 是解决重复问题的方案 - Experience is solution for recurring situation</li>
<li><strong>知识</strong> 是重用经验 - Knowledge is about reusing experience</li>
<li><strong>记笔记</strong> 是重用获取知识的时间 - Noting is about reusing time used to acquire the knowledge</li>
</ol>
<p>要完成任务，只需了解一点点知识。知识是积累、演变和时间的结果，由小片段组成。条条大路通罗马，你必须找到自己的那条。</p>
<p>通过记笔记，首先做一组标记，然后深入挖掘，最终形成一个知识池。</p>
<p><strong>索引知识</strong></p>
<ul>
<li>大脑容量有限（字面意义上的）</li>
<li>索引是为了直达重点</li>
<li>重点如：列表、表格、代码 这样的表现方式</li>
<li>记录内容是关于：上下文、背景、原因、关系、为什么、注意事项 等</li>
</ul>
<p>重点可能非常简单，例如 Next.js 不支持 ESM，这就足够做出决策。
但如果你想了解更多，你将花费更多时间得出结论，是的，它还没准备好，<strong>但是...</strong>。</p>
<p>不要浪费5分钟选择技术，使用你已经足够了解的无聊技术。</p>
<p><strong>Thoughts</strong></p>
<ul>
<li><strong>体系结构和设计</strong> - 选择是关键，你最好了解它们。</li>
<li>不要在同一块石头上绊倒两次。</li>
<li>记笔记是为了自己，写文章和博客是为了别人阅读。</li>
<li>记笔记可以释放大脑压力，就像将思维分散到另一个地方。</li>
</ul>
<p>记笔记是思考的反映，就像散点图，索引将其变成有向图，这是我们建模知识的方式。
写作就像绘画，试图在说服你一个观点，这个观点在在标题或第一段中已经提出，其余的文字都是在证明“我早就告诉过你了”。</p>]]></content:encoded>
            <category>Thinking</category>
        </item>
        <item>
            <title><![CDATA[第一次尝试机器学习]]></title>
            <link>https://wener.me/story/ml-first-try</link>
            <guid>https://wener.me/story/ml-first-try</guid>
            <pubDate>Sat, 15 Jun 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[第一次开始正式接触机器学习领域，记录和整理一些中间过程。]]></description>
            <content:encoded><![CDATA[<p>第一次开始正式接触机器学习领域，记录和整理一些中间过程。</p>
<ul>
<li>Linode
<ul>
<li>32 GB + RTX6000 GPU x1 + 8C 32GB
<ul>
<li><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>1000</mn><mi mathvariant="normal">/</mi><mi>m</mi><mi>o</mi><mo separator="true">,</mo></mrow><annotation encoding="application/x-tex">1000/mo, </annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord">1000/</span><span class="mord mathnormal">m</span><span class="mord mathnormal">o</span><span class="mpunct">,</span></span></span></span>1.50/hr</li>
</ul>
</li>
<li>只有 eu-center 地区，太慢太慢了</li>
</ul>
</li>
<li>GCP
<ul>
<li>NVIDIA Tesla L4 + 4 vCPU + 16 GB
<ul>
<li>US<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>598.46</mn><mi mathvariant="normal">/</mi><mi>m</mi><mi>o</mi><mo separator="true">,</mo><mi>U</mi><mi>S</mi></mrow><annotation encoding="application/x-tex">598.46/mo, US</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord">598.46/</span><span class="mord mathnormal">m</span><span class="mord mathnormal">o</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathnormal" style="margin-right:0.10903em">U</span><span class="mord mathnormal" style="margin-right:0.05764em">S</span></span></span></span>0.82/hr</li>
</ul>
</li>
<li>有亚太地区 - 台湾</li>
</ul>
</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="gcp-deep-learning-vm">GCP Deep Learning VM<a href="https://wener.me/story/ml-first-try#gcp-deep-learning-vm" class="hash-link" aria-label="Direct link to GCP Deep Learning VM" title="Direct link to GCP Deep Learning VM" translate="no">​</a></h2>
<ul>
<li>存储至少 100 GB+
<ul>
<li>3854 张图片，缓存为 .npy</li>
</ul>
</li>
<li>基于 Debian 11 和 Ubuntu 22.04</li>
<li>SSH 进入的时候会提示安装 driver</li>
<li>默认环境
<ul>
<li>conda</li>
<li>numpy</li>
<li>scipy</li>
<li>matplotlib</li>
<li>pandas</li>
<li>nltk</li>
<li>pillow</li>
<li>scikit-image</li>
<li>opencv-python</li>
<li>scikit-learn</li>
<li>CUDA, CuNN, NCCL</li>
</ul>
</li>
<li><a href="https://cloud.google.com/deep-learning-vm/docs/introduction" target="_blank" rel="noopener noreferrer">https://cloud.google.com/deep-learning-vm/docs/introduction</a></li>
</ul>
<div class="language-bash codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-bash codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token plain">sudo apt install -y neofetch ffmpeg libc-bin</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># python 默认来自于 /opt/conda/bin/python</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">pip install --upgrade pip</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># 确定环境驱动正常</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">cat /proc/driver/nvidia/version</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">nvidia-smi</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">sudo docker run --gpus all nvidia/cuda:12.5.0-base-ubuntu22.04 nvidia-smi</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">#</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">sudo apt install -y make build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev libncursesw5-dev xz-utils tk-dev libffi-dev liblzma-dev git</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">#pip install nvidia-cublas-cu11 nvidia-cudnn-cu11</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">sudo apt install -y docker-buildx-plugin docker-compose-plugin</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># ultralytics -&gt;</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># py-cpuinfo, mpmath, triton, sympy, opencv-python, nvidia-nvtx-cu12, nvidia-nvjitlink-cu12, nvidia-nccl-cu12, nvidia-curand-cu12, nvidia-cufft-cu12, nvidia-cuda-runtime-cu12, nvidia-cuda-nvrtc-cu12, nvidia-cuda-cupti-cu12, nvidia-cublas-cu12, nvidia-cusparse-cu12, nvidia-cudnn-cu12, nvidia-cusolver-cu12, torch, ultralytics-thop, torchvision, ultralytics</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># better running in tmux</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">tmux new -A -s main</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithStickyNavbar_EG6R" id="could-not-load-library-libcudnn_cnn_trainso8">Could not load library libcudnn_cnn_train.so.8<a href="https://wener.me/story/ml-first-try#could-not-load-library-libcudnn_cnn_trainso8" class="hash-link" aria-label="Direct link to Could not load library libcudnn_cnn_train.so.8" title="Direct link to Could not load library libcudnn_cnn_train.so.8" translate="no">​</a></h3>
<ul>
<li>/opt/conda/lib/python3.10/site-packages/nvidia/cudnn/lib/libcudnn_cnn_train.so.8</li>
<li>/opt/conda/lib/python3.10/site-packages/torch/lib</li>
<li>/usr/local/cuda/lib64</li>
</ul>
<div class="language-text codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-text codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token plain">Could not load library libcudnn_cnn_train.so.8. Error: /usr/local/cuda/lib64/libcudnn_cnn_train.so.8: undefined symbol: _ZN5cudnn3cnn5infer22queryClusterPropertiesERPhS3_, version libcudnn_cnn_infer.so.8</span><br></span></code></pre></div></div>
<div class="language-bash codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-bash codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token plain">ldd /opt/conda/lib/python3.10/site-packages/nvidia/cudnn/lib/libcudnn_cnn_train.so.8</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">ldd /usr/local/cuda/lib64/libcudnn_cnn_train.so.8</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># 修改后就可以了</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">LD_LIBRARY_PATH=/opt/conda/lib/python3.10/site-packages/nvidia/cudnn/lib/:$LD_LIBRARY_PATH yolo</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">#LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/conda/lib/python3.10/site-packages/torch/lib</span><br></span></code></pre></div></div>
<ul>
<li><a href="https://github.com/pytorch/pytorch/issues/104591" target="_blank" rel="noopener noreferrer">https://github.com/pytorch/pytorch/issues/104591</a></li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="debian">Debian<a href="https://wener.me/story/ml-first-try#debian" class="hash-link" aria-label="Direct link to Debian" title="Direct link to Debian" translate="no">​</a></h2>
<div class="language-bash codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-bash codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token plain">apt update</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">apt -y upgrade</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">apt install -y build-essential python3 python3-pip pipx htop curl wget git jq neofetch</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># Components 增加 contrib non-free non-free-firmware</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">nano /etc/apt/sources.list.d/debian.sources</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">apt install -y firmware-misc-nonfree</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">#</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">apt install -y nvidia-detect nvidia-driver</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># nvidia-cuda-toolkit - 非常大</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">nvidia-detect</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># Docker Debian</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># ====================</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># Add Docker's official GPG key:</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">sudo apt-get update</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">sudo apt-get install ca-certificates curl</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">sudo install -m 0755 -d /etc/apt/keyrings</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">sudo curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">sudo chmod a+r /etc/apt/keyrings/docker.asc</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># Add the repository to Apt sources:</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian \</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  $(. /etc/os-release &amp;&amp; echo "$VERSION_CODENAME") stable" \</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  | sudo tee /etc/apt/sources.list.d/docker.list &gt; /dev/null</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">sudo apt-get update</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">sudo apt-get -y install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin</span><br></span></code></pre></div></div>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="ubuntu">Ubuntu<a href="https://wener.me/story/ml-first-try#ubuntu" class="hash-link" aria-label="Direct link to Ubuntu" title="Direct link to Ubuntu" translate="no">​</a></h2>
<ul>
<li>Ubuntu 22.04 LTS</li>
</ul>
<div class="language-bash codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-bash codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token plain">apt update</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">apt -y upgrade</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">apt install -y build-essential python3 python3-pip pipx htop curl wget git jq neofetch</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">pipx ensurepath</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">#pipx install poetry</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">#curl https://pyenv.run | bash</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># Nvdia Driver</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># ====================</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">lspci -nn | grep -E -i "3d|display|vga"</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># https://ubuntu.com/server/docs/nvidia-drivers-installation</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># https://www.nvidia.com/download/index.aspx</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># https://www.nvidia.com/Download/Find.aspx</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># apt install -y ubuntu-drivers-common</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># ubuntu-drivers list --gpgpu # 所有支持的 GPU</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># ubuntu-drivers install # 自动检测安装所有驱动</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># ubuntu-drivers install --gpgpu</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># ubuntu-drivers install --gpgpu nvidia:535-server</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># apt install nvidia-utils-535-server</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># nvidia-utils-535-server</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">apt install -y nvidia-driver-535</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># reboot # 重启后才能生效</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">nvidia-smi</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">cat /proc/driver/nvidia/version</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">#sudo add-apt-repository ppa:graphics-drivers/ppa</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">#sudo apt-get update</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># Docker Ubuntu</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># ====================</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">sudo apt-get update</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">sudo apt-get install ca-certificates curl</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">sudo install -m 0755 -d /etc/apt/keyrings</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">sudo chmod a+r /etc/apt/keyrings/docker.asc</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># Add the repository to Apt sources:</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release &amp;&amp; echo "$VERSION_CODENAME") stable" \</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  | sudo tee /etc/apt/sources.list.d/docker.list &gt; /dev/null</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">sudo apt-get update</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">sudo apt-get -y install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># Nvidia Container Toolkit</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># ====================</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg \</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  &amp;&amp; curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list \</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  | sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' \</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    | sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">sed -i -e '/experimental/ s/^#//g' /etc/apt/sources.list.d/nvidia-container-toolkit.list</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">sudo apt-get update</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">sudo apt-get install -y nvidia-container-toolkit nvidia-docker2</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">sudo systemctl restart docker</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># 测试</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># https://hub.docker.com/r/nvidia/cuda</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">sudo docker run --gpus all nvidia/cuda:12.5.0-base-ubuntu22.04 nvidia-smi</span><br></span></code></pre></div></div>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="env">ENV<a href="https://wener.me/story/ml-first-try#env" class="hash-link" aria-label="Direct link to ENV" title="Direct link to ENV" translate="no">​</a></h2>
<div class="language-bash codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-bash codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token plain"># rclone for dataset sync</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># ====================</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">curl -Lo /tmp/rclone.deb https://github.com/rclone/rclone/releases/download/v1.67.0/rclone-v1.67.0-linux-amd64.deb</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">sudo dpkg -i /tmp/rclone.deb</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">rclone --version</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># Python</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># ====================</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">python3 --version # 建议 3.9-3.11</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">sudo mkdir -p /data/ml</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">sudo chown -R $USER:$USER /data/ml</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">cd /data/ml</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># YoloV10</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># ====================</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">git clone https://github.com/THU-MIG/yolov10 /data/ml/yolov10</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">cd /data/ml/yolov10</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">python3 -m venv venv</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">source venv/bin/activate</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">pip3 install -r requirements.txt</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># Yolo</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># ====================</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">mkdir -p /data/ml/yolo</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">cd /data/ml/yolo</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">python3 -m venv venv</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">source venv/bin/activate</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">pip3 install openvino==2023.2 'datumaro[default]'</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">pip3 install ultralytics torch torchvision</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># rclone for sync</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># ====================</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># 接受 FTP 上传 - 修改账号密码</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">rclone serve ftp --addr 0.0.0.0:18080 /data/ --user USER --pass PASS</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># 同步 修改 IP、账号密码</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># rclone sync /data/ds/ :ftp,host=127.0.0.1,port=18080,user=USER,pass=$(rclone obscure PASS):ds -P --stats-one-line --transfers 10 -M</span><br></span></code></pre></div></div>
<ul>
<li>Nvdia Driver
<ul>
<li><a href="https://www.nvidia.com/en-us/drivers/rtx-enterprise-and-quadro-driver-branch-history/" target="_blank" rel="noopener noreferrer">NVIDIA RTX / Quadro Enterprise Driver Branch History for Windows</a></li>
</ul>
</li>
</ul>
<div class="language-bash codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-bash codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token plain"># for pyenv</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">apt install -y make build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev libncursesw5-dev xz-utils tk-dev libffi-dev liblzma-dev git</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">pyenv install 3.9</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">cd /data/ml/yolov10</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">pyenv local 3.9</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">cat &lt;&lt; 'EOF' &gt;&gt; ~/.bashrc</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">export PATH="$HOME/.pyenv/bin:$PATH"</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">eval "$(pyenv init --path)"</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">eval "$(pyenv init -)"</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">eval "$(pyenv virtualenv-init -)"</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">EOF</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">source ~/.bashrc</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># 手动</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">export PYENV_ROOT="$HOME/.pyenv"</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">[[ -d $PYENV_ROOT/bin ]] &amp;&amp; export PATH="$PYENV_ROOT/bin:$PATH"</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">eval "$(pyenv init -)"</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># 自动</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># ~/.bashrc</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">eval "$(pyenv virtualenv-init -)"</span><br></span></code></pre></div></div>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="nvtop">nvtop<a href="https://wener.me/story/ml-first-try#nvtop" class="hash-link" aria-label="Direct link to nvtop" title="Direct link to nvtop" translate="no">​</a></h2>
<div class="language-bash codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-bash codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token plain">curl -o nvtop -L https://github.com/Syllo/nvtop/releases/download/3.2.0/nvtop-3.2.0-x86_64.AppImage</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">chmod +x nvtop</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">./nvtop</span><br></span></code></pre></div></div>
<p><strong>from source</strong></p>
<div class="language-bash codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-bash codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token plain">git clone https://github.com/Syllo/nvtop.git /data/gits/nvtop</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">cd /data/gits/nvtop</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">mkdir build</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">cd build</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">cmake ..</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">make</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">sudo make install</span><br></span></code></pre></div></div>
<ul>
<li><a href="https://github.com/Syllo/nvtop" target="_blank" rel="noopener noreferrer">https://github.com/Syllo/nvtop</a></li>
</ul>
<h1>FAQ</h1>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="non-monotonic-dts-previous-22695088-current-22695088-changing-to-22695089-this-may-result-in-incorrect-timestamps-in-the-output-file">Non-monotonic DTS; previous: 22695088, current: 22695088; changing to 22695089. This may result in incorrect timestamps in the output file.<a href="https://wener.me/story/ml-first-try#non-monotonic-dts-previous-22695088-current-22695088-changing-to-22695089-this-may-result-in-incorrect-timestamps-in-the-output-file" class="hash-link" aria-label="Direct link to Non-monotonic DTS; previous: 22695088, current: 22695088; changing to 22695089. This may result in incorrect timestamps in the output file." title="Direct link to Non-monotonic DTS; previous: 22695088, current: 22695088; changing to 22695089. This may result in incorrect timestamps in the output file." translate="no">​</a></h2>
<div class="language-bash codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-bash codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token plain">ffmpeg -fflags +genpts -i input.flv -c copy -vsync 1 output.mp4</span><br></span></code></pre></div></div>
<ul>
<li>flv -&gt; mp4 出现
<ul>
<li>实时视频流抖动导致</li>
</ul>
</li>
<li>DTS - Decoding Time Stamp</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="cache">cache<a href="https://wener.me/story/ml-first-try#cache" class="hash-link" aria-label="Direct link to cache" title="Direct link to cache" translate="no">​</a></h2>
<div class="language-bash codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-bash codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token plain"># clip gdown huggingface matplotlib pip torch</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">du -sh ~/.cache</span><br></span></code></pre></div></div>]]></content:encoded>
            <category>Setup</category>
            <category>Ubuntu</category>
            <category>Debian</category>
        </item>
        <item>
            <title><![CDATA[为什么选择 Alpine Linux?]]></title>
            <link>https://wener.me/story/why-alpine</link>
            <guid>https://wener.me/story/why-alpine</guid>
            <pubDate>Wed, 06 Mar 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[Small. Simple. Secure.]]></description>
            <content:encoded><![CDATA[<blockquote>
<p>Small. Simple. Secure.</p>
<p>Alpine Linux is a security-oriented, lightweight Linux distribution based on musl libc and busybox.</p>
<p>Alpine Linux 是一个基于 musl libc 和 busybox, 面向安全, 轻量级的 Linux 发布版.</p>
</blockquote>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="why">为什么<a href="https://wener.me/story/why-alpine#why" class="hash-link" aria-label="Direct link to 为什么" title="Direct link to 为什么" translate="no">​</a></h2>
<p>small footprint, non-systemd, fast enough, good community, sane defaults.</p>
<p>阿里云、腾讯云、物理服务器、虚拟机、容器都适用 AlpineLinux.</p>
<ol>
<li>环境都一样，使用各方面熟悉，熟练</li>
<li>小/快 - 阿里云 ECS 只需要上传一个 几十 MB 的镜像即可，从 0 安装只需要 3 分钟</li>
<li>简单 - 可以由内而外的了解所有 alpine 细节，对于 debian 和 centos 我都做不到，因为太复杂</li>
<li>跟上时代 - 内核 一般是最近的 lts，能快速利用上新的内核特性，比如现在 linux 内核支持 io_uring, ntfs</li>
<li>快速更新 - 安全问题响应非常快 - 因为使用面非常广</li>
<li>衍生业务集成系统 - 系统预装一些软件和服务 - 例如: k3sos</li>
</ol>
<p><strong>不适用场景</strong></p>
<ol>
<li>机器学习 - ubuntu/debian 是最好的 - 最新 Linux 开始要集成 Nvidia 驱动，情况会有所好转，目前 Nvidia 官方尚未正式支持Alpine。</li>
<li>商务用桌面系统 - 默认 xfce - <em>生态</em> 和体验没有 ubuntu 好</li>
<li>定制化嵌入式设备 - alpine 支持的 arch 远没有 debian 的多，如果 arch 支持可以考虑 alpine</li>
</ol>
<p><strong>Not Convinced?</strong></p>
<ul>
<li>轻量级
<ul>
<li>最小安装 5MB</li>
<li>musl</li>
<li>openrc</li>
</ul>
</li>
<li>部分包提供静态编译二进制，可在非 Alpine 环境使用</li>
<li>稳定滚动升级
<ul>
<li>有稳定版和最新版</li>
<li>每半年一个稳定版</li>
<li>稳定版会维护两年</li>
<li>可非常简便的升级</li>
</ul>
</li>
<li>简单的包管理
<ul>
<li>APK 本地存储逻辑和结构非常简单</li>
<li>APK 仓库逻辑结构简单 - 不同于 deb/rpm</li>
<li>包的构建也很简单 - abuild 能够在本地构建包</li>
</ul>
</li>
<li>现代化
<ul>
<li>内核版本较新
<ul>
<li>能够利用上很多新内核的功能</li>
</ul>
</li>
<li>包版本比较新
<ul>
<li>例如 zfs, docker 等都是对应版本的最新版</li>
</ul>
</li>
</ul>
</li>
<li>生态圈丰富
<ul>
<li>包非常多</li>
<li>兼容良好</li>
<li>社区活跃</li>
</ul>
</li>
<li>支持较多平台
<ul>
<li>x86_64</li>
<li>x86</li>
<li>aarch64</li>
<li>armhf</li>
<li>ppc64le</li>
<li>s390x</li>
<li>armv7</li>
<li>riscv64</li>
</ul>
</li>
<li>支持树莓派</li>
<li>很多 docker 镜像基于 AlpineLinux
<ul>
<li>Docker For Mac 的基础系统是 AlpineLinux</li>
</ul>
</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="when-not">Alpine 的缺点<a href="https://wener.me/story/why-alpine#when-not" class="hash-link" aria-label="Direct link to Alpine 的缺点" title="Direct link to Alpine 的缺点" translate="no">​</a></h2>
<ul>
<li>文档不够全面
<ul>
<li>Wiki 内容较少, 更新不多</li>
<li>但大多文档可参考 Arch 和 Gentoo</li>
</ul>
</li>
<li>musl libc 可能有兼容问题, 有时候需要补丁
<ul>
<li>但越来越多的开发者也都会做兼容了</li>
<li><a href="https://wener.me/notes/os/linux/libc/musl/faq" target="_blank" rel="noopener noreferrer">常见的 Musl 问题</a></li>
</ul>
</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="faq">使用过程常见问题<a href="https://wener.me/story/why-alpine#faq" class="hash-link" aria-label="Direct link to 使用过程常见问题" title="Direct link to 使用过程常见问题" translate="no">​</a></h2>
<div class="language-bash codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-bash codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token plain"># 建议安装基础包，对新人使用相对友好</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">apk add curl busybox-extras file nano libc6-compat gcompat bash</span><br></span></code></pre></div></div>
<ul>
<li><a href="https://wener.me/notes/os/alpine/faq" target="_blank" rel="noopener noreferrer">使用 Alpine 的常见问题</a></li>
<li>制作各种 Docker 可参考 <a href="https://github.com/wenerme/dockerfiles" target="_blank" rel="noopener noreferrer">wenerme/dockerfiles</a></li>
<li>针对 Alpine 的各种 Ansible 任务 <a href="https://github.com/wenerme/ansible-collection-wenerme-alpine" target="_blank" rel="noopener noreferrer">wenerme/ansible-collection-wenerme-alpine</a></li>
</ul>]]></content:encoded>
            <category>DevOps</category>
            <category>Alpine</category>
        </item>
        <item>
            <title><![CDATA[基于 SNI 实现无感全局代理]]></title>
            <link>https://wener.me/story/sni-proxy</link>
            <guid>https://wener.me/story/sni-proxy</guid>
            <pubDate>Wed, 21 Jun 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[基本结构]]></description>
            <content:encoded><![CDATA[<p><strong>基本结构</strong></p>
<div class="mermaid"></div>
<ol>
<li>路由器上配置 DNS 为局域网内的一个节点</li>
<li>路由器上配置 路由 172.32.1.1 到内网的一个 IP</li>
<li>DNS 匹配需要代理的路由返回 172.32.1.1</li>
<li>172.32.1.1 上部署 sni 代理，监听 80、443</li>
</ol>
<p><strong>前置条件</strong></p>
<ul>
<li>已有相关代理服务</li>
</ul>
<p><strong>后置条件</strong></p>
<ul>
<li>修改路由器配置</li>
</ul>
<p><strong>应用场景</strong></p>
<ul>
<li>家庭
<ul>
<li>无感</li>
</ul>
</li>
<li>服务器运维 - 云服务器、物理服务器
<ul>
<li>无感</li>
<li>能支持任意场景</li>
</ul>
</li>
<li>公司局域网
<ul>
<li>能限制范围</li>
</ul>
</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="手动配置节点">手动配置节点<a href="https://wener.me/story/sni-proxy#%E6%89%8B%E5%8A%A8%E9%85%8D%E7%BD%AE%E8%8A%82%E7%82%B9" class="hash-link" aria-label="Direct link to 手动配置节点" title="Direct link to 手动配置节点" translate="no">​</a></h2>
<div class="language-bash codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-bash codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token plain">ip addr add 172.32.1.1/32 dev eth0 # 增加 IP</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># 安装部署 gost - 提供 SNI 代理</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># ===============</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">curl -LO https://github.com/go-gost/gost/releases/download/v3.0.0-rc8/gost_3.0.0-rc8_linux_amd64.tar.gz</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">tar zxvf gost*.tar.gz</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># 监听 80, 443 - 上游代理为 192.168.1.2:7890，如果在当前节点也可以 127.0.0.1:7890</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">./gost -L sni://:80 -L sni://:443 -F socks5://192.168.1.2:7890</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># 测试 gost 代理是否成功</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">curl -H 'Host: google.com' 127.0.0.1</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># 安装部署 dnsmasq</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># ===============</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">apk add dnsmasq</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># 上游配置</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">cat &lt;&lt; EOF &gt; /etc/dnsmasq.d/main.conf</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">server=223.5.5.5</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">log-queries=extra</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">EOF</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># 配置代理常用的域名</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">curl -L https://ghproxy.com/raw.githubusercontent.com/wenerme/wener/master/notes/service/dns/gfwlist.txt \</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  | sed -E 's#.+#address=/&amp;/172.32.1.1#' &gt; /etc/dnsmasq.d/gfwlist.conf</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># 测试启动</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">dnsmasq -d</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># 测试，返回 172.32.1.1 为正常结果</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">nslookup google.com 127.0.0.1</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># service dnsmasq start # 服务启动</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># rc-update add dnsmasq # 开机自动启动</span><br></span></code></pre></div></div>
<ul>
<li>配置路由器
<ul>
<li>DNS</li>
<li>路由 172.32.1.1 到内网节点</li>
</ul>
</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="docker-启动服务">Docker 启动服务<a href="https://wener.me/story/sni-proxy#docker-%E5%90%AF%E5%8A%A8%E6%9C%8D%E5%8A%A1" class="hash-link" aria-label="Direct link to Docker 启动服务" title="Direct link to Docker 启动服务" translate="no">​</a></h2>
<div class="language-bash codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-bash codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token plain"># --network=host or -p 80:80 -p 443:443 -p 53:53/udp</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># -e FAKEIP=172.32.1.1</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">docker run --rm -it -e PROXY=socks5://192.168.66.1:7890 -p 80:80 -p 443:443 -p 53:53/udp --name proxy wener/sni-rev-proxy</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">nslookup google.com 127.0.0.1</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">curl --resolve google.com:80:127.0.0.1 google.com</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># 添加 FAKEIP</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">ip addr add 172.32.1.1/32 dev eth0</span><br></span></code></pre></div></div>
<ul>
<li>配置路由器
<ul>
<li>DNS</li>
<li>路由 172.32.1.1 到内网节点</li>
</ul>
</li>
</ul>
<h1>FAQ</h1>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="无法修改路由器">无法修改路由器<a href="https://wener.me/story/sni-proxy#%E6%97%A0%E6%B3%95%E4%BF%AE%E6%94%B9%E8%B7%AF%E7%94%B1%E5%99%A8" class="hash-link" aria-label="Direct link to 无法修改路由器" title="Direct link to 无法修改路由器" translate="no">​</a></h2>
<p>本地使用直接修改 resolv.conf 或者 hosts</p>
<div class="language-txt codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_Lwh4">/etc/resolv.conf</div><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-txt codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token plain">nameserver 127.0.0.1</span><br></span></code></pre></div></div>
<ul>
<li>使用部署 DNS 的节点 IP</li>
</ul>
<div class="language-txt codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_Lwh4">/etc/hosts</div><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-txt codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token plain">172.32.1.1 docker.io</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">172.32.1.1 registry-1.docker.io</span><br></span></code></pre></div></div>
<ul>
<li>直接映射单个域名</li>
<li>注意： CNAME 的域名也需要映射</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="为��什么用-1723211">为什么用 172.32.1.1<a href="https://wener.me/story/sni-proxy#%E4%B8%BA%E4%BB%80%E4%B9%88%E7%94%A8-1723211" class="hash-link" aria-label="Direct link to 为什么用 172.32.1.1" title="Direct link to 为什么用 172.32.1.1" translate="no">​</a></h2>
<ul>
<li>172.32.1.1 为公网 IP 地址，但这个地址被使用的概率非常低</li>
<li>如果不给代理节点一个公网地址而是使用内网地址，那么 iOS 访问网络时会提示 “是否允许应用访问本地网络”。</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="为什么选择-dnssni-作为全局代理">为什么选择 DNS+SNI 作为全局代理<a href="https://wener.me/story/sni-proxy#%E4%B8%BA%E4%BB%80%E4%B9%88%E9%80%89%E6%8B%A9-dnssni-%E4%BD%9C%E4%B8%BA%E5%85%A8%E5%B1%80%E4%BB%A3%E7%90%86" class="hash-link" aria-label="Direct link to 为什么选择 DNS+SNI 作为全局代理" title="Direct link to 为什么选择 DNS+SNI 作为全局代理" translate="no">​</a></h2>
<ul>
<li>客户端无感，不需要改任何配置</li>
<li>能实现全局 - 例如 docker 不需要就行配置，k8s 不需要就行配置 就能拉取镜像</li>
<li>代理范围易于控制 - DNS 层控制</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="能不能不限制域名所有都代理">能不能不限制域名所有都代理<a href="https://wener.me/story/sni-proxy#%E8%83%BD%E4%B8%8D%E8%83%BD%E4%B8%8D%E9%99%90%E5%88%B6%E5%9F%9F%E5%90%8D%E6%89%80%E6%9C%89%E9%83%BD%E4%BB%A3%E7%90%86" class="hash-link" aria-label="Direct link to 能不能不限制域名所有都代理" title="Direct link to 能不能不限制域名所有都代理" translate="no">​</a></h2>
<ul>
<li>可以，dnsmasq 默认全部返回固定 ip</li>
<li>确保上游代理支持策略控制即可，例如 clash</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="关于上游-dns">关于上游 DNS<a href="https://wener.me/story/sni-proxy#%E5%85%B3%E4%BA%8E%E4%B8%8A%E6%B8%B8-dns" class="hash-link" aria-label="Direct link to 关于上游 DNS" title="Direct link to 关于上游 DNS" translate="no">​</a></h2>
<ul>
<li>可以选择公共的，例如 223.5.5.5</li>
<li>也可以选择自己部署的 adguard 之类的
<ul>
<li>支持拦截隐私相关域名</li>
<li>避免 DNS 污染</li>
</ul>
</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="其他无感全局代理的方式">其他无感全局代理的方式<a href="https://wener.me/story/sni-proxy#%E5%85%B6%E4%BB%96%E6%97%A0%E6%84%9F%E5%85%A8%E5%B1%80%E4%BB%A3%E7%90%86%E7%9A%84%E6%96%B9%E5%BC%8F" class="hash-link" aria-label="Direct link to 其他无感全局代理的方式" title="Direct link to 其他无感全局代理的方式" translate="no">​</a></h2>
<ol>
<li>修改网关，tproxy - 无感，但是重新部署 dhcp，替代路由作为网关</li>
<li>在 WiFi+Lan 的节点上，将 WiFi 配置为走代理，连 WiFi 则走代理，增加一个 lan 不修改现有。</li>
</ol>]]></content:encoded>
            <category>代理</category>
            <category>运维</category>
        </item>
        <item>
            <title><![CDATA[恢复群晖数据盘]]></title>
            <link>https://wener.me/story/recover-synology</link>
            <guid>https://wener.me/story/recover-synology</guid>
            <pubDate>Mon, 29 May 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[Recover Synology]]></description>
            <content:encoded><![CDATA[<h2 class="anchor anchorWithStickyNavbar_EG6R" id="recover-synology">Recover Synology<a href="https://wener.me/story/recover-synology#recover-synology" class="hash-link" aria-label="Direct link to Recover Synology" title="Direct link to Recover Synology" translate="no">​</a></h2>
<ul>
<li>Recover Synology from AlpineLinux</li>
<li>btrfs report I/O Error</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="问题">问题<a href="https://wener.me/story/recover-synology#%E9%97%AE%E9%A2%98" class="hash-link" aria-label="Direct link to 问题" title="Direct link to 问题" translate="no">​</a></h2>
<p>群晖的盘搞的很复杂</p>
<ul>
<li>mdraid -&gt; lvm -&gt; btrfs</li>
</ul>
<p>只看到 btrfs 报错（I/O Error），无法使用，没看到具体的磁盘错误，只能逐级排查。</p>
<p>主要目的是挂载 btrfs 恢复数据。</p>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="操作">操作<a href="https://wener.me/story/recover-synology#%E6%93%8D%E4%BD%9C" class="hash-link" aria-label="Direct link to 操作" title="Direct link to 操作" translate="no">​</a></h2>
<div class="language-bash codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-bash codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token plain">file -s /dev/sda1 # linux_raid_member</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">file -s /dev/sda2 # swap</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">file -s /dev/sda3 # 发现是用的 mdadm RAID 6</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># /dev/sda3: Linux Software RAID version 1.2 (1) UUID= name=DF:2 level=6 disks=4</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">apk add mdadm</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">mdadm --examine --scan  # 扫描</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">mdadm --assemble --scan # 添加</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">cat /proc/mdstat        # 状态</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">file -s /dev/md127 # 发现是 LVM</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># /dev/md127: LVM2 PV (Linux Logical Volume Manager), UUID: , size: 11980386729984</span><br></span></code></pre></div></div>
<p><strong>挂载 LVM</strong></p>
<div class="language-bash codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-bash codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token plain">apk add lvm2</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">pvscan</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># PV /dev/md127   VG vg1             lvm2 [&lt;10.90 TiB / 604.00 MiB free]</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">vgscan</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># Found volume group "vg1" using metadata type lvm2</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">lvdisplay</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">lvs</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># /dev/vg1/syno_vg_reserved_area</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># /dev/vg1/volume_1</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">vgchange -ay vg1 # 激活 vg1</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">file -s /dev/vg1/volume_1 # -&gt; /dev/mapper/vg1-volume_1</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">file -s /dev/mapper/vg1-volume_1</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># /dev/mapper/vg1-volume_1: BTRFS Filesystem label "2022.12.04-17:42:08 v42661", sectorsize 4096, nodesize 16384, leafsize 16384, UUID=, 151894196224/11979737530368 bytes used, 1 devices</span><br></span></code></pre></div></div>
<p><strong>挂载 btrfs</strong></p>
<div class="language-bash codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-bash codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token plain">apk add btrfs-progs btrfs-progs-extra</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">modprobe btrfs</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">btrfs device scan</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">btrfs filesystem show</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">btrfs check /dev/mapper/vg1-volume_1 # 尝试检测</span><br></span></code></pre></div></div>
<div class="language-text codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-text codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token plain">ERROR: errors found in fs roots</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">found 151884984320 bytes used, error(s) found</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">total csum bytes: 2896392</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">total tree bytes: 127860736</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">total fs tree bytes: 99958784</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">total extent tree bytes: 15761408</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">btree space waste bytes: 25632487</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">file data blocks allocated: 103613448192</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> referenced 103061467136</span><br></span></code></pre></div></div>
<ul>
<li>发现不少问题</li>
</ul>
<div class="language-bash codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-bash codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token plain">mount /dev/mapper/vg1-volume_1 /mnt # 尝试挂载</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># wrong fs type, bad option, bad superblock on /dev/mapper/vg1-volume_1</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">dmesg # 内核日志找原因</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># BTRFS critical (device dm-1): corrupt leaf</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">mount -t btrfs -o recovery,ro /dev/mapper/vg1-volume_1 /mnt</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">btrfs check /dev/mapper/vg1-volume_1 --repair # 尝试修复，但是失败</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># ERROR: failed to repair root items: I/O error</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">btrfs scrub start -Bf /dev/mapper/vg1-volume_1</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># btrfs rescue zero-log /dev/&lt;device_name&gt;</span><br></span></code></pre></div></div>
<ul>
<li>smartctl 尝试检测硬盘问题</li>
<li>mdadm 尝试 resync</li>
</ul>
<blockquote>
<p>无解, TODO</p>
</blockquote>
<div class="language-text codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-text codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token plain">/dev/mapper/cachedev_0 on /volume1 type btrfs (rw,nodev,relatime,ssd,synoacl,space_cache=v2,auto_reclaim_space,metadata_ratio=50,block_group_cache_tree,subvolid=256,subvol=/@syno)</span><br></span></code></pre></div></div>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="wrong-fs-type-bad-option-bad-superblock-on-devmappervg1-volume_1">wrong fs type, bad option, bad superblock on /dev/mapper/vg1-volume_1<a href="https://wener.me/story/recover-synology#wrong-fs-type-bad-option-bad-superblock-on-devmappervg1-volume_1" class="hash-link" aria-label="Direct link to wrong fs type, bad option, bad superblock on /dev/mapper/vg1-volume_1" title="Direct link to wrong fs type, bad option, bad superblock on /dev/mapper/vg1-volume_1" translate="no">​</a></h2>
<div class="language-bash codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-bash codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token plain">mount -t btrfs /dev/mapper/vg1-volume_1 /mnt</span><br></span></code></pre></div></div>
<div class="language-txt codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-txt codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token plain">BTRFS: device label 2022.12.04-17:42:08 v42661 devid 1 transid 449145 /dev/mapper/vg1-volume_1 scanned by mount (4067)</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">BTRFS info (device dm-1): using crc32c (crc32c-intel) checksum algorithm</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">BTRFS info (device dm-1): using free space tree</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">BTRFS critical (device dm-1): corrupt leaf: root=1 block=668844032 slot=1, invalid root flags, have 0x400000000 expect mask 0x1000000000001</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">BTRFS error (device dm-1): read time tree block corruption detected on logical 668844032 mirror 1</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">BTRFS critical (device dm-1): corrupt leaf: root=1 block=668844032 slot=1, invalid root flags, have 0x400000000 expect mask 0x1000000000001</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">BTRFS error (device dm-1): read time tree block corruption detected on logical 668844032 mirror 2</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">BTRFS error (device dm-1): open_ctree failed</span><br></span></code></pre></div></div>
<div class="language-bash codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-bash codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token plain">btrfs check /dev/mapper/vg1-volume_1 --repair</span><br></span></code></pre></div></div>
<div class="language-bash codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-bash codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token plain">ERROR: failed to repair root items: I/O error</span><br></span></code></pre></div></div>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="smat-check">S.M.A.T Check<a href="https://wener.me/story/recover-synology#smat-check" class="hash-link" aria-label="Direct link to S.M.A.T Check" title="Direct link to S.M.A.T Check" translate="no">​</a></h2>
<div class="language-bash codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-bash codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token plain">apk add smartmontools</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">smartctl -t long /dev/sda</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">smartctl -t long /dev/sdb</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">smartctl -t long /dev/sdc</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">smartctl -t long /dev/sdd</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># 很慢</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">smartctl -l selftest /dev/sda</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">smartctl -l selftest /dev/sdb</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">smartctl -l selftest /dev/sdc</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">smartctl -l selftest /dev/sdd</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">smartctl -a /dev/sda</span><br></span></code></pre></div></div>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="lvm-check">LVM Check<a href="https://wener.me/story/recover-synology#lvm-check" class="hash-link" aria-label="Direct link to LVM Check" title="Direct link to LVM Check" translate="no">​</a></h2>
<div class="language-bash codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-bash codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token plain">vgck vg1 -v</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">pvck /dev/md127</span><br></span></code></pre></div></div>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="mdadm-check">mdadm check<a href="https://wener.me/story/recover-synology#mdadm-check" class="hash-link" aria-label="Direct link to mdadm check" title="Direct link to mdadm check" translate="no">​</a></h2>
<blockquote>
<p>4*10T SAS 需要跑 8h, iostat -&gt; ~200MB/s</p>
</blockquote>
<div class="language-bash codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-bash codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token plain">mdadm --detail /dev/md127</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">mdadm --action=check /dev/md127</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># 查看有问题的块</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">watch cat /sys/block/md127/md/mismatch_cnt</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># 查看进度</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">cat /proc/mdstat</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">cat /sys/block/md127/md/sync_action</span><br></span></code></pre></div></div>
<div class="language-text codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-text codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token plain">md127: mismatch sector in range 713232-713240</span><br></span></code></pre></div></div>
<div class="language-bash codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-bash codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token plain">echo idle &gt; /sys/block/md127/md/sync_action # 停止 check</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">mdadm --action=repair /dev/md127            # 尝试修复 - repair=resync</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">iostat -h                                   # write 是有修复</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># K/Sec</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">cat /proc/sys/dev/raid/speed_limit_max</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">sysctl dev.raid.speed_limit_max # 200000</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">sysctl dev.raid.speed_limit_max=2000000</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># 不是平滑限速，而是平均 - 因此 resync 一会儿满速，一会儿 0</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">sysctl dev.raid.speed_limit_max=100000 # 如果觉得修复了问题，可以降低速度，然后尝试系统操作</span><br></span></code></pre></div></div>
<ul>
<li><a href="https://unix.stackexchange.com/a/531230/47774" target="_blank" rel="noopener noreferrer">https://unix.stackexchange.com/a/531230/47774</a></li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="btrfs-critical-device-dm-1-corrupt-leaf">BTRFS critical (device dm-1): corrupt leaf<a href="https://wener.me/story/recover-synology#btrfs-critical-device-dm-1-corrupt-leaf" class="hash-link" aria-label="Direct link to BTRFS critical (device dm-1): corrupt leaf" title="Direct link to BTRFS critical (device dm-1): corrupt leaf" translate="no">​</a></h2>
<div class="language-text codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-text codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token plain">BTRFS error (device dm-1): read time tree block corruption detected on logical 668844032 mirror 2</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">BTRFS critical (device dm-1): corrupt leaf: root=1 block=668844032 slot=1, invalid root flags, have 0x400000000 expect mask 0x1000000000001</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">BTRFS error (device dm-1): read time tree block corruption detected on logical 668844032 mirror 1</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">BTRFS critical (device dm-1): corrupt leaf: root=1 block=668844032 slot=1, invalid root flags, have 0x400000000 expect mask 0x1000000000001</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">BTRFS error (device dm-1): read time tree block corruption detected on logical 668844032 mirror 2</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">BTRFS error (device dm-1): open_ctree failed</span><br></span></code></pre></div></div>
<div class="language-bash codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-bash codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token plain">btrfs check /dev/mapper/vg1-volume_1 --repair</span><br></span></code></pre></div></div>
<div class="language-txt codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-txt codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token plain">[1/7] checking root items</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">checksum verify failed on 711114752 wanted 0xed010ef2 found 0xb32e10d9</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">checksum verify failed on 711114752 wanted 0xed010ef2 found 0x3a406fa5</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">checksum verify failed on 711114752 wanted 0xed010ef2 found 0xb32e10d9</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">Csum didn't match</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">ERROR: failed to repair root items: I/O error</span><br></span></code></pre></div></div>
<hr>
<div class="language-text codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-text codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token plain">BTRFS: device label 2022.12.04-17:42:08 v42661 devid 1 transid 449145 /dev/mapper/vg1-volume_1 scanned by btrfs (22854)</span><br></span></code></pre></div></div>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="btrfs-backup">btrfs backup<a href="https://wener.me/story/recover-synology#btrfs-backup" class="hash-link" aria-label="Direct link to btrfs backup" title="Direct link to btrfs backup" translate="no">​</a></h2>
<p>apk add partclone</p>
<div class="language-bash codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-bash codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token plain">#btrfstune -u /dev/mapper/vg1-volume_1</span><br></span></code></pre></div></div>
<p><a href="https://manpages.debian.org/jessie/partclone/partclone.btrfs.8" target="_blank" rel="noopener noreferrer">https://manpages.debian.org/jessie/partclone/partclone.btrfs.8</a></p>
<p>noerror: Instructs dd to continue operation, ignoring all read errors
sync: Instruct dd to fill input blocks with zeroes if there were any read errors</p>
<p>dd if=/dev/sda of=/dev/sdb1 bs=1MB conv=noerror,sync status=progress
| gzip -c &gt; backup.img.gz
gunzip -c /PATH/TO/DRIVE/backup_image.img.gz | dd of=/dev/sda</p>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="lv-status-not-available">LV Status NOT available<a href="https://wener.me/story/recover-synology#lv-status-not-available" class="hash-link" aria-label="Direct link to LV Status NOT available" title="Direct link to LV Status NOT available" translate="no">​</a></h2>
<p>是因为没有 active</p>
<div class="language-bash codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-bash codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token plain">vgchange -ay vg1</span><br></span></code></pre></div></div>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="warning-pv-devmd127-in-vg-vg1-is-using-an-old-pv-header-modify-the-vg-to-update">WARNING: PV /dev/md127 in VG vg1 is using an old PV header, modify the VG to update.<a href="https://wener.me/story/recover-synology#warning-pv-devmd127-in-vg-vg1-is-using-an-old-pv-header-modify-the-vg-to-update" class="hash-link" aria-label="Direct link to WARNING: PV /dev/md127 in VG vg1 is using an old PV header, modify the VG to update." title="Direct link to WARNING: PV /dev/md127 in VG vg1 is using an old PV header, modify the VG to update." translate="no">​</a></h2>
<p>不管</p>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="file-descriptor-63-pipe-111755-leaked-on-pvck-invocation">File descriptor 63 (pipe: 111755) leaked on pvck invocation.<a href="https://wener.me/story/recover-synology#file-descriptor-63-pipe-111755-leaked-on-pvck-invocation" class="hash-link" aria-label="Direct link to File descriptor 63 (pipe: 111755) leaked on pvck invocation." title="Direct link to File descriptor 63 (pipe: 111755) leaked on pvck invocation." translate="no">​</a></h2>
<div class="language-bash codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-bash codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token plain">LVM_SUPPRESS_FD_WARNINGS=1 vgck vg1</span><br></span></code></pre></div></div>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="参考">参考<a href="https://wener.me/story/recover-synology#%E5%8F%82%E8%80%83" class="hash-link" aria-label="Direct link to 参考" title="Direct link to 参考" translate="no">​</a></h2>
<ul>
<li><a href="https://man7.org/linux/man-pages/man8/btrfs.8.html" target="_blank" rel="noopener noreferrer">btrfs.8</a></li>
<li><a href="https://man7.org/linux/man-pages/man8/btrfs-check.8.html" target="_blank" rel="noopener noreferrer">btrfs-check.8</a></li>
<li><a href="https://man7.org/linux/man-pages/man8/vgchange.8.html" target="_blank" rel="noopener noreferrer">vgchange.8</a></li>
<li><a href="https://man7.org/linux/man-pages/man8/vgck.8.html" target="_blank" rel="noopener noreferrer">vgck.8</a></li>
<li><a href="https://man7.org/linux/man-pages/man4/md.4.html" target="_blank" rel="noopener noreferrer">md.4</a></li>
<li><a href="https://man7.org/linux/man-pages/man8/mdadm.8.html" target="_blank" rel="noopener noreferrer">mdadm.8</a></li>
<li>BTRFS: failed to read log tree
<ul>
<li><code>btrfs rescue zero-log /dev/&lt;devicename&gt;</code></li>
<li><a href="https://www.suse.com/support/kb/doc/?id=000018761" target="_blank" rel="noopener noreferrer">https://www.suse.com/support/kb/doc/?id=000018761</a></li>
</ul>
</li>
<li><a href="https://www.cyberciti.biz/tips/linux-raid-increase-resync-rebuild-speed.html" target="_blank" rel="noopener noreferrer">https://www.cyberciti.biz/tips/linux-raid-increase-resync-rebuild-speed.html</a></li>
</ul>
<p><strong>clean up</strong></p>
<div class="language-bash codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-bash codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token plain">apk del mdadm lvm2 btrfs-progs btrfs-progs-extra</span><br></span></code></pre></div></div>]]></content:encoded>
            <category>AlpineLinux</category>
            <category>运维</category>
        </item>
        <item>
            <title><![CDATA[迁移阿里云 CDN 到 Cloudflare]]></title>
            <link>https://wener.me/story/migrate-aliyun-cdn-to-cf</link>
            <guid>https://wener.me/story/migrate-aliyun-cdn-to-cf</guid>
            <pubDate>Fri, 07 Oct 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[每月节省 30¥ 阿里云全站 CDN 流量费用。]]></description>
            <content:encoded><![CDATA[<p><img decoding="async" loading="lazy" src="https://wener.me/assets/images/2022-10-07-cf-stat-5b42e5f04a314944c410c5b8ae8f550d.png" width="1352" height="1012" class="img_IxRA"></p>
<p>每月节省 30¥ 阿里云全站 CDN 流量费用。</p>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="背景">背景<a href="https://wener.me/story/migrate-aliyun-cdn-to-cf#%E8%83%8C%E6%99%AF" class="hash-link" aria-label="Direct link to 背景" title="Direct link to 背景" translate="no">​</a></h2>
<p><a href="https://wener.tech/" target="_blank" rel="noopener noreferrer">https://wener.tech</a> 为国内备案域名，<a href="https://wener.me/" target="_blank" rel="noopener noreferrer">https://wener.me</a> 为未备案域名，两个站点提供相同内容，数据都为 github pages</p>
<ul>
<li>wener.me <a href="https://github.com/wenerme/wener/tree/gh-pages" target="_blank" rel="noopener noreferrer">https://github.com/wenerme/wener/tree/gh-pages</a></li>
<li>wener.tech <a href="https://github.com/wenerme/wener.tech/tree/gh-pages" target="_blank" rel="noopener noreferrer">https://github.com/wenerme/wener.tech/tree/gh-pages</a></li>
<li><a href="https://charts.wener.tech/" target="_blank" rel="noopener noreferrer">https://charts.wener.tech</a> <a href="https://github.com/wenerme/charts" target="_blank" rel="noopener noreferrer">https://github.com/wenerme/charts</a></li>
</ul>
<p>因为一个 repo 只能有一个 CNAME，所以使用了两个。</p>
<p>在国内备案域名最省事的方式是放到国内云平台解析，迁出可能导致备案撤销，因此 wener.tech 使用了阿里云全站 CDN。</p>
<p>其中 charts.wener.tech 流量最大，因为加到一些 helm charts 的索引站里，且部署很多地方都用到了。
其中量最大的请求为 index.yaml ， 单个文件不小，请求次数高，每个月的全站 CDN 基本都花费在了 charts.wener.tech。</p>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="迁移">迁移<a href="https://wener.me/story/migrate-aliyun-cdn-to-cf#%E8%BF%81%E7%A7%BB" class="hash-link" aria-label="Direct link to 迁移" title="Direct link to 迁移" translate="no">​</a></h2>
<p>每个月为 charts.wener.tech 流量付费也不是办法，因此打算迁移。</p>
<p><strong>之前</strong></p>
<p>import Mermaid from '@theme/Mermaid';</p>
<p>&lt;Mermaid
chart={<code>graph TD     charts.wener.tech --CNAME--&gt; AliyunCDN     AliyunCDN --&gt; GitHubPages</code>}
/&gt;</p>
<p><strong>之后</strong></p>
<p>&lt;Mermaid
chart={<code>graph TD     charts.wener.tech -- CNAME --&gt; fb.wener.me     fb.wener.me --&gt; ArgoTunnel --Kubernetes--&gt; Nginx     Nginx --&gt; GitHubPages</code>}
/&gt;</p>
<p>因为 wener.tech 是在国内，因此只能使用 CNAME 方式到 cloudflare，cloudflare 支持为外部域名配置 ssl ，然后 fallback 到托管域名。</p>
<p>使用了 Nginx 作为反向代理缓存，尝试了 Varnish，但发现还是 Nginx 最为简单暴力。</p>
<p>GitHub Pages 有时候在国内也不一定能访问，加了本地缓存是最为保险的。</p>
<blockquote>
<p><strong>Note</strong> <a href="https://wener.me/notes/devops/web/proxy-cache" target="_blank" rel="noopener noreferrer">Why choose nginx as proxy cache</a>.</p>
</blockquote>
<p><strong>Cloudflare 缓存命中问题</strong></p>
<p>一开始缓存命中是很低的</p>
<p><img decoding="async" loading="lazy" src="https://wener.me/assets/images/2022-10-07-cf-stat-5b42e5f04a314944c410c5b8ae8f550d.png" width="1352" height="1012" class="img_IxRA"></p>
<p>因为 Cloudflare 基于 extension 缓存而不是 mime，且默认不缓存 html yaml 这种，在通过 Page Rule 添加全站缓存后，缓存命中率一下子就上去了。</p>
<p>就这样每个月节省了一小笔阿里云全站 CDN 流量费用。</p>
<p><strong>穿透到 Nginx 的流量</strong></p>
<p><img decoding="async" loading="lazy" src="https://wener.me/assets/images/2022-10-07-goaccess-stat-8384f4263c7f9df98596d6cf699b4e0d.png" width="1172" height="314" class="img_IxRA"></p>]]></content:encoded>
            <category>DevOps</category>
            <category>Aliyun</category>
        </item>
        <item>
            <title><![CDATA[CRM 实现经历]]></title>
            <link>https://wener.me/story/crm-trails</link>
            <guid>https://wener.me/story/crm-trails</guid>
            <pubDate>Wed, 17 Aug 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[| v   | date              |]]></description>
            <content:encoded><![CDATA[<table><thead><tr><th>v</th><th>date</th></tr></thead><tbody><tr><td>v1</td><td>2019.11 - 2021.01</td></tr><tr><td>v2</td><td>2020.01 - 2020.06</td></tr><tr><td>v3</td><td>2020.11 - 2022.07</td></tr><tr><td>v4</td><td>2022.06 - 2022.11</td></tr><tr><td>v5</td><td>2022.11 - 2023.03</td></tr><tr><td>v6</td><td>2023.03 -</td></tr></tbody></table>
<div class="theme-admonition theme-admonition-tip admonition_phik alert alert--success"><div class="admonitionHeading_zxjO"><span class="admonitionIcon_bSNx"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>Choose</div><div class="admonitionContent_R6P7"><ul>
<li>FE
<ul>
<li>React</li>
<li>Vite</li>
<li>tailwindcss</li>
<li>daisyui</li>
<li>headlessui</li>
<li>floatingui</li>
<li>GraphQL + urlq + CodeGen</li>
<li>react-hook-form</li>
<li>zustand</li>
</ul>
</li>
<li>BE
<ul>
<li>PostgreSQL</li>
<li>mikro-orm</li>
<li>type-graphql</li>
<li>NestJS</li>
<li>Hono</li>
<li>swc+esbuild -&gt; single bundle</li>
<li>ts-node+swc -&gt; faster dev</li>
</ul>
</li>
<li>不要选择
<ul>
<li><del>andt</del></li>
<li><del>trpc</del></li>
</ul>
</li>
</ul></div></div>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="v1">v1<a href="https://wener.me/story/crm-trails#v1" class="hash-link" aria-label="Direct link to v1" title="Direct link to v1" translate="no">​</a></h2>
<ul>
<li>后端 - Java - 2019.11 - 2021.01</li>
<li>前端 - NextJS v9 + Antd - 2019.11 - 2021.3
<ul>
<li>@reduxjs/toolkit</li>
</ul>
</li>
</ul>
<div class="theme-admonition theme-admonition-tip admonition_phik alert alert--success"><div class="admonitionHeading_zxjO"><span class="admonitionIcon_bSNx"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>总结</div><div class="admonitionContent_R6P7"><p>非常简单的尝试，很快就失败了，目标并不明确。
对 CRM 并不了解，过于盲目。
堆砌了一些基础前端组件。前端开发能力尚不成熟。</p></div></div>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="v2">v2<a href="https://wener.me/story/crm-trails#v2" class="hash-link" aria-label="Direct link to v2" title="Direct link to v2" translate="no">​</a></h2>
<ul>
<li>2020.01 - 2020.06</li>
<li>后端 - Node - nestjs
<ul>
<li>nestjs</li>
<li>objection - ORM</li>
<li>swagger</li>
</ul>
</li>
<li>前端 - Antd v4+NextJS v9
<ul>
<li>react-final-form</li>
<li>redux</li>
<li>tinacms</li>
</ul>
</li>
</ul>
<div class="theme-admonition theme-admonition-tip admonition_phik alert alert--success"><div class="admonitionHeading_zxjO"><span class="admonitionIcon_bSNx"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>总结</div><div class="admonitionContent_R6P7"><p><strong>后端</strong></p><p>后端尝试构建基于 Schema 的 CRM，但过于动态，过于灵活导致逻辑开发复杂。
对 NodeJS 后端开发并不足够了解，目标更像是一个 low-code 后端，但是支持 CRM 实体。
这个阶段对 CRM 逻辑有了一些了解。</p><ul>
<li>需要的是 先 CRM 再 low-code</li>
<li>核心逻辑是确定的才能更好开发业务 - 不然要做太多假设</li>
<li>NodeJS 容器真的很大</li>
<li>确定了要走 Schema 的方式 - 前端通过 Schema 做更多的事情</li>
</ul><p><strong>前端</strong></p><p>前端选择 Antd，发现阻力越来越大，实现定制化很难。
这时的前端开发能力还相对欠缺，对生态还不够了解。</p><ul>
<li>探索了前端想要达成的目标 - 窗口、多 Tab</li>
<li>不要选择 antd 这种过于全面无法定制的组件库</li>
</ul></div></div>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="v3">v3<a href="https://wener.me/story/crm-trails#v3" class="hash-link" aria-label="Direct link to v3" title="Direct link to v3" translate="no">​</a></h2>
<ul>
<li>后端 - Go - 2020.11 - 2022.07
<ul>
<li>gorm + restful - 前期</li>
<li>ent + gqlgen - 中期</li>
<li>gorm - 非核心 CRM 业务</li>
</ul>
</li>
<li>前端 - 2020.11 - 2022.1
<ul>
<li>NextJS</li>
<li>BlueprintJS - 前期</li>
<li>graphql-codegen</li>
<li>urql - GraphQL</li>
<li>TailwindCSS - 后期</li>
<li>zustand - 后期</li>
<li>react-hook-form</li>
</ul>
</li>
</ul>
<div class="theme-admonition theme-admonition-tip admonition_phik alert alert--success"><div class="admonitionHeading_zxjO"><span class="admonitionIcon_bSNx"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>总结</div><div class="admonitionContent_R6P7"><p>这个阶段是历时最长的，除了核心的 CRM 还完成了其他的一些附属模块。</p><p><strong>后端</strong></p><p>后端使用 Golang 开发上进行了一些探索，前期 gorm+restful 方式代码量大且重复。
之后选择 ent+gqlgen+自定义生成代码。</p><ul>
<li>GraphQL 对前端非常友好 - 但选择了先 GraphQL 而非先 GRPC 是比较失误的</li>
<li>基于 ent 实现自定义生成逻辑
<ul>
<li>生成了 schema - 但非标准 jsonschema - 且长期未从后端拉而是直接放在了前端
<ul>
<li>改动困难</li>
<li>自定义 schema 前期用起来爽，后期维护困难 - 记忆层面和熟悉度层面</li>
</ul>
</li>
<li>生成了 graphql 的 golang resolver 代码 - 通过解析 AST 的方式植入
<ul>
<li>生成逻辑过于依赖 ent，改动维护困难</li>
<li>生成 golang resolver 逻辑维护困难</li>
</ul>
</li>
</ul>
</li>
<li>ent 生成过多的东西 - 代码库冗余</li>
<li>ent 升级有风险
<ul>
<li>生成逻辑不兼容 - <strong>主要问题</strong> - 不敢随意升级 - 开发阻碍</li>
<li>自身不兼容</li>
</ul>
</li>
<li>确定了要先 GRPC 的方式开发</li>
<li>基于多 DB 后端的多租户逻辑不好维护</li>
<li>优先设计的 On-Prime 模式，未考虑 SaaS，后期尝到了苦头</li>
</ul><p><strong>前端</strong></p><p>前期基于 Blueprint 快速实现大多功能，但还是因为经验不足，很多东西实现有缺陷。但实现了初期原型，达到了想要的结果。</p><ul>
<li>Blueprint 提供 CSS 直接使用，非常方便 - 侵入性远远小于 Antd</li>
<li>Blueprint 升级需要改动所有的 CSS 前缀 - ⚠️</li>
<li>后期选择 Tailwind 开发</li>
<li>组件传递、自定义经验欠缺 - 做了很多不好维护的扩展</li>
<li>基础 Schema 不标准 - 很多东西基于不标准的 schema 开发后结果是往更不好的方向发展</li>
<li>优先考虑了用户使用而非管理使用，导致所有管理维护开通都需要人为 - 不可持续</li>
<li>期望中的模块化一直未实现</li>
</ul></div></div>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="v4">v4<a href="https://wener.me/story/crm-trails#v4" class="hash-link" aria-label="Direct link to v4" title="Direct link to v4" translate="no">​</a></h2>
<ul>
<li>2022.06 -&gt; 2022.11</li>
<li>后端
<ul>
<li>确定了要优先 GRPC，且使用标准 Schema。
<ul>
<li>因此将 protobuf 的定义作为 Single-Source—Of—Truth Schema。</li>
<li>分离接口 Schema 和 DB - 之前是 ent schema -&gt; graphql schema</li>
</ul>
</li>
<li>虽然选择 GRPC 但也要直接能给前端访问
<ul>
<li>选择了 connect 协议</li>
<li>实现了 connect-gateway
<ul>
<li>实现了基于 Redis 服务发现</li>
<li>实现了基于 grpc reflection 自动暴露服务</li>
</ul>
</li>
</ul>
</li>
<li>DB 确定不要通过 ORM 自动维护而是手动维护
<ul>
<li>实现基于 PG RLS 的多租户逻辑而非多 DB 模式</li>
<li>实现 CRM 时 DB Schema 的重要性等同接口</li>
<li>业务沉淀为数据库模型</li>
<li>也因此分离 API 模型和数据库模型</li>
</ul>
</li>
<li>Golang -&gt; NodeJS
<ul>
<li>初期尝试用 Golang 实现</li>
<li>核心问题 ⚠️
<ul>
<li>开发一段 前端再回到 Golang 会 <strong>痛不欲生</strong> - 上下文切换、开发思路切换</li>
<li>Golang 生成代码只能静态生成，NodeJS 可以动态生成
<ul>
<li>NodeJS 更加灵活 - PoC 阶段更有优势</li>
</ul>
</li>
<li>使用 NodeJS 可以和前端在同一个仓库 - 核心逻辑开发维护更容易</li>
</ul>
</li>
</ul>
</li>
<li>NodeJS
<ul>
<li>fastify+jsonschema+sequelize</li>
<li>通过 grpc 生成 descriptor, definitoon</li>
<li>启动后通过 descriptor 生成 jsonschema</li>
<li>通过 jsonschema+definitoon 生成 grpc-service - nice-grpc</li>
<li>实现标准的 CRUD 语义</li>
<li>fastify POST -&gt; grpc 实现 - 类似 connect 语义</li>
<li>grpc-server -&gt; grpc 实现</li>
<li>未来: web -&gt; connect-gateway -&gt; grpc-server -&gt; grpc 实现</li>
</ul>
</li>
</ul>
</li>
<li>前端
<ul>
<li>daisyui+tailwindcss - 放弃 <del>Blueprint</del></li>
<li>不再直接选择成熟的 UI 框架，而是选择基于 CSS 的样式库</li>
<li>daisyui 提供了一套命名 class 的方式 - 有 Theme</li>
<li>确定 管理功能优先于用户功能</li>
<li>确定 功能能被看到才算开发完成</li>
<li>模块化
<ul>
<li>仍在尝试中 - 借鉴其他实现</li>
<li>提供开放的 OSS 存储</li>
<li>目前明确方向
<ul>
<li>分离 os 逻辑和核心 route 逻辑 - 揉在一起困难</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="v5">v5<a href="https://wener.me/story/crm-trails#v5" class="hash-link" aria-label="Direct link to v5" title="Direct link to v5" translate="no">​</a></h2>
<ul>
<li>2022.11 -&gt; 2023.03</li>
<li>方式的调整
<ul>
<li>不只是独立于业务单纯的思考怎么做 CRM - 没有任何意义/产出的东西不可用</li>
<li>业务层面将以前的思路以 DB Schema 的方式沉淀</li>
<li>贴合现实做一些实际的事情</li>
</ul>
</li>
<li>后端
<ul>
<li>RPC over NATS
<ul>
<li>简单易用</li>
<li>能实现多租户</li>
</ul>
</li>
<li>自定义 RPC
<ul>
<li>弱 Schema - 一个人维护不过来</li>
</ul>
</li>
<li>重新定义分层
<ul>
<li>DB -&gt; Entity -&gt; EntityService -&gt; RemoteService -&gt; Controller, tRPC, GraphQL</li>
<li>层次更多，但是更清晰</li>
<li>改动相对独立，互不影响</li>
</ul>
</li>
<li>依然全面的 NodeJS/TS/NextJS/NestJS</li>
<li>尝试更加轻量的服务 - HonoJS</li>
<li>尝试更加轻量的前端 - Vite
<ul>
<li>不能 SSR/SSG/Server Action</li>
<li>添加多个页面相对麻烦</li>
</ul>
</li>
<li>尝试 Typescript 生成 zod/typebox - Single-Source-Of-Truth</li>
</ul>
</li>
<li>前端
<ul>
<li>大部分页面直接使用了 v4 的内容 - 逻辑结构上有一定调整</li>
<li>证明 daisyui+tailwindcss 的移植性的确很好</li>
<li>尝试引入了 shadcn</li>
<li>核心功能能够做到模块化</li>
<li>尝试重新恢复多窗口</li>
</ul>
</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="v6">v6<a href="https://wener.me/story/crm-trails#v6" class="hash-link" aria-label="Direct link to v6" title="Direct link to v6" translate="no">​</a></h2>
<ul>
<li>2023.3 -&gt;</li>
<li>基于 v5 大致内容不变的情况下调整以提升开发效率</li>
<li>主要调整内容
<ul>
<li>API 调整 - trpc -&gt; GQL</li>
<li>结构选型调整 - 轻量化, 开发效率优先</li>
</ul>
</li>
<li>调整内容
<ul>
<li><del>Nats RPC + trpc</del> -&gt; GraphQL
<ul>
<li>trpc 内容多了过后类型推导太慢</li>
<li>GraphQL 直接对客户端保留
<ul>
<li>避免要求通过 RPC+trpc 对客户端</li>
<li>减少开发量</li>
</ul>
</li>
<li>RPC 保留为对服务端</li>
</ul>
</li>
<li>NextJS -&gt; Vite - 纯静态前端 console
<ul>
<li>开发反映要快得多</li>
<li>现在的 NextJS 不适合做后台，不适合做太过动态的项目</li>
<li>Vite HMR 体验要好得多</li>
</ul>
</li>
<li><del>fastify + NestJS Controller</del> -&gt; Hono
<ul>
<li>保留 NestJS 作为 IoC 容器</li>
<li>Hono 简单容易维护</li>
<li>缺点是 Hono OpenAPI 使用 zod-openapi
<ul>
<li>没 NestJS Controller 那么好用</li>
<li>但 可以 share schema 给前端 - 不过使用了 GraphQL Codegen 一般也不需要</li>
</ul>
</li>
</ul>
</li>
<li>GraphQL 使用 type-graphql+yoga+urql</li>
<li>大量引入 mixin 来抽象业务逻辑
<ul>
<li>ORM 层 <a href="https://github.com/wenerme/wode/blob/main/packages/nestjs/src/entity/mixins/index.ts" target="_blank" rel="noopener noreferrer">https://github.com/wenerme/wode/blob/main/packages/nestjs/src/entity/mixins/index.ts</a></li>
<li>GQL 层 <a href="https://github.com/wenerme/wode/blob/main/packages/nestjs/src/type-graphql/mixins/index.ts" target="_blank" rel="noopener noreferrer">https://github.com/wenerme/wode/blob/main/packages/nestjs/src/type-graphql/mixins/index.ts</a></li>
</ul>
</li>
<li>Golang -&gt; Bun
<ul>
<li>将最后依赖 Golang 的 企业微信会话内容存档转为 Bun</li>
<li>bun:ffi 能很好的和 c interop
<ul>
<li><a href="https://github.com/wenerme/wode/blob/main/packages/client/src/wecom/archive/bun/WeWorkFinanceClient.ts" target="_blank" rel="noopener noreferrer">WeWorkFinanceClient.ts</a></li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>领域开发
<ul>
<li>Follow 固定的 schema 模式 <a href="https://wener.me/notes/dev/design/schema" target="_blank" rel="noopener noreferrer">https://wener.me/notes/dev/design/schema</a></li>
<li>逐渐固化大的业务框架 <a href="https://wener.me/notes/dev/design/erp" target="_blank" rel="noopener noreferrer">https://wener.me/notes/dev/design/erp</a></li>
<li>逐步引入一些公共的业务逻辑</li>
</ul>
</li>
<li>结果
<ul>
<li>目前差不多的前后端部署了 3+ 套</li>
<li>固化下来差不多的前后端框架
<ul>
<li>前端 <a href="https://github.com/wenerme/wode/tree/main/packages/console" target="_blank" rel="noopener noreferrer">https://github.com/wenerme/wode/tree/main/packages/console</a></li>
<li>后端 <a href="https://github.com/wenerme/wode/tree/main/packages/nestjs" target="_blank" rel="noopener noreferrer">https://github.com/wenerme/wode/tree/main/packages/nestjs</a></li>
</ul>
</li>
<li>外部集成 <a href="https://github.com/wenerme/wode/tree/main/packages/client" target="_blank" rel="noopener noreferrer">https://github.com/wenerme/wode/tree/main/packages/client</a></li>
</ul>
</li>
<li>接下来
<ul>
<li>抽取更多公共业务逻辑</li>
<li>引入更多业务域</li>
<li>文件处理 - 类似网盘</li>
<li>更多外部服务集成</li>
</ul>
</li>
</ul>]]></content:encoded>
            <category>思考</category>
            <category>CRM</category>
        </item>
        <item>
            <title><![CDATA[大师之路]]></title>
            <link>https://wener.me/story/raod-to-master</link>
            <guid>https://wener.me/story/raod-to-master</guid>
            <pubDate>Sat, 11 Jun 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[幼儿园小朋友的画和自由派名画师的话没什么区别，小学生作文和名作家的文章也没什么区别。它们在形式和内容上都是差不多的。]]></description>
            <content:encoded><![CDATA[<p>幼儿园小朋友的画和自由派名画师的话没什么区别，小学生作文和名作家的文章也没什么区别。它们在形式和内容上都是差不多的。</p>
<p>不同的是，后者会倾注更多的时间、精力去完成作品。而要完成这样的作品又必须要有足够的阅历，知识经验积累，时间的沉淀。</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[2022上海封城日记]]></title>
            <link>https://wener.me/story/2022-shanghai-lockdown-memo</link>
            <guid>https://wener.me/story/2022-shanghai-lockdown-memo</guid>
            <pubDate>Tue, 24 May 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[封控自 2022-03-16]]></description>
            <content:encoded><![CDATA[<p>封控自 2022-03-16
解封于 2022-06-01
共封控 77 天</p>
<ul>
<li>生活
<ul>
<li>封控期间重看了所有 <a href="https://wener.me/notes/culture/movie/movie-awesome" target="_blank" rel="noopener noreferrer">MCU 电影</a></li>
<li>看了部分 MCU TV - 惩罚者、夜魔侠</li>
<li>通关 路易吉鬼屋、马里奥与疯兔</li>
<li>天空剑 - 15h</li>
<li>抢菜
<ul>
<li>因为生活是夜猫子作息，大多能抢到 6 点那波 叮咚。</li>
<li>通过设置 iOS 自动点，省力
<ul>
<li>设置-通用-辅助功能-切换控制</li>
<li>先录入要点的位置，然后连按 3 次 电源键 启用</li>
<li>类似 宏</li>
</ul>
</li>
</ul>
</li>
<li>咖啡戒断
<ul>
<li>尝试不喝咖啡，3天后发生戒断反应，欲仙欲死</li>
</ul>
</li>
</ul>
</li>
<li>代码
<ul>
<li>种子索引项目 <a href="https://github.com/wenerme/torrenti" target="_blank" rel="noopener noreferrer">wenerme/torrenti</a>
<ul>
<li>衍生代理项目 <a href="https://github.com/wenerme/proxc" target="_blank" rel="noopener noreferrer">wenerme/proxc</a></li>
</ul>
</li>
<li>企业官网 - Strapi+NextJS
<ul>
<li>衍生 <a href="https://github.com/wenerme/wode" target="_blank" rel="noopener noreferrer">Tiptap 编辑器</a> - 仿 Google Doc</li>
</ul>
</li>
<li>Web3 DAO 前端项目
<ul>
<li><a href="https://wener.me/notes/blockchain/blockchain-awesome" target="_blank" rel="noopener noreferrer">学习 Web3 相关</a></li>
<li>玩币 - 损失 500+</li>
</ul>
</li>
</ul>
</li>
</ul>]]></content:encoded>
            <category>生活</category>
        </item>
        <item>
            <title><![CDATA[企业建站的基本前提考量]]></title>
            <link>https://wener.me/story/prerequires-for-offcial-site</link>
            <guid>https://wener.me/story/prerequires-for-offcial-site</guid>
            <pubDate>Tue, 24 May 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[基本功能]]></description>
            <content:encoded><![CDATA[<p><strong>基本功能</strong></p>
<ol>
<li>文章编辑</li>
<li>用户联系表单提交</li>
<li>静态 - 利于 SEO</li>
</ol>
<p><strong>考虑</strong></p>
<ol>
<li>是否需要自有域名
<ul>
<li>没有自有域名可用平台提供的</li>
<li>域名是必须的</li>
</ul>
</li>
<li>是否需要 HTTPS 证书
<ul>
<li>可提供免费证书 - 涉及运维</li>
<li>可买 - 云平台购买，需要定期更新维护</li>
</ul>
</li>
<li>是否需要备案
<ul>
<li>未备案微信无法直接打开</li>
<li>最简单的备案需要挂云服务商 - 服务器</li>
<li>使用平台提供的域名可避免备案</li>
</ul>
</li>
<li>托管或自行提供服务器
<ul>
<li>托管 - 平台提供服务器资源 - 使用平台带宽</li>
<li>自行提供服务器 - 需要维护、定时付费
<ul>
<li>一般免费 1mb 带宽（图片多会非常慢）</li>
</ul>
</li>
</ul>
</li>
</ol>]]></content:encoded>
            <category>指南</category>
        </item>
        <item>
            <title><![CDATA[Love the World]]></title>
            <link>https://wener.me/story/love-the-world</link>
            <guid>https://wener.me/story/love-the-world</guid>
            <pubDate>Thu, 19 May 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[深切的爱着这个美好的世界，因此更深切的恨着让这个世界肮脏的东西。]]></description>
            <content:encoded><![CDATA[<p>深切的爱着这个美好的世界，因此更深切的恨着让这个世界肮脏的东西。</p>
<p>服从性实验，不停的挑战你的底线，或许拿准了这群人骨子里都是想着“顺着它们就好了”，这个底线越来越低。</p>
<p>谁都可以指着你的脸来教育你，只要它认为它是“对的”，规则都在嘴上，权利在无限放大，没有人觉得这有什么不对。
或许有人觉得不对，可是他们不被允许“发声”，我也不被允许“发声”。</p>
<p>简单的随声附和还不足以满足它们的“指导”欲望，它们希望你觉得意识到自己的错误，虽然你知道事实上并不是一个错误，但还是错了。
错在身在这个环境。</p>
<p>长辈觉得接受它们的“教育”、“指导”没有失去什么，但这本身就在一直失去，并且失去的越来越多。</p>
<p>当作为一个底层人/普通人，早已认识到自己无法改变任何事情，无法获取到更多，那么无论做或者不做什么都是在失去，失去自己，失去亲人，失去孩子的未来，就没那么想要继续去附和。
早一点解脱也算是一种逃避，需要勇气。放弃了对任何人负责，一点点的失去耐心，一点点的积累仇恨。</p>
<p>Tring to look the bright side, but in the dark room, there is no light.</p>
<details class="details_QKu6 alert alert--info details_IyD5" data-collapsed="true"><summary>起因：</summary><div><div class="collapsibleContent_iPxL">
<!-- -->
<p>封控期，我妈带着我孩子去花园玩，可是皮球不小心掉出了围墙外，我妈想在围墙让别人帮忙捡球，扳开了一点围墙上的铁丝。
<br>
小区不知道那里来的人，敲上门来教育，教育了一会，我妈也在应和，可是我不耐烦了，我说我们知道错了，不会了，然后关门了。
对方并不罢休，扬言要报警，我妈只好开门继续接受教育，对方也更起劲了。</p>
</div></div></details>]]></content:encoded>
            <category>思考</category>
            <category>生活</category>
        </item>
        <item>
            <title><![CDATA[数据同步模式]]></title>
            <link>https://wener.me/story/etl-pattens</link>
            <guid>https://wener.me/story/etl-pattens</guid>
            <pubDate>Wed, 26 Jan 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[A 同步到 B]]></description>
            <content:encoded><![CDATA[<blockquote>
<p>A 同步到 B</p>
<p>DBA -&gt; A -&gt; B -&gt; DBB</p>
<p>Extract -&gt; Load</p>
<p>Extract -Transform-&gt; Load</p>
</blockquote>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="场景">场景<a href="https://wener.me/story/etl-pattens#%E5%9C%BA%E6%99%AF" class="hash-link" aria-label="Direct link to 场景" title="Direct link to 场景" translate="no">​</a></h2>
<p><strong>A,B,DBA,DBB 互通性</strong></p>








































<table><thead><tr><th>from\to</th><th>DBA</th><th>A</th><th>B</th><th>DBB</th></tr></thead><tbody><tr><td>DBA</td><td></td><td></td><td></td><td>🟡</td></tr><tr><td>A</td><td></td><td></td><td>🟢</td><td>🟡</td></tr><tr><td>B</td><td>🟠</td><td>🟢</td><td></td><td></td></tr><tr><td>DBB</td><td>🟠</td><td></td><td></td><td></td></tr></tbody></table>
<ul>
<li>🟢 - 有可能 - A &lt;-&gt; B</li>
<li>🟡 - 也许可能 - 提供公有云数据库作为同步目标</li>
<li>🟠 - 不太可能
<ul>
<li>如果是内部服务是可能的</li>
</ul>
</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="模式">模式<a href="https://wener.me/story/etl-pattens#%E6%A8%A1%E5%BC%8F" class="hash-link" aria-label="Direct link to 模式" title="Direct link to 模式" translate="no">​</a></h2>
<ul>
<li>Push - 推 - A -&gt; B</li>
<li>Pull - 拉 - A &lt;- B</li>
<li>Reactive - A &lt;-&gt; B</li>
<li>第三方 - A -&gt; X -&gt; B</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="方式">方式<a href="https://wener.me/story/etl-pattens#%E6%96%B9%E5%BC%8F" class="hash-link" aria-label="Direct link to 方式" title="Direct link to 方式" translate="no">​</a></h2>
<ul>
<li>全量</li>
<li>增量
<ul>
<li>基于增量游标</li>
<li>基于 CDC 事件</li>
</ul>
</li>
<li>Schema+Data</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="实现方案">实现方案<a href="https://wener.me/story/etl-pattens#%E5%AE%9E%E7%8E%B0%E6%96%B9%E6%A1%88" class="hash-link" aria-label="Direct link to 实现方案" title="Direct link to 实现方案" translate="no">​</a></h2>
<ul>
<li>DBA -&gt; A -&gt; B -&gt; DBB
<ul>
<li>B 暴露 API，A 请求 B - Push/Hook</li>
<li>例如: WebHook</li>
</ul>
</li>
<li>DBA -&gt; A &lt;- B -&gt; DBB
<ul>
<li>A 暴露 API，B 请求 A - Pull/Query</li>
<li>例如: WebHook</li>
</ul>
</li>
<li>DBA -&gt; A -&gt; DBB
<ul>
<li>例如: 配置同步目标给 A, A 直接写入</li>
</ul>
</li>
<li>DBA -&gt; B -&gt; DBB
<ul>
<li>例如: RLS 暴露给 B 访问</li>
</ul>
</li>
<li>DBA &lt;-&gt; DBB
<ul>
<li>例如: DB Link, Replication</li>
</ul>
</li>
<li>DBA -&gt; A -&gt; Queue -&gt; B -&gt; DBB
<ul>
<li>A,B 通过 Queue 交互 - A,B 都没有暴露接口</li>
<li>被动</li>
<li>Queue 定位
<ul>
<li>数据存储 - Kafka</li>
<li>信息传输 - Redis, Nats - 例如 RPC over Redis Stream</li>
</ul>
</li>
</ul>
</li>
<li>DBA -&gt; X -&gt; DBB</li>
<li>DBA -&gt; A -&gt; X -&gt; B -&gt; DBB
<ul>
<li>借助第三方服务 - 例如: AirByte, PgLoader</li>
<li>X 服务作为 中间服务 与 A,B 交互
<ul>
<li>替代 Queue 角色</li>
<li>主动</li>
</ul>
</li>
<li>X 具有适配能力</li>
</ul>
</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="transform-考虑">Transform 考虑<a href="https://wener.me/story/etl-pattens#transform-%E8%80%83%E8%99%91" class="hash-link" aria-label="Direct link to Transform 考虑" title="Direct link to Transform 考虑" translate="no">​</a></h2>
<ul>
<li>数据 Schema 适配</li>
<li>数据类型适配</li>
<li>过滤、转换、计算</li>
<li>通常包含逻辑
<ul>
<li>逻辑 ~= Script
<ul>
<li>Lua, Python, JS</li>
</ul>
</li>
<li>gRPC - 外部调用</li>
</ul>
</li>
<li>Transform 的复杂度决定了是 ETL 还是流处理
<ul>
<li>ETL - 简单适配、调整、元数据</li>
<li>流处理 - 包含状态、依赖前后消息/Window、包含聚合</li>
</ul>
</li>
<li>例如: Flink, SparkSQL, Storm、KafkaSQL</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="数据编码-考虑">数据编码 考虑<a href="https://wener.me/story/etl-pattens#%E6%95%B0%E6%8D%AE%E7%BC%96%E7%A0%81-%E8%80%83%E8%99%91" class="hash-link" aria-label="Direct link to 数据编码 考虑" title="Direct link to 数据编码 考虑" translate="no">​</a></h2>
<ul>
<li>文本编码 - CSV,JSON,JSONL - 基础，丢失类型信息</li>
<li>二进制编码 - Avro, Parqute, Protobuf</li>
<li>CBOR - Concise Binary Object Representation
<ul>
<li>对比 JSON 能更好的保留类型信息</li>
</ul>
</li>
<li>Schema+Data - 数据更紧凑</li>
</ul>]]></content:encoded>
            <category>Design</category>
            <category>Architecture</category>
        </item>
        <item>
            <title><![CDATA[组建你自己的 NAS 服务器]]></title>
            <link>https://wener.me/story/build-your-own-nas</link>
            <guid>https://wener.me/story/build-your-own-nas</guid>
            <pubDate>Fri, 14 Jan 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[组建 NAS 需要考虑的因素和建议。]]></description>
            <content:encoded><![CDATA[<p>组建 NAS 需要考虑的因素和建议。</p>
<div class="theme-admonition theme-admonition-tip admonition_phik alert alert--success"><div class="admonitionHeading_zxjO"><span class="admonitionIcon_bSNx"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>核心因素</div><div class="admonitionContent_R6P7"><ul>
<li>硬件 - 直接影响功耗
<ul>
<li>硬盘 - 基本不变</li>
<li>主机 - 变化很大</li>
</ul>
</li>
<li>软件 - 基本不变</li>
</ul></div></div>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="物料">物料<a href="https://wener.me/story/build-your-own-nas#%E7%89%A9%E6%96%99" class="hash-link" aria-label="Direct link to 物料" title="Direct link to 物料" translate="no">​</a></h2>
<div class="theme-admonition theme-admonition-tip admonition_phik alert alert--success"><div class="admonitionHeading_zxjO"><span class="admonitionIcon_bSNx"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>目标</div><div class="admonitionContent_R6P7"><ul>
<li>价格便宜</li>
<li>容量高</li>
</ul></div></div>
<ul>
<li>HP MicroServer 全新微塔 - 参考价格: ¥4000 , 功耗 60W
<ul>
<li>HP MicroServer Gen 10
<ul>
<li>全新 AMD x3216 8G ¥2300</li>
<li>最多 4 个硬盘</li>
</ul>
</li>
<li>SAS 6T×4 - ¥1600</li>
<li>SAS 卡 - ¥100</li>
</ul>
</li>
<li>二手塔式 - 参考价格: ¥4700 , 功耗 120W
<ul>
<li>DDR4 服务器 - ~¥2000
<ul>
<li>1U 4×3.5 寸盘位</li>
<li>2U 8×3.5 寸盘位</li>
</ul>
</li>
<li>CPU XeonE5v4×2 ~¥1000 - 32 核 64 线程</li>
<li>内存 DDR4 ECC REG 16G×4 - 64G ~¥1000</li>
<li>SAS 6T×4 - ¥1600</li>
<li>SAS 卡 - ¥100</li>
</ul>
</li>
<li>星际蜗牛 J1900 - 参考价格: ¥2500 , 功耗 30W
<ul>
<li>星际蜗牛 DDR3 J1900 8G ~ ¥800</li>
<li>SAS 6T×4 - ¥1600</li>
<li>SAS 卡 - ¥100</li>
<li>非常入门款，没什么上升空间，没什么折腾的意义</li>
</ul>
</li>
</ul>
<div class="theme-admonition theme-admonition-tip admonition_phik alert alert--success"><div class="admonitionHeading_zxjO"><span class="admonitionIcon_bSNx"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>容量考虑</div><div class="admonitionContent_R6P7"><p>现在 2022 年，手机平台存储上 T 的已经越来越多，笔记本 SSD 都有几 T 的了，台式机更不用说，几 T 的存储很正常。</p><ul>
<li>单盘 &lt; 4T 会比较浪费盘位</li>
<li>单盘 &gt; 10T 恢复非常慢</li>
<li>建议至少 6T×4 - RAID5 可用大约 15T</li>
</ul></div></div>
<h3 class="anchor anchorWithStickyNavbar_EG6R" id="部件参考价格">部件参考价格<a href="https://wener.me/story/build-your-own-nas#%E9%83%A8%E4%BB%B6%E5%8F%82%E8%80%83%E4%BB%B7%E6%A0%BC" class="hash-link" aria-label="Direct link to 部件参考价格" title="Direct link to 部件参考价格" translate="no">​</a></h3>













































































<table><thead><tr><th>部件</th><th>规格</th><th>¥</th><th>taobao</th></tr></thead><tbody><tr><td>SAS</td><td>6T 3.5 寸 7.2K</td><td>¥400</td><td><a href="https://item.taobao.com/item.htm?id=560836442610" target="_blank" rel="noopener noreferrer">https://item.taobao.com/item.htm?id=560836442610</a></td></tr><tr><td>SAS</td><td>1.2T 2.5 寸 10K</td><td>¥160</td><td><a href="https://item.taobao.com/item.htm?id=547264783830" target="_blank" rel="noopener noreferrer">https://item.taobao.com/item.htm?id=547264783830</a></td></tr><tr><td>SAS 直通卡</td><td></td><td>¥100</td><td><a href="https://item.taobao.com/item.htm?id=521090584542" target="_blank" rel="noopener noreferrer">https://item.taobao.com/item.htm?id=521090584542</a></td></tr><tr><td>SATA</td><td>14TB 3.5 寸 7.2K</td><td>¥1200</td><td><a href="https://item.taobao.com/item.htm?id=599544680031" target="_blank" rel="noopener noreferrer">https://item.taobao.com/item.htm?id=599544680031</a></td></tr><tr><td>内存 DDR3 ECC REG</td><td>16G</td><td>~¥120</td><td><a href="https://item.taobao.com/item.htm?id=13978621463" target="_blank" rel="noopener noreferrer">https://item.taobao.com/item.htm?id=13978621463</a></td></tr><tr><td>内存 DDR4 ECC REG</td><td>16G</td><td>~¥250</td><td><a href="https://item.taobao.com/item.htm?id=567468034444" target="_blank" rel="noopener noreferrer">https://item.taobao.com/item.htm?id=567468034444</a></td></tr><tr><td>内存 DDR4 ECC REG</td><td>32G</td><td>~¥600</td><td><a href="https://item.taobao.com/item.htm?id=567468034444" target="_blank" rel="noopener noreferrer">https://item.taobao.com/item.htm?id=567468034444</a></td></tr><tr><td>内存 DDR4 ECC NOREG</td><td>16G</td><td>~¥500</td><td></td></tr><tr><td>内存 DDR4 ECC NOREG</td><td>32G</td><td>~¥1000</td><td></td></tr><tr><td>CPU <a href="https://ark.intel.com/content/www/us/en/ark/products/91766/intel-xeon-processor-e52683-v4-40m-cache-2-10-ghz.html" target="_blank" rel="noopener noreferrer">E5-2683V4</a></td><td>2.1G 16C32T 120W</td><td>~¥500</td><td><a href="https://item.taobao.com/item.htm?id=597514302182" target="_blank" rel="noopener noreferrer">https://item.taobao.com/item.htm?id=597514302182</a></td></tr><tr><td>CPU <a href="https://www.intel.cn/content/www/cn/zh/products/sku/88170/intel-xeon-processor-e31235l-v5-8m-cache-2-00-ghz/specifications.html" target="_blank" rel="noopener noreferrer">E3-1235Lv5</a></td><td></td><td></td><td></td></tr></tbody></table>
<div class="theme-admonition theme-admonition-caution admonition_phik alert alert--warning"><div class="admonitionHeading_zxjO"><span class="admonitionIcon_bSNx"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>PC 和 企业级服务器 过渡产品</div><div class="admonitionContent_R6P7"><ul>
<li>服务器市场有比 PC 强，但比服务器弱的一个产品线，通常用于个人工作室之类的场景。</li>
<li>特点
<ul>
<li>服务器体积小</li>
<li>CPU 功耗低 - 25W</li>
<li>内存使用 ECC NOREG
<ul>
<li>二手市场少，价格偏高</li>
<li>需要 CPU 支持</li>
<li>需要主板支持</li>
</ul>
</li>
</ul>
</li>
</ul></div></div>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="主机选择">主机选择<a href="https://wener.me/story/build-your-own-nas#%E4%B8%BB%E6%9C%BA%E9%80%89%E6%8B%A9" class="hash-link" aria-label="Direct link to 主机选择" title="Direct link to 主机选择" translate="no">​</a></h2>
<ul>
<li>机型选择
<ul>
<li>微塔 - 噪音小，功耗一般 ~60w
<ul>
<li>二手少 - 大多买新的，价格高</li>
<li>体积小</li>
<li>一般主板不支持 ECC REG 内存</li>
</ul>
</li>
<li>服务器 - 噪音大，功耗一般 ~120w
<ul>
<li>二手多 - 便宜</li>
<li>体积大 - 一般 1U</li>
<li>扩展能力强 - 通常支持 一个半高+一个全高 PCI - 因此可以配 GPU</li>
<li>如果没有书房、库房或隐瞒角落不推荐</li>
<li>部分服务器噪音也不是特别大</li>
</ul>
</li>
</ul>
</li>
<li>平台选择
<ul>
<li>通常二手都是落后好几个世代，但建议至少选择 DDR4</li>
<li>DDR3 价格是 DDR4 的一半 - 如果预算有限，那也是可以的
<ul>
<li>选择 DDR3 CPU 也会便宜一半以上 - 是再上一个世代的平台</li>
</ul>
</li>
</ul>
</li>
<li>配置选择
<ul>
<li>全新整套主机 - 包含 CPU+内存
<ul>
<li>性价比不高，但省事</li>
</ul>
</li>
<li>二手服务器
<ul>
<li>性价比最高，省事</li>
<li>可选性不多</li>
</ul>
</li>
<li>自己配主机 - 主板+机箱+电源+CPU+内存
<ul>
<li>事情最多，如果不是喜欢搞这个，建议不要</li>
</ul>
</li>
</ul>
</li>
<li>服务器选择
<ul>
<li>架式
<ul>
<li>二手市场最多，占地面积大</li>
<li>1U
<ul>
<li>8 个 2.5 寸盘位</li>
<li>4 个 3.5 寸盘位</li>
</ul>
</li>
</ul>
</li>
<li>塔式
<ul>
<li>二手市场不多，占地面积大</li>
<li>和普通台式电脑差不多</li>
<li>服务器主板</li>
</ul>
</li>
<li>刀片
<ul>
<li>二手市场很少</li>
</ul>
</li>
<li>模块
<ul>
<li>二手市场不太多，占地面积很小</li>
<li>魔改后的非常安静</li>
</ul>
</li>
</ul>
</li>
<li>成品 NAS 服务器
<ul>
<li>群晖 - 不再这里的讨论范围内</li>
</ul>
</li>
</ul>
<h3 class="anchor anchorWithStickyNavbar_EG6R" id="配置目标">配置目标<a href="https://wener.me/story/build-your-own-nas#%E9%85%8D%E7%BD%AE%E7%9B%AE%E6%A0%87" class="hash-link" aria-label="Direct link to 配置目标" title="Direct link to 配置目标" translate="no">​</a></h3>

































<table><thead><tr><th>-</th><th>机型</th><th>CPU</th><th>内存</th><th>硬盘</th></tr></thead><tbody><tr><td>最低</td><td>微塔,ITX</td><td>4 核</td><td>8G</td><td>6T×4 raidz1</td></tr><tr><td>推荐</td><td>-</td><td>4 核+</td><td>16G+</td><td></td></tr><tr><td>更高</td><td>服务器</td><td>32 核心+</td><td>128G+</td><td></td></tr></tbody></table>
<blockquote>
<p>通常配置个最低，后期能够调整组件即可</p>
</blockquote>
<h3 class="anchor anchorWithStickyNavbar_EG6R" id="全新整套主机">全新整套主机<a href="https://wener.me/story/build-your-own-nas#%E5%85%A8%E6%96%B0%E6%95%B4%E5%A5%97%E4%B8%BB%E6%9C%BA" class="hash-link" aria-label="Direct link to 全新整套主机" title="Direct link to 全新整套主机" translate="no">​</a></h3>
<ul>
<li><a href="https://www.hpe.com/psnow/doc/a00008701enw" target="_blank" rel="noopener noreferrer">hp microserver gen 10</a>
<ul>
<li>AMD x3216 8G ¥2300</li>
<li><a href="https://item.taobao.com/item.htm?id=564308249868#detail" target="_blank" rel="noopener noreferrer">https://item.taobao.com/item.htm?id=564308249868#detail</a></li>
</ul>
</li>
<li>hp microserver gen 10 plus
<ul>
<li><strong>非常小巧</strong></li>
<li>需要海淘</li>
<li>¥5000</li>
</ul>
</li>
<li>mineNAS 至强 微塔
<ul>
<li>Core 系列 CPU <a href="https://item.taobao.com/item.htm?id=546747046542" target="_blank" rel="noopener noreferrer">https://item.taobao.com/item.htm?id=546747046542</a>
<ul>
<li>¥3600</li>
</ul>
</li>
<li>Xeon 系列 CPU <a href="https://item.taobao.com/item.htm?id=523041473305" target="_blank" rel="noopener noreferrer">https://item.taobao.com/item.htm?id=523041473305</a>
<ul>
<li>¥7500+</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 class="anchor anchorWithStickyNavbar_EG6R" id="二手服务器">二手服务器<a href="https://wener.me/story/build-your-own-nas#%E4%BA%8C%E6%89%8B%E6%9C%8D%E5%8A%A1%E5%99%A8" class="hash-link" aria-label="Direct link to 二手服务器" title="Direct link to 二手服务器" translate="no">​</a></h3>
<ul>
<li>架式
<ul>
<li>HP DL160G9 <a href="https://item.taobao.com/item.htm?id=564082577179" target="_blank" rel="noopener noreferrer">https://item.taobao.com/item.htm?id=564082577179</a>
<ul>
<li>准系统 ¥750</li>
<li>E5-4650V3*2 24 核 48 线 + 64G + Intel 800G SSD ¥3290</li>
<li>8×2.5 寸盘位</li>
</ul>
</li>
<li>DELL R430 X99 <a href="https://item.taobao.com/item.htm?id=567440742661" target="_blank" rel="noopener noreferrer">https://item.taobao.com/item.htm?id=567440742661</a>
<ul>
<li>准系统 ¥2200</li>
<li>4×3.5 寸盘位</li>
</ul>
</li>
<li>DELL R630 <a href="https://item.taobao.com/item.htm?id=557304169457" target="_blank" rel="noopener noreferrer">https://item.taobao.com/item.htm?id=557304169457</a>
<ul>
<li>准系统 ¥1449</li>
<li>8×2.5 寸盘位</li>
</ul>
</li>
<li>DELL R730 - <a href="https://item.taobao.com/item.htm?id=604846119404" target="_blank" rel="noopener noreferrer">https://item.taobao.com/item.htm?id=604846119404</a>
<ul>
<li>2U</li>
<li>8-12 3.5 寸盘位, 8-24 2.5 寸盘位,</li>
</ul>
</li>
</ul>
</li>
<li>模块服务器
<ul>
<li>广达 单模块即可</li>
<li>改装后的非常安静</li>
<li>价格 ~¥500</li>
<li><strong>注意</strong> 硬盘可能不太好放</li>
</ul>
</li>
</ul>
<h3 class="anchor anchorWithStickyNavbar_EG6R" id="装机">装机<a href="https://wener.me/story/build-your-own-nas#%E8%A3%85%E6%9C%BA" class="hash-link" aria-label="Direct link to 装机" title="Direct link to 装机" translate="no">​</a></h3>
<div class="theme-admonition theme-admonition-tip admonition_phik alert alert--success"><div class="admonitionHeading_zxjO"><span class="admonitionIcon_bSNx"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>tip</div><div class="admonitionContent_R6P7"><ul>
<li>装机通常主板都是买新的</li>
<li>机箱看情况买新的或二手的或定制的</li>
<li>明确装机要达成的目标 - 不要为了装机而装机
<ul>
<li>容量？</li>
<li>尺寸大小？</li>
<li>硬盘、内存、CPU？</li>
</ul>
</li>
</ul></div></div>
<ul>
<li>主板
<ul>
<li><a href="https://www.asrockrack.com/" target="_blank" rel="noopener noreferrer">https://www.asrockrack.com/</a>
<ul>
<li>选择好后可淘二手</li>
</ul>
</li>
</ul>
</li>
<li>机箱
<ul>
<li>确定好主板类型后决定</li>
<li>小的情况可定制 亚克力 机箱</li>
</ul>
</li>
<li>电源
<ul>
<li>功耗 &lt; 120W 时可考虑外置</li>
<li>服务器功耗最好买 二手 服务器电源模块</li>
</ul>
</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="系统">系统<a href="https://wener.me/story/build-your-own-nas#%E7%B3%BB%E7%BB%9F" class="hash-link" aria-label="Direct link to 系统" title="Direct link to 系统" translate="no">​</a></h2>
<ol>
<li>黑群晖</li>
</ol>
<ul>
<li>安装使用容易</li>
<li>现成的应用</li>
<li>适用于新手或不愿意折腾的玩家</li>
<li>相对封闭 - 不建议用服务器运行，可以考虑开虚拟机</li>
</ul>
<ol start="2">
<li>Linux</li>
</ol>
<h3 class="anchor anchorWithStickyNavbar_EG6R" id="linux">Linux<a href="https://wener.me/story/build-your-own-nas#linux" class="hash-link" aria-label="Direct link to Linux" title="Direct link to Linux" translate="no">​</a></h3>
<ul>
<li>AlpineLinux</li>
<li>zfs
<ul>
<li>看情况使用 raidz1,raidz2,draid
<ul>
<li>raidz1=raid5 - 4 盘时</li>
<li>raidz2=raid6 - 6 盘时</li>
<li>draid - &gt;= 12 盘时</li>
</ul>
</li>
<li>SSD 缓存 - 如果有预算</li>
</ul>
</li>
<li>应用场景
<ul>
<li>多媒体 - Jeylyfin, Plex, 照片, 音乐</li>
<li>下载器 - aria2, qtorrent, transimision</li>
<li>家庭 - Nextcloud</li>
<li>共享 - samba, afp</li>
<li>备份 - afp/TimeMachine</li>
<li>常用应用 - vaultwarden</li>
<li>服务器
<ul>
<li>Kubernetes/k3s - 推荐</li>
<li>Docker</li>
<li>虚拟机</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1>常见问题</h1>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="如何选择硬盘缓存">如何选择硬盘缓存<a href="https://wener.me/story/build-your-own-nas#%E5%A6%82%E4%BD%95%E9%80%89%E6%8B%A9%E7%A1%AC%E7%9B%98%E7%BC%93%E5%AD%98" class="hash-link" aria-label="Direct link to 如何选择硬盘缓存" title="Direct link to 如何选择硬盘缓存" translate="no">​</a></h2>
<ul>
<li>如果使用 ZFS+SSD 作为缓存，那硬盘的缓存影响不大</li>
<li>如果使用 硬件 RAID，那硬盘的缓存能提效</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="为什么选择-sas-硬盘">为什么选择 SAS 硬盘<a href="https://wener.me/story/build-your-own-nas#%E4%B8%BA%E4%BB%80%E4%B9%88%E9%80%89%E6%8B%A9-sas-%E7%A1%AC%E7%9B%98" class="hash-link" aria-label="Direct link to 为什么选择 SAS 硬盘" title="Direct link to 为什么选择 SAS 硬盘" translate="no">​</a></h2>
<ul>
<li>SAS 为企业级硬盘 - 二手质量高</li>
<li>二手市场多</li>
<li>支持热插拔 - 更换容易</li>
<li><strong>但</strong> 通常需要额外的 SAS 卡 - 一般选择半高 PCI</li>
<li>SAS 还需要注意区分 2.5 和 3.5
<ul>
<li>3.5 - 容量 300G-5T, 转速 7200+</li>
<li>2.5 - 容量 120G-1T, 转速 10000-15000</li>
</ul>
</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="如何选择-cpu">如何选择 CPU<a href="https://wener.me/story/build-your-own-nas#%E5%A6%82%E4%BD%95%E9%80%89%E6%8B%A9-cpu" class="hash-link" aria-label="Direct link to 如何选择 CPU" title="Direct link to 如何选择 CPU" translate="no">​</a></h2>
<ul>
<li>志强 Xeon
<ul>
<li>优点
<ul>
<li>二手市场多</li>
<li>价格便宜</li>
<li>性能非常好 - 核心多</li>
</ul>
</li>
<li>缺点
<ul>
<li>功耗高 - 120W</li>
<li>需要主板支持</li>
<li>一般媒体能力普通</li>
<li>全新非常贵</li>
</ul>
</li>
</ul>
</li>
<li>志强 Xeon L
<ul>
<li>优点
<ul>
<li>功耗低 - 25W</li>
</ul>
</li>
<li>缺点
<ul>
<li>ECC NOREG</li>
<li>通常内存只支持 2 通道 - 一般家用 NAS 足矣</li>
</ul>
</li>
</ul>
</li>
<li>酷睿 Core
<ul>
<li>优点
<ul>
<li>单核频率高</li>
<li>媒体处理能力强</li>
<li>功耗一般 - 60W</li>
</ul>
</li>
<li>缺点
<ul>
<li>贵</li>
<li>核心数少 - 作为服务器核心数比单核频率更重要</li>
</ul>
</li>
</ul>
</li>
<li>凌动 Celeron
<ul>
<li>优点
<ul>
<li>功耗非常低 - 10W</li>
</ul>
</li>
<li>缺点
<ul>
<li>能力有限</li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="服务器-和-pchtpc-的区别">服务器 和 PC/HTPC 的区别<a href="https://wener.me/story/build-your-own-nas#%E6%9C%8D%E5%8A%A1%E5%99%A8-%E5%92%8C-pchtpc-%E7%9A%84%E5%8C%BA%E5%88%AB" class="hash-link" aria-label="Direct link to 服务器 和 PC/HTPC 的区别" title="Direct link to 服务器 和 PC/HTPC 的区别" translate="no">​</a></h2>
<ul>
<li>服务器通常支持管理模块
<ul>
<li>启动慢</li>
<li>支持远程远离 - 开机，停机，健康检查，KVM</li>
<li>固件升级</li>
</ul>
</li>
<li>PC/HTPC 一般直接启动即可</li>
</ul>]]></content:encoded>
            <category>运维</category>
            <category>DevOps</category>
        </item>
        <item>
            <title><![CDATA[2021 总结]]></title>
            <link>https://wener.me/story/2021-review</link>
            <guid>https://wener.me/story/2021-review</guid>
            <pubDate>Sat, 01 Jan 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[It's HARD, but progressing.]]></description>
            <content:encoded><![CDATA[<blockquote>
<p>It's HARD, but progressing.</p>
</blockquote>
<hr>
<blockquote>
<p>Long way to go.</p>
</blockquote>]]></content:encoded>
            <category>思考</category>
        </item>
        <item>
            <title><![CDATA[从新学习]]></title>
            <link>https://wener.me/story/relearning</link>
            <guid>https://wener.me/story/relearning</guid>
            <pubDate>Mon, 20 Dec 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[最近开始学习 PMP，管理，对我来说是一个“全新”的领域。]]></description>
            <content:encoded><![CDATA[<p>最近开始学习 PMP，管理，对我来说是一个“全新”的领域。</p>
<p><img decoding="async" loading="lazy" alt="relearning.banner.jpg" src="https://wener.me/assets/images/2021-12-20-relearning.banner-02d7e7abc66f323ab494cd4533ab04f0.jpg" width="1792" height="1024" class="img_IxRA"></p>
<p>学习去学习，是这次学习开始老师讲的主题，因此也产生了自己的一些思考。
学习去学习，我认为是 meta-learning。学习，应该从未停止过，当我们从猴子开始，我们就在学习去熟悉、了解、掌握新的工具、环境、事物。</p>
<p>不需要学习的思考，是潜意识的提升。对学习过程进行思考归纳总结是，得到的是过程改进方式，因此两个人学习一天的结果会有所不同。</p>
<p>让给我联想到英语的两个单词，study &amp; learn。study 强调的只是过程，learn 强调的是结果内容，应该思考平时学习是 study 还是 learn。</p>
<blockquote>
<p>带思考的学习更容易获取一项技能知识。</p>
</blockquote>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="管理">管理<a href="https://wener.me/story/relearning#%E7%AE%A1%E7%90%86" class="hash-link" aria-label="Direct link to 管理" title="Direct link to 管理" translate="no">​</a></h2>
<p>管理，对我来说是一个 “全新” 的领域，作为技术研发，从未从管理学的概念去思考过问题。
但之所以是“全新”，是因为，生活无处不是管理，家庭纠纷、生活规划、日常工作。</p>
<p>我理解的管理，是控制。</p>
<p><img decoding="async" loading="lazy" src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIKICAgdmVyc2lvbj0iMS4wIgogICB3aWR0aD0iNTAwIgogICBoZWlnaHQ9IjEyNiIKICAgdmlld0JveD0iMCAwIDM5NCA5NiI+CjxkZWZzPgogICA8bWFya2VyIGlkPSJhcnJvdyIgbWFya2VyVW5pdHM9InVzZXJTcGFjZU9uVXNlIiBtYXJrZXJXaWR0aD0iMTIiIG1hcmtlckhlaWdodD0iMTIiIHJlZlg9IjQuNSIgcmVmWT0iNiIgb3JpZW50PSJhdXRvIj4KICAgICAgPHBhdGggZD0iTSAzLDMgNC41LDYgMyw5IDksNiB6IiBzdHlsZT0ic3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjIiLz4KICAgPC9tYXJrZXI+CjwvZGVmcz4KPGcKICAgdHJhbnNmb3JtPSJtYXRyaXgoMC45ODExOTM2LDAsMCwwLjk4MzA0OTUsOS4yNzUxMTYyZS00LDMuMjcxMjQ3NSkiCiAgIGlkPSJnMjU2NCI+CiAgIDxyZWN0IHg9IjAiIHk9Ii01IiB3aWR0aD0iNDAxLjU1MDc4IiBoZWlnaHQ9IjEwMSIgaWQ9InBhdGgxMTEiIHN0eWxlPSJmaWxsOiNmZmZmZmYiLz4KICAgPGcgc3R5bGU9InN0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoyIj4KICAgICAgPGNpcmNsZSBjeD0iNjkiIGN5PSIyMi45NDE0MDQiIHI9IjQiIGlkPSJwYXRoMTQxIi8+CiAgICAgIDxnIHN0eWxlPSJmaWxsOiNmZmZmZmYiPgogICAgICAgICA8cmVjdCB4PSIxMjUiIHk9IjYuOTQxNDA0IiB3aWR0aD0iNjcuNDQ5MjIiIGhlaWdodD0iMzIiIGlkPSJwYXRoMTEzIi8+CiAgICAgICAgIDxyZWN0IHg9IjE4OSIgeT0iNTguOTQxNDA0IiB3aWR0aD0iNjcuNDQ5MjIiIGhlaWdodD0iMzIiIGlkPSJwYXRoMTU3Ii8+CiAgICAgICAgIDxyZWN0IHg9IjI0OSIgeT0iNi45NDE0MDQiIHdpZHRoPSI2Ny40NDkyMiIgaGVpZ2h0PSIzMiIgaWQ9InBhdGgyMzUiLz4KICAgICAgPC9nPgogICAgICA8ZyBmaWxsPSJub25lIiBtYXJrZXItZW5kPSJ1cmwoI2Fycm93KSI+CiAgICAgICAgIDxsaW5lIHgxPSI3MyIgeTE9IjIyLjk0MTQwNCIgeDI9IjExOC4yNjU2MiIgeTI9IjIyLjk0MTQwNCIgaWQ9InBhdGgxMzciLz4KICAgICAgICAgPGxpbmUgeDE9IjEiIHkxPSIyMi45NDE0MDQiIHgyPSI1OC4yNjU2MiIgeTI9IjIyLjk0MTQwNCIgaWQ9InBhdGgxNDUiLz4KICAgICAgICAgPGxpbmUgeDE9IjMxNi40NDkyMiIgeTE9IjIyLjk0MTQwNCIgeDI9IjM4Ni44MTI1IiB5Mj0iMjIuOTQxNDA0IiBpZD0icGF0aDE0OSIvPgogICAgICAgICA8cG9seWxpbmUgcG9pbnRzPSIzNTUsMjIuOTQxNDA0IDM1NSw3NC45NDE0MDQgMjYzLjE4NzUsNzQuOTQxNDA0IiBpZD0icGF0aDE1MyIvPgogICAgICAgICA8cG9seWxpbmUgcG9pbnRzPSIxODksNzQuOTQxNDA0IDY5LDc0Ljk0MTQwNCA2OSwzMy42Nzk2ODQiIGlkPSJwYXRoMTczIi8+CiAgICAgICAgIDxsaW5lIHgxPSIxOTIuNDQ5MjIiIHkxPSIyMi45NDE0MDQiIHgyPSIyNDIuMjY1NjIiIHkyPSIyMi45NDE0MDQiIGlkPSJwYXRoMjUxIi8+CiAgICAgIDwvZz4KICAgPC9nPgogICA8ZyBzdHlsZT0iZm9udC1mYW1pbHk6RGVqYVZ1IFNhbnMiPgogICAgICA8dGV4dCBzdHlsZT0iZm9udC1zaXplOjExLjMiPjx0c3BhbgogICAgICAgICB4PSIxMzAiIHk9IjI1Ljc5Mjk2OSIgaWQ9ImcxMTUiPkNvbnRyb2xsZXI8L3RzcGFuPjx0c3BhbgogICAgICAgICB4PSIyMDIuNjk5MjIiIHk9Ijc3Ljc5Mjk2OSIgaWQ9ImcxNTkiPlNlbnNvcjwvdHNwYW4+PHRzcGFuCiAgICAgICAgIHg9IjI2MC45NzY1NiIgeT0iMjUuNzkyOTY5IiBpZD0iZzIzNyI+U3lzdGVtPC90c3Bhbj48L3RleHQ+CiAgICAgIDx0ZXh0IHN0eWxlPSJmb250LXNpemU6MTEuMztmb250LXdlaWdodDpib2xkIj48dHNwYW4KICAgICAgICAgeD0iNTYuMjkyOTY5IiB5PSIxOC40NzY1NjIiIGlkPSJnMTc3Ij4rPC90c3Bhbj48dHNwYW4KICAgICAgICAgeD0iNTQuMjY1OTYiIHk9IjM3LjY1NjI1IiBpZD0iZzE4MSI+4oiSPC90c3Bhbj48L3RleHQ+CiAgICAgIDx0ZXh0IHN0eWxlPSJmb250LXNpemU6OC41Ij48dHNwYW4KICAgICAgICAgeD0iNi4xNjAxNTU4IiB5PSIxOC4xNzk2ODgiIGlkPSJnMTg1Ij5SZWZlcmVuY2U8L3RzcGFuPjx0c3BhbgogICAgICAgICB4PSI3Ni4wMzUxNTYiIHk9IjUuOTY1ODU1IiBpZD0iZzIwNSI+TWVhc3VyZWQ8L3RzcGFuPjx0c3BhbgogICAgICAgICB4PSI4Ni44MTI1IiB5PSIxNy45NjQ4NDQiIGlkPSJnMjIzIj5lcnJvcjwvdHNwYW4+PHRzcGFuCiAgICAgICAgIHg9IjIwMy4yNjE3MiIgeT0iMy40MDYyNSIgaWQ9ImcyNTUiPlN5c3RlbTwvdHNwYW4+PHRzcGFuCiAgICAgICAgIHg9IjIwOC4zMTI1IiB5PSIxNS40MDYyNSIgaWQ9ImcyNjkiPmlucHV0PC90c3Bhbj48dHNwYW4KICAgICAgICAgeD0iMzIzLjE2Nzk3IiB5PSIxMy4xNzU3ODEiIGlkPSJnMjgxIj5TeXN0ZW0gb3V0cHV0PC90c3Bhbj48dHNwYW4KICAgICAgICAgeD0iOTMuNzg1MTU2IiB5PSI2OS41MjczNDQiIGlkPSJnMzA5Ij5NZWFzdXJlZCBvdXRwdXQ8L3RzcGFuPjwvdGV4dD4KICAgPC9nPgo8L2c+Cjwvc3ZnPg==" width="500" height="126" class="img_IxRA"></p>
<p>控制理论的 反馈循环（feedbak loop） 图。</p>
<p><img decoding="async" loading="lazy" src="https://wener.me/assets/images/PDCA-Multi-Loop-d5230e719fa53c545327b7d86f6becf9.png" width="1432" height="390" class="img_IxRA"></p>
<p>精益生产的 PDCA 循坏图。</p>
<p><img decoding="async" loading="lazy" src="https://kroki.io/plantuml/svg/eNpljrFKBDEQhvt5iqkF5fZQ7AQtfAGvOyxCdm43bDIJySyH1hYqIip2VlZrp5Wlb7PnPYZ7rhwBq498f_iS1BgOKiqHzrPXdfSOUGJLkLZLVNwkCjidZLIMBouD3KgY_XJWG90wpYSTvcNsDEo3qqKZEUvH1lTsiAUtLSS71CbSKtGJjyXFvLSfP0wL1Vo59Sxn5pKwmOZfJS2KK_uvUQDUpiQkF-RiaCQdTRDjGWC-c467R9g_vPe3bwh_HMy6u-qvn2DERqxuuvXrHYz4Fc-f_ccXjNiIoQXbfTh_vzyu7jsYkTV_AJjNmHw=" alt="" class="img_IxRA"></p>
<p>项目管理 5 大过程组。</p>
<p>虽然是不同领域里的主题，但表达的却是相同的内容。</p>
<p>控制，减少变量，提升可预测性。</p>
<p>控制，是为了 提升/改进 现有过程。</p>
<p>What happened is happened,but what we can learn from the past ?</p>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="学习">学习<a href="https://wener.me/story/relearning#%E5%AD%A6%E4%B9%A0" class="hash-link" aria-label="Direct link to 学习" title="Direct link to 学习" translate="no">​</a></h2>
<p>为了更好理解和管理自己的学习，我梳理了我的学习过程，希望能从无意识变成有意识的学习。</p>
<p><strong>我的学习过程</strong></p>
<ul>
<li>Repeat - 重复 - 抄写重复最为浅显的内容</li>
<li>Refactor - 重构 - 按照自己的理解从新组织内容语言</li>
<li>Realize - 从新意识 - 对照生活、工作、现实、跳出固化场景</li>
<li>Regain - 从新获取 - 掌握</li>
</ul>
<div class="theme-admonition theme-admonition-caution admonition_phik alert alert--warning"><div class="admonitionHeading_zxjO"><span class="admonitionIcon_bSNx"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>caution</div><div class="admonitionContent_R6P7"><p>并不是所有主题都会经历所有的过程。学以致用，无以致用的主题只会停留在早期阶段。</p></div></div>
<blockquote>
<p><del>We learn from teacher.</del></p>
<p>Teacher teached us.</p>
<p>Whatever teaches us is our teacher.</p>
</blockquote>
<p>老师、书本、文章、源码、事件都可能触发我们的“提升”。</p>
<p><strong>Repeat</strong></p>
<p>这时候我会进行简单的记录，好记性不如烂笔头。</p>
<p>在 <a href="https://github.com/wenerme/wener" target="_blank" rel="noopener noreferrer">https://github.com/wenerme/wener</a> 这里我记录了感兴趣的很多内容，有的可能是工作相关的，例如：技术；可能是工作无关的，例如：<a href="https://github.com/wenerme/wener/blob/958471ae9d9e2399e3c901f68380cea3c3154e82/notes/healthcare/disease/rheumatoid-arthritis.md" target="_blank" rel="noopener noreferrer">医学</a>。</p>
<div class="theme-admonition theme-admonition-tip admonition_phik alert alert--success"><div class="admonitionHeading_zxjO"><span class="admonitionIcon_bSNx"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>Repeat阶段 内容的特点</div><div class="admonitionContent_R6P7"><ul>
<li>浅显</li>
<li>离散</li>
<li>精简</li>
</ul></div></div>
<ul>
<li>浅显 - You get what you see</li>
<li>离散</li>
<li>精简 - 知识索引</li>
</ul>
<p><strong>Refactor</strong></p>
<p>有时候，我们会重复遇到相同的问题，抱有相同的疑问，需要参考相同的话题。</p>
<p>此时参考简单记录的内容已经不足以回答或解决问题，因此，会对内容进行重新重构。</p>
<p>重构意味着重新梳理内容知识，从新调整“索引”内容，按照自己的“脑回路”对问题域进行理解分析。</p>
<p>使用 git 进行笔记的好处是它能记录你的“<a href="https://github.com/wenerme/wener/commits/master/notes/devops/kubernetes" target="_blank" rel="noopener noreferrer">过程</a>”。</p>
<div class="theme-admonition theme-admonition-tip admonition_phik alert alert--success"><div class="admonitionHeading_zxjO"><span class="admonitionIcon_bSNx"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>Refactor阶段 内容的特点</div><div class="admonitionContent_R6P7"><ul>
<li>发散的单个主题牵引出新的主题</li>
<li>按照自己的“脑回路”组织的内容结构</li>
<li>部分内容会进行细化</li>
</ul></div></div>
<p><strong>Realize</strong></p>
<p>意识阶段有两个方式：</p>
<ol>
<li>自身</li>
<li>跨域</li>
</ol>
<p>自身，我们通常讲从失败学习，更多的是意识到了问题点，问题点在不经历的时候是不会发现的，而当遇到问题，解决问题这个循环进行多次后，会基于基本的理论知识抽取出属于自己的经验结论。</p>
<p>跨域，学习和成长不可能是一条路走到头的，有所成就的人绝不可能只会精通一门学科，理解不同领域事物的相关性是意识提升的关键。</p>
<p><strong>Regain</strong></p>
<p>之所以叫 regain，是因为已经经历过早期的 gain 过程，regain 时，才是我们真正的有所掌握，学以致用。</p>
<div class="theme-admonition theme-admonition-tip admonition_phik alert alert--success"><div class="admonitionHeading_zxjO"><span class="admonitionIcon_bSNx"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>Ragain 阶段的体现</div><div class="admonitionContent_R6P7"><ul>
<li>独立决策</li>
<li>预测行为</li>
</ul></div></div>
<p>这时候我愿称之为： 专家/Master。</p>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="思维模式">思维模式<a href="https://wener.me/story/relearning#%E6%80%9D%E7%BB%B4%E6%A8%A1%E5%BC%8F" class="hash-link" aria-label="Direct link to 思维模式" title="Direct link to 思维模式" translate="no">​</a></h2>
<p>通常学习分为两种场景：</p>
<ol>
<li>工具技术学习 - 实</li>
<li>思想方法论学习 - 虚</li>
</ol>
<p>因为我的主要职责是软件开发，管理对我来说是个“虚”技能，因为并不能直接指导工作。但对于项目经理来说，管理是个“实”技能，而计算机理论则是“虚”技能。</p>
<p>学习项目管理，是更多是为了去了解项目管理的思维模式。</p>
<div class="theme-admonition theme-admonition-tip admonition_phik alert alert--success"><div class="admonitionHeading_zxjO"><span class="admonitionIcon_bSNx"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>模式=锤子=工具</div><div class="admonitionContent_R6P7"><p>当我们手里拿着锤子时，看到所有东西都是钉子。</p><p>-- 工具定律</p></div></div>
<p>项目管理思维模式 和 软件设计模式 非常相似，是当我们解决问题时的参照。</p>
<p>但当你没有这个参照，没有这把锤子时，就会陷入困境，不断重复错误。</p>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="学习主题">学习主题<a href="https://wener.me/story/relearning#%E5%AD%A6%E4%B9%A0%E4%B8%BB%E9%A2%98" class="hash-link" aria-label="Direct link to 学习主题" title="Direct link to 学习主题" translate="no">​</a></h2>
<p>学习是一个动作，必然会有目标，学习的主题可以是大而广的，也可以是小而精的。不如说，我们都需要把自己的知识储备磨练成 T 型结构。</p>
<p><img decoding="async" loading="lazy" src="https://kroki.io/excalidraw/svg/eNrdls1u20YQgN-Fvars_v_opjiOrdR1XNiFHRhCsRbX0tYUSZArWbLha1-gt158aq-9Bn2eBO1jdEjqD6LSCE7ToNVJuzucmZ35dmbuAz_LbNAO7LRvYhfl5varfuyyq9TkUdAKbGxHNvFF0L68D1wEgpHz12PHJ53vh_34ZO-wc6WPLEjO9Zg8T29hOQ3anLOQaa6YFkhTyVvBLGgTqUItNWEKMaHKzVsX-WHQpoiGCsEJoYIShQkcDa0bDH3QRq3AJIPYVv8Kn6c3di-N0xzsfYGqH1i8Mv2bQZ6Ok2hx5nOTFJnJ4QJwfu3i-NTPSi3B0PSH47x0u9Z2XvuAF-uFXJFCTEAK1A6GiS0gDqQVpJnpOw-XwQj8KW1m3agMUW_5_dDkWS0fVC6VlqyF-GGhicYaSdkKJjYvXJrApkLL1XGa9ME01lxIwjQCn1zxHPLgy8-vTVxYuGupc3-Zm2Qcx61gnEXG1zao1gzCzwUGM1nq6gxeohbqtS63RBr1wPXYFH4vHY2cBy0n5UcLzYU3uX_mksglg8WeTaKNnUqqU-Z_aE20Jre2N-fjoTWniT4zL7_pdvvadrPvzu72z05u7iZbaRKahUhLjISmlGuq5jjhUDHKGFOEEbwOFFrxg7UOBUMIM8FAuhT63_BEFZacKoHXeUKswRMwQSBOVD6dJ64gCU2eUKsR38-A0wYxy1gQQTdjobAC8un2p1Vh-e3gIp3-wPeywy_ZgZrI8-TV-etPmPMVjSXpgoZCIiwEwlyQRd1EALrEVDKOscT66eA234cUIYFSQTRFYAee06pacQK1iiu9O5cbLJXCDZKkqI3sBMIHQNoNkW3YNq7d6_0dSZThxqvCAiGOSJmO96BEOjOD2aE9OPdndpQM85sL--LfQkkSHjJCOBNQISRWi6JJQoyZBJoo01J8PErQ5zEUIQRcKokEdJ4VXJqEEjiWUBkg9YirBVyEUwzWMf5n2YJGoOjnZmtLQBpxqGmrIJnuvT5Jzgf7t9PJeEzNy4wd6_6qDXo79XUXlEKHME5xiYnSaFkcOA5hsFJI8XrQmuelbAqLNBD1X256Rfl_1fQY3JYiCO1602OqOURRSaki8ulNT2ClNSiuUtAO_nj85c_ffnz3-FjGIk38qbsDOwTVqxdm5OJZFYJSvhO7ATgWxPba14XEO5iyF9s-zaqIFzZ2SakFPuuDFgOrvLtELs3dwCUmPtv0YK1WzQFZlSrRLFUUmrMQMD29t1SZztHR9CLqjM5y__zg9PTV19fj409fqhi8C0KV0gpRxYSumyCDIVXCoCewxgxIr0CnWoUw6Smp64L28V1w84nMAQPLBPoww7ujuUNtEgQjincgB9S-ffPT2zc_v_v91wYjO6P1YZhWRh56VZ4teH7_8PAXflh1kw==" alt="" class="img_IxRA"></p>
<div class="theme-admonition theme-admonition-tip admonition_phik alert alert--success"><div class="admonitionHeading_zxjO"><span class="admonitionIcon_bSNx"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>俗话说</div><div class="admonitionContent_R6P7"><p>东方不亮西方亮。</p></div></div>
<p>不同的角度、观点都会让学习更加容易。</p>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="学习过程">学习过程<a href="https://wener.me/story/relearning#%E5%AD%A6%E4%B9%A0%E8%BF%87%E7%A8%8B" class="hash-link" aria-label="Direct link to 学习过程" title="Direct link to 学习过程" translate="no">​</a></h2>
<p>学习是持续的不是一次性的，没有时间限制，没有领域限制，没有思维限制。</p>
<p>学习的驱动却肯定是有根因的，每个人都有所不同。</p>
<blockquote>
<p>We are who we are.</p>
</blockquote>
<p>学习的参考不应该是别人，而是自己。今天的自己期望明天的自己对比昨天的自己能有所提升，而达成这样提升的就是学习。</p>]]></content:encoded>
            <category>思考</category>
            <category>学习</category>
        </item>
        <item>
            <title><![CDATA[系统盘恢复]]></title>
            <link>https://wener.me/story/rescue-root-disk</link>
            <guid>https://wener.me/story/rescue-root-disk</guid>
            <pubDate>Wed, 17 Nov 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[喜欢使用 闪存盘/U 盘 作为 Linux 系统盘，但 U 盘 使用寿命有限，当异常后需要对系统盘进行更换。]]></description>
            <content:encoded><![CDATA[<p>喜欢使用 闪存盘/U 盘 作为 Linux 系统盘，但 U 盘 使用寿命有限，当异常后需要对系统盘进行更换。</p>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="现状">现状<a href="https://wener.me/story/rescue-root-disk#%E7%8E%B0%E7%8A%B6" class="hash-link" aria-label="Direct link to 现状" title="Direct link to 现状" translate="no">​</a></h2>
<p>使用 SanDisk CZ430 作为 系统盘/Root 分区</p>
<p><strong>lsblk</strong></p>
<div class="language-text codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-text codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token plain">NAME   MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">sda      8:0    1 114.6G  0 disk</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">├─sda1   8:1    1   128M  0 part /boot</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">└─sda2   8:2    1 114.4G  0 part /</span><br></span></code></pre></div></div>
<p><strong>df -h /</strong></p>
<div class="language-text codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-text codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token plain">Filesystem      Size  Used Avail Use% Mounted on</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">/dev/sda2       113G  2.2G  106G   2% /</span><br></span></code></pre></div></div>
<p>在使用一段时间后， U 盘 异常，系统被重新挂载为只读。</p>
<div class="language-bash codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-bash codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token plain">touch ~/test</span><br></span></code></pre></div></div>
<div class="language-text codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-text codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token plain">touch: cannot touch '/home/admin/test': Read-only file system</span><br></span></code></pre></div></div>
<p>dmesg 可看到异常信息，几个月前已经出问题了，因为系统还能正常使用暂且没管。</p>
<div class="language-dmesg codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_Lwh4">dmesg -T</div><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-dmesg codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token plain">[Tue Sep  7 16:48:16 2021] sd 2:0:0:0: [sda] tag#0 UNKNOWN(0x2003) Result: hostbyte=0x00 driverbyte=0x08 cmd_age=0s</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">[Tue Sep  7 16:48:16 2021] sd 2:0:0:0: [sda] tag#0 Sense Key : 0x7 [current]</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">[Tue Sep  7 16:48:16 2021] sd 2:0:0:0: [sda] tag#0 ASC=0x27 ASCQ=0x0</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">[Tue Sep  7 16:48:16 2021] sd 2:0:0:0: [sda] tag#0 CDB: opcode=0x2a 2a 00 00 04 16 90 00 00 08 00</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">[Tue Sep  7 16:48:16 2021] blk_update_request: critical target error, dev sda, sector 267920 op 0x1:(WRITE) flags 0x103000 phys_seg 1 prio class 0</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">[Tue Sep  7 16:48:16 2021] Buffer I/O error on dev sda2, logical block 466, lost async page write</span><br></span></code></pre></div></div>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="备份恢复方案">备份恢复方案<a href="https://wener.me/story/rescue-root-disk#%E5%A4%87%E4%BB%BD%E6%81%A2%E5%A4%8D%E6%96%B9%E6%A1%88" class="hash-link" aria-label="Direct link to 备份恢复方案" title="Direct link to 备份恢复方案" translate="no">​</a></h2>
<p>将现在数据完整备份到新的 U 盘，重启即可，但恢复备份有好几种方式。</p>
<ol>
<li>全盘 dd</li>
</ol>
<ul>
<li>速度慢</li>
<li>简单暴力</li>
<li>需要提供至少现在磁盘大小的存储</li>
</ul>
<ol start="2">
<li>重装 - 仅拷贝数据</li>
</ol>
<ul>
<li>麻烦</li>
</ul>
<ol start="3">
<li>恢复分区、恢复数据</li>
</ol>
<ul>
<li>高效</li>
<li>需要多一点操作</li>
<li>新磁盘只需要已用空间大小即可</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="恢复">恢复<a href="https://wener.me/story/rescue-root-disk#%E6%81%A2%E5%A4%8D" class="hash-link" aria-label="Direct link to 恢复" title="Direct link to 恢复" translate="no">​</a></h2>
<p>因为这是已经第三四次恢复了，之前都是 dd，但这次打算用更高效的方式，且这次的系统盘为 128G，但目前只有 64G 的空闲 U 盘，故选择方案 3。</p>
<p><strong>恢复过程</strong></p>
<ol start="0">
<li>准备备份盘</li>
<li>分区表备份 &amp; 启动分区备份</li>
<li>root 分区 备份 &amp; 修复</li>
<li>验证</li>
<li>关机</li>
<li>取掉坏的 U 盘</li>
<li>开机</li>
</ol>
<div class="theme-admonition theme-admonition-tip admonition_phik alert alert--success"><div class="admonitionHeading_zxjO"><span class="admonitionIcon_bSNx"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>恢复备份注意事项</div><div class="admonitionContent_R6P7"><ul>
<li>选择一个可写目录 - tmpfs,shm - 例如: /run, /var/run, /tmp, /dev/shm</li>
</ul></div></div>
<h3 class="anchor anchorWithStickyNavbar_EG6R" id="准备备份盘">准备备份盘<a href="https://wener.me/story/rescue-root-disk#%E5%87%86%E5%A4%87%E5%A4%87%E4%BB%BD%E7%9B%98" class="hash-link" aria-label="Direct link to 准备备份盘" title="Direct link to 准备备份盘" translate="no">​</a></h3>
<ul>
<li>备份盘 /dev/sdb 64G，同 SanDisk CZ430</li>
</ul>
<div class="language-pre codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_Lwh4">lsblk</div><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-pre codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token plain">NAME   MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">sda      8:0    1 114.6G  0 disk</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">├─sda1   8:1    1   128M  0 part /boot</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">└─sda2   8:2    1 114.4G  0 part /</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">sdb      8:16   1  57.3G  0 disk</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithStickyNavbar_EG6R" id="分区表启动分区备份">分区表&amp;启动分区备份<a href="https://wener.me/story/rescue-root-disk#%E5%88%86%E5%8C%BA%E8%A1%A8%E5%90%AF%E5%8A%A8%E5%88%86%E5%8C%BA%E5%A4%87%E4%BB%BD" class="hash-link" aria-label="Direct link to 分区表&amp;启动分区备份" title="Direct link to 分区表&amp;启动分区备份" translate="no">​</a></h3>
<div class="language-bash codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-bash codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token plain"># 确保使用 root 用户操作</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">sudo su</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># 进入可写目录</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">cd /run</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># 正常情况分区表的备份恢复</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># sfdisk -d /dev/sda | sfdisk -f /dev/sdb</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># 选择简单方式备份后再修改最后分区</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># 这里 dd 130MB - 同时把 boot 分区备份了</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">dd if=/dev/sda of=/dev/sdb bs=1M count=130 status=progress</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># 转储分区表</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">sfdisk -d /dev/sda &gt; sda.partition.table.txt</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># 删除第二个分区的大小</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">sed -r '$ s/size=\s*\d+,//' -i sda.partition.table.txt</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># 重建分区</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">sfdisk -f /dev/sdb &lt; sda.partition.table.txt</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># 现在分区大小正常</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">fdisk -l /dev/sdb</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># 分区变化重新扫描</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">mdev -s</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">partprobe</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># 出现分区</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">ls /dev/sdb*</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithStickyNavbar_EG6R" id="root-分区备份">root 分区备份<a href="https://wener.me/story/rescue-root-disk#root-%E5%88%86%E5%8C%BA%E5%A4%87%E4%BB%BD" class="hash-link" aria-label="Direct link to root 分区备份" title="Direct link to root 分区备份" translate="no">​</a></h3>
<ul>
<li>root 分区一般较大，直接 dd 慢且伤 U 盘</li>
<li>直接 dd 需要目标相同大小，否则丢数据</li>
<li>这里使用 clone 数据方式</li>
</ul>
<div class="theme-admonition theme-admonition-caution admonition_phik alert alert--warning"><div class="admonitionHeading_zxjO"><span class="admonitionIcon_bSNx"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>由于目标分区更小，不能直接复制</div><div class="admonitionContent_R6P7"><div class="language-bash codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-bash codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token plain"># ext4 数据拷贝 - 基于 ext4 block，只拷贝使用部分</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># 也可以用于生成 img 备份镜像之后用</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">e2image -ra -p /dev/sda2 /dev/sdb2</span><br></span></code></pre></div></div><ul>
<li>如果分区大小足够，直接复制即可</li>
</ul></div></div>
<p>因为 root 分区为 120G，但实际只用了 2.2G， 先镜像分区，缩小分区，然后恢复到新的分区。</p>
<p>索性内存有 4G，因此直接在 /dev/shm 操作，否则需要外置存储。</p>
<div class="language-bash codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-bash codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token plain"># 从新挂载为 3G - 默认 2G</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">mount -o remount,size=3G /dev/shm</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># 制作镜像</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">e2image -rap /dev/sda2 sda2.raw</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># 修复分区 - 一般 U盘 损坏都会导致 fs 异常</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">e2fsck -y sda2.raw</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># 缩小分区</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">resize2fs sda2.raw 4G</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># 恢复</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">e2image -rap sda2.raw /dev/sdb2</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># 扩展分区</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">resize2fs /dev/sdb2</span><br></span></code></pre></div></div>
<h3 class="anchor anchorWithStickyNavbar_EG6R" id="验证">验证<a href="https://wener.me/story/rescue-root-disk#%E9%AA%8C%E8%AF%81" class="hash-link" aria-label="Direct link to 验证" title="Direct link to 验证" translate="no">​</a></h3>
<div class="language-bash codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-bash codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token plain">fsck /dev/sdb1</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">fsck /dev/sdb2</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">mount /dev/sdb2 /mnt</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">mount /dev/sdb1 /mnt/boot</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># 不应该有变化</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">rsync -vna /boot/ /mnt/boot/</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"># 由于 fsck 修复，可能 root 分区大小不同</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">df / /mnt /boot /mnt/boot</span><br></span></code></pre></div></div>
<p>如果装有 qemu，还可以直接启用 qemu 验证，目前就简单确认文件没问题即可。</p>
<div class="language-bash codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-bash codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token plain"># 准备关机重启</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">umount -R /mnt</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">eject /dev/sdb</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">poweroff</span><br></span></code></pre></div></div>
<p>取掉 坏的 U 盘，启动。</p>
<p>一切恢复正常。🎉</p>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="总结">总结<a href="https://wener.me/story/rescue-root-disk#%E6%80%BB%E7%BB%93" class="hash-link" aria-label="Direct link to 总结" title="Direct link to 总结" translate="no">​</a></h2>
<p>系统盘恢复还是相当容易，但 CZ430 确实有点老了，在 2017 年左右上市，相同规格已经卖 4、5 年了，性能各方面有所欠缺，之后会尽量选择 Lexar S47。</p>
<ul>
<li><a href="https://wener.me/notes/ops/admin/fio-out/" target="_blank" rel="noopener noreferrer">https://wener.me/notes/ops/admin/fio-out/</a>
<ul>
<li>SanDisk CZ430, Lexar S47 性能对比</li>
</ul>
</li>
</ul>
<p>此外使用 U 盘 做系统盘的时候，一定注意修改 docker 之类的数据目录为其他存储，因为跑实际应用的时候 U 盘 性能是不足的，且数据库类型应用的小 BlockSize IO 不适合 U 盘。</p>]]></content:encoded>
            <category>AlpineLinux</category>
            <category>运维</category>
        </item>
        <item>
            <title><![CDATA[PostgreSQL ORDER BY+LIMIT 时的索引选择]]></title>
            <link>https://wener.me/story/postgresql-use-wrong-index-with-order-and-limit</link>
            <guid>https://wener.me/story/postgresql-use-wrong-index-with-order-and-limit</guid>
            <pubDate>Mon, 11 Oct 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[当使用 ORDER BY+LIMIT 时 PostgreSQL 可能会选择更差的执行方式，数据量大时，执行效率相差成百上千倍。]]></description>
            <content:encoded><![CDATA[<p>当使用 ORDER BY+LIMIT 时 PostgreSQL 可能会选择更差的执行方式，数据量大时，执行效率相差成百上千倍。</p>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="索引选择问题">索引选择问题<a href="https://wener.me/story/postgresql-use-wrong-index-with-order-and-limit#%E7%B4%A2%E5%BC%95%E9%80%89%E6%8B%A9%E9%97%AE%E9%A2%98" class="hash-link" aria-label="Direct link to 索引选择问题" title="Direct link to 索引选择问题" translate="no">​</a></h2>
<p>假设结构如下</p>
<div class="language-sql codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-sql codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">create</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">table</span><span class="token plain"> organizations</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  id bigserial </span><span class="token keyword" style="font-style:italic">primary</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">key</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  jgmc </span><span class="token keyword" style="font-style:italic">text</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  clrq </span><span class="token keyword" style="font-style:italic">date</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">create</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">index</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">if</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">not</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">exists</span><span class="token plain"> idx_organizations_clrq </span><span class="token keyword" style="font-style:italic">on</span><span class="token plain"> ic</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">organizations </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">clrq</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">create</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">index</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">if</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">not</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">exists</span><span class="token plain"> idx_organizations_jgmc_fts </span><span class="token keyword" style="font-style:italic">on</span><span class="token plain"> ic</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">organizations </span><span class="token keyword" style="font-style:italic">using</span><span class="token plain"> pgroonga </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">jgmc</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><br></span></code></pre></div></div>
<p>查询通常基于 clrq 排序，但会 搜索 jgmc。</p>
<div class="language-sql codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-sql codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">explain</span><span class="token plain"> analyse verbose</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">select</span><span class="token plain"> jgmc</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> clrq</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> id</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">from</span><span class="token plain"> ic</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">organizations</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">where</span><span class="token plain"> jgmc </span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain">@</span><span class="token operator" style="color:rgb(137, 221, 255)">~</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">'百度'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token operator" style="color:rgb(137, 221, 255)">and</span><span class="token plain"> clrq </span><span class="token operator" style="color:rgb(137, 221, 255)">is</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">not</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 88, 116)">null</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">order</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">by</span><span class="token plain"> clrq </span><span class="token keyword" style="font-style:italic">desc</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">limit</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">30</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">offset</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><br></span></code></pre></div></div>
<div class="theme-admonition theme-admonition-tip admonition_phik alert alert--success"><div class="admonitionHeading_zxjO"><span class="admonitionIcon_bSNx"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>使用了 clrq is not null 而非 nulls last</div><div class="admonitionContent_R6P7"><p>因为索引默认隐含 nulls last，也就是说 <code>order by clrq asc</code> 隐含 <code>order by clrq asc nulls last</code>。
当使用 <code>order by clrq desc nulls last</code> 时不会选择 <code>idx_organizations_clrq</code> 索引，因为 nulls 顺序不匹配。
因此使用 <code>is not null</code> 排除。</p></div></div>
<p>给出的执行计划和结果如下</p>
<div class="language-text codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-text codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token plain">Limit  (cost=0.43..33132.51 rows=30 width=1284) (actual time=4678.918..13104.109 rows=6 loops=1)</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">"  Output: jgmc, clrq, id"</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  -&gt;  Index Scan Backward using idx_organizations_clrq on ic.organizations  (cost=0.43..4069724.46 rows=3685 width=1284) (actual time=4678.916..13104.101 rows=6 loops=1)</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">"        Output: jgmc, clrq, id"</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">        Index Cond: (organizations.clrq IS NOT NULL)</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">        Filter: (organizations.jgmc &amp;@~ '百度'::text)</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">        Rows Removed by Filter: 3685166</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">Planning Time: 0.826 ms</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">Execution Time: 13104.226 ms</span><br></span></code></pre></div></div>
<p>实际执行了 13s，选择了 idx_organizations_clrq 索引。</p>
<p>但如果去掉 LIMIT：</p>
<div class="language-sql codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-sql codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">explain</span><span class="token plain"> analyse verbose</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">select</span><span class="token plain"> jgmc</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> clrq</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> id</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">from</span><span class="token plain"> ic</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">organizations</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">where</span><span class="token plain"> jgmc </span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain">@</span><span class="token operator" style="color:rgb(137, 221, 255)">~</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">'百度'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token operator" style="color:rgb(137, 221, 255)">and</span><span class="token plain"> clrq </span><span class="token operator" style="color:rgb(137, 221, 255)">is</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">not</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 88, 116)">null</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">order</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">by</span><span class="token plain"> clrq </span><span class="token keyword" style="font-style:italic">desc</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">-- limit 30 offset 0</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><br></span></code></pre></div></div>
<p>给出的计划如下</p>
<div class="language-text codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-text codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token plain">Sort  (cost=40479.53..40488.74 rows=3685 width=1284) (actual time=19.771..19.774 rows=6 loops=1)</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">"  Output: jgmc, clrq, id"</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  Sort Key: organizations.clrq DESC</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  Sort Method: quicksort  Memory: 36kB</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  -&gt;  Index Scan using idx_organizations_jgmc_fts on ic.organizations  (cost=0.00..40261.24 rows=3685 width=1284) (actual time=19.716..19.737 rows=6 loops=1)</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">"        Output: jgmc, clrq, id"</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">        Index Cond: (organizations.jgmc &amp;@~ '百度'::text)</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">        Filter: (organizations.clrq IS NOT NULL)</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">Planning Time: 0.706 ms</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">Execution Time: 21.824 ms</span><br></span></code></pre></div></div>
<p>实际执行只需要 21ms，选择了 idx_organizations_jgmc_fts 索引。</p>
<p>PostgreSQL 在这样的场景下选择了错误的索引，导致查询时间相差 600 倍。</p>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="索引选择原因">索引选择原因<a href="https://wener.me/story/postgresql-use-wrong-index-with-order-and-limit#%E7%B4%A2%E5%BC%95%E9%80%89%E6%8B%A9%E5%8E%9F%E5%9B%A0" class="hash-link" aria-label="Direct link to 索引选择原因" title="Direct link to 索引选择原因" translate="no">​</a></h2>
<p>ORDER BY+LIMIT 让查询有 <strong>提前结束的可能</strong>。</p>
<p>例如 实际数据 10k,但 limit 10, 只需使用索引扫描 10 条数据便可以停止执行，而不需要先判断 其他 条件。</p>
<p>因此 PostgreSQL 的 optimizer 有让这种选择优先的逻辑 <a href="https://github.com/postgres/postgres/blob/REL_13_STABLE/src/backend/optimizer/util/pathnode.c#L3633-L3753" target="_blank" rel="noopener noreferrer">pathnode.c#L3633-L3753</a>。</p>
<ul>
<li>create_limit_path 针对 LIMIT/OFFSET 创建执行计划
<ul>
<li>adjust_limit_rows_costs 调整此时的 rows costs</li>
</ul>
</li>
</ul>
<div class="language-c codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_Lwh4">adjust_limit_rows_costs</div><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-c codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">total_cost </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain">startup_cost </span><span class="token operator" style="color:rgb(137, 221, 255)">+</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">input_total_cost </span><span class="token operator" style="color:rgb(137, 221, 255)">-</span><span class="token plain"> input_startup_cost</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain"> count_rows </span><span class="token operator" style="color:rgb(137, 221, 255)">/</span><span class="token plain"> input_rows</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><br></span></code></pre></div></div>

































<table><thead><tr><th style="text-align:right">var</th><th>meaning</th></tr></thead><tbody><tr><td style="text-align:right">total_cost</td><td>总 cost</td></tr><tr><td style="text-align:right">startup_cost</td><td>初始 cost</td></tr><tr><td style="text-align:right">input_total_cost</td><td>输入总 cost</td></tr><tr><td style="text-align:right">input_startup_cost</td><td>输入初始总 cost</td></tr><tr><td style="text-align:right">count_rows</td><td>limit</td></tr><tr><td style="text-align:right">input_rows</td><td>输入 行</td></tr></tbody></table>
<p>在这个调整逻辑里会将现在的过程 cost <code>input_total_cost - input_startup_cost</code> 乘以 系数 <code>count_rows / input_rows</code>。
这个系数便会使得 ORDER BY+LIMIT 时优先选择索引。</p>
<p>因为两个计划的 rows 相同，优先了 排序索引则会选择排序索引方案。</p>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="解决问题">解决问题<a href="https://wener.me/story/postgresql-use-wrong-index-with-order-and-limit#%E8%A7%A3%E5%86%B3%E9%97%AE%E9%A2%98" class="hash-link" aria-label="Direct link to 解决问题" title="Direct link to 解决问题" translate="no">​</a></h2>
<div class="theme-admonition theme-admonition-tip admonition_phik alert alert--success"><div class="admonitionHeading_zxjO"><span class="admonitionIcon_bSNx"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>目标</div><div class="admonitionContent_R6P7"><p>避免选择排序索引</p></div></div>
<ol>
<li>使用 noop function 避免索引</li>
</ol>
<div class="language-sql codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-sql codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">explain</span><span class="token plain"> verbose</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">select</span><span class="token plain"> clrq</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> jgmc</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> id</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">from</span><span class="token plain"> ic</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">organizations</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">where</span><span class="token plain"> jgmc </span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain">@</span><span class="token operator" style="color:rgb(137, 221, 255)">~</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">'百度'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token operator" style="color:rgb(137, 221, 255)">and</span><span class="token plain"> clrq </span><span class="token operator" style="color:rgb(137, 221, 255)">is</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">not</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 88, 116)">null</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">order</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">by</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">coalesce</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">clrq</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">desc</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">limit</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">30</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">offset</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><br></span></code></pre></div></div>
<ol start="2">
<li>使用 expression 避免索引</li>
</ol>
<div class="language-sql codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-sql codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">explain</span><span class="token plain"> verbose</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">select</span><span class="token plain"> clrq</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> jgmc</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> id</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">from</span><span class="token plain"> ic</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">organizations</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">where</span><span class="token plain"> jgmc </span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain">@</span><span class="token operator" style="color:rgb(137, 221, 255)">~</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">'百度'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token operator" style="color:rgb(137, 221, 255)">and</span><span class="token plain"> clrq </span><span class="token operator" style="color:rgb(137, 221, 255)">is</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">not</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 88, 116)">null</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">order</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">by</span><span class="token plain"> clrq</span><span class="token operator" style="color:rgb(137, 221, 255)">+</span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">desc</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">limit</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">30</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">offset</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><br></span></code></pre></div></div>
<ol start="3">
<li>添加 nulls 顺序避免索引</li>
</ol>
<ul>
<li>因为 index 默认 asc nulls last 因此 asc 和 desc 选择不同 nulls 顺序</li>
<li>where is not null</li>
<li>desc nulls last</li>
<li>asc nulls first</li>
</ul>
<div class="language-sql codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-sql codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">explain</span><span class="token plain"> verbose</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">select</span><span class="token plain"> clrq</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> jgmc</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> id</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">from</span><span class="token plain"> ic</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">organizations</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">where</span><span class="token plain"> jgmc </span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain">@</span><span class="token operator" style="color:rgb(137, 221, 255)">~</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">'百度'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token operator" style="color:rgb(137, 221, 255)">and</span><span class="token plain"> clrq </span><span class="token operator" style="color:rgb(137, 221, 255)">is</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">not</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 88, 116)">null</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">order</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">by</span><span class="token plain"> clrq </span><span class="token keyword" style="font-style:italic">desc</span><span class="token plain"> nulls </span><span class="token keyword" style="font-style:italic">last</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">limit</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">30</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">offset</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><br></span></code></pre></div></div>
<ol start="4">
<li>调整 STATISTICS</li>
</ol>
<ul>
<li>在两者统计 rows 不同的情况下 - 这里不适用</li>
<li>SET STATISTICS</li>
<li>CREATE STATISTICS</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="statistics-场景">STATISTICS 场景<a href="https://wener.me/story/postgresql-use-wrong-index-with-order-and-limit#statistics-%E5%9C%BA%E6%99%AF" class="hash-link" aria-label="Direct link to STATISTICS 场景" title="Direct link to STATISTICS 场景" translate="no">​</a></h2>
<p>STATISTICS 影响 n_distinct，n_distinct 影响 inputs_rows，多个计划相同，可增加 STATISTICS 避免错误选择索引。</p>
<div class="language-sql codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-sql codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">SELECT</span><span class="token plain"> attname</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">       n_distinct</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">       null_frac</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">FROM</span><span class="token plain"> pg_stats</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">WHERE</span><span class="token plain"> tablename </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">'organizations'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">  </span><span class="token operator" style="color:rgb(137, 221, 255)">and</span><span class="token plain"> attname </span><span class="token operator" style="color:rgb(137, 221, 255)">in</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">'clrq'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">'jgmc'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><br></span></code></pre></div></div>




















<table><thead><tr><th style="text-align:left">attname</th><th style="text-align:left">n_distinct</th><th style="text-align:left">null_frac</th></tr></thead><tbody><tr><td style="text-align:left">clrq</td><td style="text-align:left">7914</td><td style="text-align:left">0.00030666665</td></tr><tr><td style="text-align:left">jgmc</td><td style="text-align:left">-0.99441195</td><td style="text-align:left">0.0030133333</td></tr></tbody></table>
<div class="language-sql codeBlockContainer_ljD1 theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_rhaG"><pre tabindex="0" class="prism-code language-sql codeBlock_mx9Q thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_NG8l"><span class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">SELECT</span><span class="token plain"> relname</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> reltuples</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> relpages</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">FROM</span><span class="token plain"> pg_class</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">WHERE</span><span class="token plain"> relname </span><span class="token operator" style="color:rgb(137, 221, 255)">LIKE</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">'organizations'</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><br></span></code></pre></div></div>















<table><thead><tr><th style="text-align:left">relname</th><th style="text-align:left">reltuples</th><th style="text-align:left">relpages</th></tr></thead><tbody><tr><td style="text-align:left">organizations</td><td style="text-align:left">3686376</td><td style="text-align:left">1116103</td></tr></tbody></table>
<h2 class="anchor anchorWithStickyNavbar_EG6R" id="参考">参考<a href="https://wener.me/story/postgresql-use-wrong-index-with-order-and-limit#%E5%8F%82%E8%80%83" class="hash-link" aria-label="Direct link to 参考" title="Direct link to 参考" translate="no">​</a></h2>
<ul>
<li><a href="https://gocardless.com/blog/debugging-the-postgres-query-planner/" target="_blank" rel="noopener noreferrer">https://gocardless.com/blog/debugging-the-postgres-query-planner/</a></li>
<li><a href="https://dba.stackexchange.com/a/258986/234272" target="_blank" rel="noopener noreferrer">https://dba.stackexchange.com/a/258986/234272</a></li>
</ul>]]></content:encoded>
            <category>PostgreSQL</category>
            <category>DevOps</category>
        </item>
    </channel>
</rss>