<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Posts | 有志者事竟成</title><link>https://www.liwenshen.com/post/</link><atom:link href="https://www.liwenshen.com/post/index.xml" rel="self" type="application/rss+xml"/><description>Posts</description><generator>Hugo Blox Builder (https://hugoblox.com)</generator><language>zh</language><lastBuildDate>Sun, 09 Nov 2025 23:50:42 +0800</lastBuildDate><image><url>https://www.liwenshen.com/media/icon_hu_dd5d76fef920c49e.png</url><title>Posts</title><link>https://www.liwenshen.com/post/</link></image><item><title>PostgreSQL Group By 不走索引？另类解决方案</title><link>https://www.liwenshen.com/post/pgsql-groupby-not-using-index/</link><pubDate>Sun, 09 Nov 2025 23:50:42 +0800</pubDate><guid>https://www.liwenshen.com/post/pgsql-groupby-not-using-index/</guid><description>&lt;p&gt;PostgreSQL版本为18，MySQL版本为8.0。&lt;/p&gt;
&lt;h1 id="postgresql表现"&gt;PostgreSQL表现&lt;/h1&gt;
&lt;p&gt;PostgreSQL建一张表，插入1000万条数据，content字段为为107个字符串之一&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;create&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;table&lt;/span&gt; test1(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; id bigint &lt;span style="color:#66d9ef"&gt;GENERATED&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;BY&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;DEFAULT&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;AS&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;IDENTITY&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;PRIMARY&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;KEY&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; content varchar(&lt;span style="color:#ae81ff"&gt;100&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;CREATE&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;INDEX&lt;/span&gt; test_idx &lt;span style="color:#66d9ef"&gt;ON&lt;/span&gt; test1 &lt;span style="color:#66d9ef"&gt;USING&lt;/span&gt; btree (content);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;insert&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;into&lt;/span&gt; test1(content)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;select&lt;/span&gt; REPEAT(((random() &lt;span style="color:#f92672"&gt;*&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;100000&lt;/span&gt;)::bigint &lt;span style="color:#f92672"&gt;%&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;107&lt;/span&gt;)::text, &lt;span style="color:#ae81ff"&gt;11&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;from&lt;/span&gt; generate_series(&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;10000000&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;drop&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;index&lt;/span&gt; test_idx;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;create&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;index&lt;/span&gt; test_idx &lt;span style="color:#66d9ef"&gt;on&lt;/span&gt; test1(content);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;vacuum&lt;/span&gt; test1;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;analyze&lt;/span&gt; test1;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;执行语句如下：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;set&lt;/span&gt; max_parallel_workers_per_gather &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;; &lt;span style="color:#75715e"&gt;-- 禁用并行扫描
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;set&lt;/span&gt; jit &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;off&lt;/span&gt;; &lt;span style="color:#75715e"&gt;-- 关闭JIT
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;explain&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;analyze&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;verbose&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;select&lt;/span&gt; content
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;from&lt;/span&gt; test1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;group&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;by&lt;/span&gt; content;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;得到的执行计划如下，耗时2.8s：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;HashAggregate (cost=229831.01..229832.08 rows=107 width=22) (actual time=2830.261..2830.270 rows=107.00 loops=1)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Output: content
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Group Key: test1.content
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Batches: 1 Memory Usage: 32kB
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Buffers: shared hit=92331
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -&amp;gt; Seq Scan on test.test1 (cost=0.00..202331.01 rows=11000001 width=22) (actual time=0.014..752.663 rows=11000001.00 loops=1)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Output: id, content
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Buffers: shared hit=92331
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Planning Time: 0.093 ms
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Execution Time: 2830.309 ms
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;强制走索引&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;set&lt;/span&gt; enable_seqscan&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;false&lt;/span&gt;; &lt;span style="color:#75715e"&gt;-- 禁止全表扫
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;set&lt;/span&gt; max_parallel_workers_per_gather &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;; &lt;span style="color:#75715e"&gt;-- 禁用并行扫描
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;set&lt;/span&gt; jit &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;off&lt;/span&gt;; &lt;span style="color:#75715e"&gt;-- 关闭JIT
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;explain&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;analyze&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;verbose&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;select&lt;/span&gt; content
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;from&lt;/span&gt; test1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;group&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;by&lt;/span&gt; content;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;得到的执行计划如下，耗时1.4s：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Group (cost=0.43..209778.68 rows=107 width=22) (actual time=0.055..1434.377 rows=107.00 loops=1)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Output: content
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Group Key: test1.content
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Buffers: shared hit=321 read=8628
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -&amp;gt; Index Only Scan using test_idx on test.test1 (cost=0.43..184779.50 rows=9999671 width=22) (actual time=0.054..810.491 rows=10000000.00 loops=1)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Output: content
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Heap Fetches: 0
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Index Searches: 1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Buffers: shared hit=321 read=8628
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Planning:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Buffers: shared hit=24
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Planning Time: 0.129 ms
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Execution Time: 1434.431 ms
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;可用看出，PostgreSQL即使强制走索引，虽然查询速度虽然有提升，但查询速度还是和表数据量有关，不能秒出结果。&lt;/p&gt;
&lt;h1 id="mysql表现"&gt;MySQL表现&lt;/h1&gt;
&lt;p&gt;建表插入数据约800万条：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;create&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;table&lt;/span&gt; test1(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; id bigint &lt;span style="color:#66d9ef"&gt;not&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;NULL&lt;/span&gt; AUTO_INCREMENT &lt;span style="color:#66d9ef"&gt;PRIMARY&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;KEY&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; content VARCHAR(&lt;span style="color:#ae81ff"&gt;100&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;-- 随便插入一条数据后
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;-- 反复执行该语句直到表里有800万条数据
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;INSERT&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;into&lt;/span&gt; test1 (content)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;select&lt;/span&gt; REPEAT(&lt;span style="color:#66d9ef"&gt;CONVERT&lt;/span&gt;(rand() &lt;span style="color:#f92672"&gt;*&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;100000&lt;/span&gt; &lt;span style="color:#f92672"&gt;%&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;107&lt;/span&gt;, UNSIGNED INTEGER), &lt;span style="color:#ae81ff"&gt;11&lt;/span&gt;) &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; x
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;from&lt;/span&gt; test1;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;drop&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;index&lt;/span&gt; test_idx &lt;span style="color:#66d9ef"&gt;on&lt;/span&gt; test1;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;create&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;index&lt;/span&gt; test_idx &lt;span style="color:#66d9ef"&gt;on&lt;/span&gt; test1(content);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;explain&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;analyze&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;select&lt;/span&gt; content
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;from&lt;/span&gt; test1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;group&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;by&lt;/span&gt; content;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;有索引的情况下，耗时1ms，秒出结果：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-&amp;gt; Covering index skip scan for deduplication on test1 using test_idx (cost=27458 rows=18305) (actual time=0.0868..1.26 rows=108 loops=1)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;没有索引的情况下，全表扫耗时5.3s：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-&amp;gt; Table scan on &amp;lt;temporary&amp;gt; (cost=1.66e+6..1.76e+6 rows=8.14e+6) (actual time=5395..5395 rows=108 loops=1)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -&amp;gt; Temporary table with deduplication (cost=1.66e+6..1.66e+6 rows=8.14e+6) (actual time=5395..5395 rows=108 loops=1)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -&amp;gt; Table scan on test1 (cost=842067 rows=8.14e+6) (actual time=0.946..2980 rows=8.39e+6 loops=1)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;显然这种简单group by+有索引的场景下，MySQL的查询速度和表数据量无关，比PostgreSQL要好。&lt;/p&gt;
&lt;p&gt;当然PostgreSQL也不是没有优点，显然其全表扫比MySQL要快很多，建索引的速度比MySQL也快(7s vs 40s)，还支持JIT和并行执行，这些都比MySQL强。&lt;/p&gt;
&lt;h1 id="旁门左道"&gt;旁门左道&lt;/h1&gt;
&lt;p&gt;那么PostgreSQL到底有一些偏方来快速解决“检索某一列的唯一不同值”这个问题呢，有的。&lt;/p&gt;
&lt;p&gt;思路打开一下：我们寄希望于Group By 能使用索引快速查询。究其原因，我们是希望不需要扫描整个索引，只扫描索引中我们需要的上层B树节点就行。&lt;/p&gt;
&lt;p&gt;我们完全可用手动实现这个过程，先取出这一列的当前值作为最小值，然后通过递归，不断从列中取得比当前值大的最小值并替换当前值，直到取完所有值。那么当前值取过的集合，就是唯一不同值的集合。&lt;/p&gt;
&lt;p&gt;显然，取最小值和查找比当前值大的最小值的这个过程，完全可以走索引的。我们可以用递归CTE实现这些步骤：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;explain&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;analyze&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;verbose&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;WITH&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;RECURSIVE&lt;/span&gt; cte &lt;span style="color:#66d9ef"&gt;AS&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;SELECT&lt;/span&gt; content
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;FROM&lt;/span&gt; test1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ORDER&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;BY&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;LIMIT&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;UNION&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;ALL&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;SELECT&lt;/span&gt; l.&lt;span style="color:#f92672"&gt;*&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;FROM&lt;/span&gt; cte &lt;span style="color:#66d9ef"&gt;c&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;CROSS&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;JOIN&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;LATERAL&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;SELECT&lt;/span&gt; t.content
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;FROM&lt;/span&gt; test1 t
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;WHERE&lt;/span&gt; t.content &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;c&lt;/span&gt;.content
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ORDER&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;BY&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;LIMIT&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ) l
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;SELECT&lt;/span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;FROM&lt;/span&gt; cte;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;查看执行计划，耗时不到1ms，只需0.8ms，非常完美：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;CTE Scan on cte (cost=50.06..52.08 rows=101 width=218) (actual time=0.020..0.770 rows=107.00 loops=1)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Output: cte.content
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Storage: Memory Maximum Storage: 22kB
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Buffers: shared hit=338
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; CTE cte
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -&amp;gt; Recursive Union (cost=0.43..50.06 rows=101 width=22) (actual time=0.019..0.735 rows=107.00 loops=1)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Storage: Memory Maximum Storage: 33kB
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Buffers: shared hit=338
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -&amp;gt; Limit (cost=0.43..0.45 rows=1 width=22) (actual time=0.018..0.018 rows=1.00 loops=1)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Output: test1.content
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Buffers: shared hit=4
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -&amp;gt; Index Only Scan using test_idx on compub.test1 (cost=0.43..184789.84 rows=10000360 width=22) (actual time=0.017..0.017 rows=1.00 loops=1)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Output: test1.content
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Heap Fetches: 0
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Index Searches: 1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Buffers: shared hit=4
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -&amp;gt; Nested Loop (cost=0.43..4.86 rows=10 width=22) (actual time=0.006..0.006 rows=0.99 loops=107)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Output: t.content
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Buffers: shared hit=334
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -&amp;gt; WorkTable Scan on cte c (cost=0.00..0.20 rows=10 width=218) (actual time=0.000..0.000 rows=1.00 loops=107)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Output: c.content
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -&amp;gt; Limit (cost=0.43..0.46 rows=1 width=22) (actual time=0.006..0.006 rows=0.99 loops=107)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Output: t.content
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Buffers: shared hit=334
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -&amp;gt; Index Only Scan using test_idx on compub.test1 t (cost=0.43..69931.86 rows=3333453 width=22) (actual time=0.006..0.006 rows=0.99 loops=107)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Output: t.content
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Index Cond: (t.content &amp;gt; (c.content)::text)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Heap Fetches: 0
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Index Searches: 107
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Buffers: shared hit=334
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Planning Time: 0.154 ms
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Execution Time: 0.798 ms
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id="旁门左道之旁门左道"&gt;旁门左道之旁门左道&lt;/h1&gt;
&lt;p&gt;然而某些国产数据库，不支持LATERAL join，只能改写为使用相关子查询实现了：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;explain&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;analyze&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;verbose&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;WITH&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;RECURSIVE&lt;/span&gt; cte &lt;span style="color:#66d9ef"&gt;AS&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;-- 锚点成员：取最小的content（同原逻辑）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;SELECT&lt;/span&gt; content
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;FROM&lt;/span&gt; test1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ORDER&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;BY&lt;/span&gt; content &lt;span style="color:#75715e"&gt;-- 显式按content排序，取最小的1个
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;LIMIT&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;UNION&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;ALL&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;-- 递归成员：用相关子查询找下一个值，用EXISTS控制递归终止
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;SELECT&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;-- 相关子查询：为当前c.content找最小的更大值
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;SELECT&lt;/span&gt; t.content
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;FROM&lt;/span&gt; test1 t
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;WHERE&lt;/span&gt; t.content &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;c&lt;/span&gt;.content &lt;span style="color:#75715e"&gt;-- 只找比当前值大的
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ORDER&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;BY&lt;/span&gt; t.content &lt;span style="color:#75715e"&gt;-- 排序后取最小的
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;LIMIT&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; &lt;span style="color:#75715e"&gt;-- 确保只返回1个值
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ) &lt;span style="color:#66d9ef"&gt;AS&lt;/span&gt; content
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;FROM&lt;/span&gt; cte &lt;span style="color:#66d9ef"&gt;c&lt;/span&gt; &lt;span style="color:#75715e"&gt;-- 每一次递归都是一行当前值
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;-- 过滤条件：只有存在更大值时才继续递归
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;WHERE&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;EXISTS&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;SELECT&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;FROM&lt;/span&gt; test1 t
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;WHERE&lt;/span&gt; t.content &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;c&lt;/span&gt;.content
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;SELECT&lt;/span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;FROM&lt;/span&gt; cte;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;测试结果相差不大，耗时1.1ms：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;CTE Scan on cte (cost=61.81..62.43 rows=31 width=218) (actual time=0.029..1.113 rows=107.00 loops=1)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Output: cte.content
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Storage: Memory Maximum Storage: 22kB
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Buffers: shared hit=669
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; CTE cte
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -&amp;gt; Recursive Union (cost=0.43..61.81 rows=31 width=218) (actual time=0.028..1.078 rows=107.00 loops=1)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Storage: Memory Maximum Storage: 33kB
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Buffers: shared hit=669
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -&amp;gt; Limit (cost=0.43..0.45 rows=1 width=22) (actual time=0.027..0.027 rows=1.00 loops=1)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Output: test1.content
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Buffers: shared hit=4
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -&amp;gt; Index Only Scan using test_idx on compub.test1 (cost=0.43..184789.84 rows=10000360 width=22) (actual time=0.026..0.026 rows=1.00 loops=1)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Output: test1.content
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Heap Fetches: 0
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Index Searches: 1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Buffers: shared hit=4
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -&amp;gt; Nested Loop Semi Join (cost=0.43..6.10 rows=3 width=218) (actual time=0.009..0.009 rows=0.99 loops=107)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Output: (SubPlan 1)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Buffers: shared hit=665
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -&amp;gt; WorkTable Scan on cte c (cost=0.00..0.20 rows=10 width=218) (actual time=0.000..0.000 rows=1.00 loops=107)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Output: c.content
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -&amp;gt; Index Only Scan using test_idx on compub.test1 t_1 (cost=0.43..61814.26 rows=3333453 width=22) (actual time=0.005..0.005 rows=0.99 loops=107)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Output: t_1.content
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Index Cond: (t_1.content &amp;gt; (c.content)::text)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Heap Fetches: 0
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Index Searches: 107
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Buffers: shared hit=334
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; SubPlan 1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -&amp;gt; Limit (cost=0.43..0.46 rows=1 width=22) (actual time=0.003..0.003 rows=1.00 loops=106)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Output: t.content
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Buffers: shared hit=331
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -&amp;gt; Index Only Scan using test_idx on compub.test1 t (cost=0.43..69931.86 rows=3333453 width=22) (actual time=0.003..0.003 rows=1.00 loops=106)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Output: t.content
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Index Cond: (t.content &amp;gt; (c.content)::text)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Heap Fetches: 0
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Index Searches: 106
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Buffers: shared hit=331
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Planning Time: 0.177 ms
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Execution Time: 1.153 ms
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;注意：递归CTE是有限制的，数据库也许会限制递归的深度，也许会爆栈，请仔细测试后再使用。&lt;/strong&gt;&lt;/p&gt;
&lt;h1 id="vacuum-full性能暴跌彩蛋"&gt;vacuum full性能暴跌彩蛋：&lt;/h1&gt;
&lt;p&gt;PostgreSQL中，如果对表进行vacuum full，然后强制其走索引，得到的执行计划如下：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Group (cost=0.43..600032.52 rows=107 width=22) (actual time=0.019..6437.286 rows=107.00 loops=1)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Output: content
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Group Key: test1.content
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Buffers: shared hit=6660031
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -&amp;gt; Index Only Scan using test_idx on test.test1 (cost=0.43..572532.52 rows=11000001 width=22) (actual time=0.018..5664.125 rows=11000001.00 loops=1)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Output: content
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Heap Fetches: 11000001
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Index Searches: 1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Buffers: shared hit=6660031
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Planning Time: 0.073 ms
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Execution Time: 6437.376 ms
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;根据&lt;a href="https://www.kimi.com/share/19a69855-87e2-857f-8000-0000a9c5cc40" target="_blank" rel="noopener"&gt;Kimi的解释&lt;/a&gt;：VACUUM FULL 后，Visibility Map 被清空，导致 Index-Only Scan 退化，必须回表查可见性，性能暴跌。&lt;/p&gt;
&lt;p&gt;所以不要轻易使用vacuum full，平时autovacuum和vacuum就够了，如果必须 VACUUM FULL，则需要之后手动跑一次 VACUUM 来修复 VM，就能恢复查询性能了。&lt;/p&gt;</description></item><item><title>Proxmox VE 网卡Hang解决方法</title><link>https://www.liwenshen.com/post/pve-e1000e-hang-solve/</link><pubDate>Thu, 23 Oct 2025 07:17:24 +0800</pubDate><guid>https://www.liwenshen.com/post/pve-e1000e-hang-solve/</guid><description>&lt;p&gt;家里的NAS安装了Proxmox VE，总是出现网卡Hang，导致NAS总是断线。&lt;/p&gt;
&lt;p&gt;网卡为I217-V：系统日志报错信息如下：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Oct &lt;span style="color:#ae81ff"&gt;20&lt;/span&gt; 00:00:07 pve kernel: e1000e 0000:00:19.0 enp0s25: Detected Hardware Unit Hang:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; TDH &amp;lt;4a&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; TDT &amp;lt;71&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; next_to_use &amp;lt;71&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; next_to_clean &amp;lt;49&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;buffer_info&lt;span style="color:#f92672"&gt;[&lt;/span&gt;next_to_clean&lt;span style="color:#f92672"&gt;]&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; time_stamp &amp;lt;123eee4c7&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; next_to_watch &amp;lt;4a&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; jiffies &amp;lt;13e43ccc0&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; next_to_watch.status &amp;lt;0&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;MAC Status &amp;lt;80083&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;PHY Status &amp;lt;796d&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;PHY 1000BASE-T Status &amp;lt;3800&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;PHY Extended Status &amp;lt;3000&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;PCI Status &amp;lt;10&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;经过测试，发现关掉网卡的部分offload功能可以有效缓解该问题。&lt;/p&gt;
&lt;p&gt;一次性执行命令（需要安装 ethtool）：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ethtool -K enp0s25 tso off gso off
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;开机执行：
修改/etc/network/interfaces文件，在出事的网口下添加post-up命令：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;iface enp0s25 inet manual
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; post-up ethtool -K $IFACE tso off gso off
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;确认是否关闭TSO和GSO：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ethtool -k enp0s25
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;确认一下2项为否为off&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tcp-segmentation-offload: off
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;generic-segmentation-offload: off
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>优化路由器连接参数</title><link>https://www.liwenshen.com/post/optimize-router-connections/</link><pubDate>Thu, 09 May 2024 00:27:57 +0800</pubDate><guid>https://www.liwenshen.com/post/optimize-router-connections/</guid><description>&lt;p&gt;摘录了&lt;a href="https://help.mikrotik.com/docs/display/ROS/Connection&amp;#43;tracking" target="_blank" rel="noopener"&gt;Mikrotik文档&lt;/a&gt;里的网络参数，并整理成Shell命令。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# mikrotik&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; &amp;gt; /proc/sys/net/netfilter/nf_conntrack_tcp_loose
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo &lt;span style="color:#ae81ff"&gt;5&lt;/span&gt; &amp;gt; /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_syn_sent
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo &lt;span style="color:#ae81ff"&gt;5&lt;/span&gt; &amp;gt; /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_syn_recv
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo &lt;span style="color:#ae81ff"&gt;86400&lt;/span&gt; &amp;gt; /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_established
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo &lt;span style="color:#ae81ff"&gt;10&lt;/span&gt; &amp;gt; /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_fin_wait
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo &lt;span style="color:#ae81ff"&gt;10&lt;/span&gt; &amp;gt; /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_close_wait
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo &lt;span style="color:#ae81ff"&gt;10&lt;/span&gt; &amp;gt; /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_last_ack
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo &lt;span style="color:#ae81ff"&gt;10&lt;/span&gt; &amp;gt; /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_time_wait
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo &lt;span style="color:#ae81ff"&gt;10&lt;/span&gt; &amp;gt; /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_close
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo &lt;span style="color:#ae81ff"&gt;10&lt;/span&gt; &amp;gt; /proc/sys/net/netfilter/nf_conntrack_udp_timeout
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo &lt;span style="color:#ae81ff"&gt;180&lt;/span&gt; &amp;gt; /proc/sys/net/netfilter/nf_conntrack_udp_timeout_stream
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo &lt;span style="color:#ae81ff"&gt;10&lt;/span&gt; &amp;gt; /proc/sys/net/netfilter/nf_conntrack_icmp_timeout
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo &lt;span style="color:#ae81ff"&gt;10&lt;/span&gt; &amp;gt; /proc/sys/net/netfilter/nf_conntrack_icmpv6_timeout
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo &lt;span style="color:#ae81ff"&gt;600&lt;/span&gt; &amp;gt; /proc/sys/net/netfilter/nf_conntrack_generic_timeout
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;额外再附上2条常见优化参数：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# conntrack&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &amp;gt; /proc/sys/net/netfilter/nf_conntrack_checksum
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; &amp;gt; /proc/sys/net/netfilter/nf_conntrack_tcp_be_liberal
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;放到路由器启动脚本里即可。&lt;/p&gt;
&lt;p&gt;在&lt;a href="https://gist.github.com/liwenshen2000/89d99cbe1f084de5708b30c13c23bdc6" target="_blank" rel="noopener"&gt;Gist&lt;/a&gt;中反馈意见&lt;/p&gt;</description></item><item><title>Windows上使用GPG配置Git和SSH</title><link>https://www.liwenshen.com/post/gpg-with-git-ssh-on-windows/</link><pubDate>Sat, 02 Dec 2023 22:49:58 +0800</pubDate><guid>https://www.liwenshen.com/post/gpg-with-git-ssh-on-windows/</guid><description>&lt;p&gt;记录一下在Windows上如何将GPG和Git，SSH结合起来使用。&lt;/p&gt;
&lt;h1 id="环境"&gt;环境&lt;/h1&gt;
&lt;p&gt;系统为Windows 11 23H2。&lt;/p&gt;
&lt;p&gt;SSH是Windows系统自带的OpenSSH，不是自行安装的，也不是Git Bash里的ssh。&lt;/p&gt;
&lt;p&gt;GPG是&lt;a href="https://www.gnupg.org/ftp/gcrypt/binary/" target="_blank" rel="noopener"&gt;gnupg-w32&lt;/a&gt;，由scoop安装，不是Git Bash里的gpg。&lt;/p&gt;
&lt;p&gt;SSH和GPG的版本如下：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt; ssh -V
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;OpenSSH_for_Windows_8.6p1, LibreSSL &lt;span style="color:#ae81ff"&gt;3.4&lt;/span&gt;.3
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt; gpg --version
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;gpg (GnuPG) &lt;span style="color:#ae81ff"&gt;2.4&lt;/span&gt;.3
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Home&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; C:\Users\liwenshen\scoop\apps\gpg\current\home
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;注意这里的GPG的Home文件夹，下文涉及的GPG的所有配置文件都放在这个Home文件夹中。&lt;/p&gt;
&lt;h1 id="准备工作"&gt;准备工作&lt;/h1&gt;
&lt;h2 id="git-配置"&gt;GIT 配置&lt;/h2&gt;
&lt;p&gt;Git在默认情况下，使用Git Bash里自带的ssh和gpg程序。
我们需要强制其使用Windows内置的SSH和gnupg-w32的gpg程序。&lt;/p&gt;
&lt;p&gt;先在PowerShell查看当前Windows下的程序路径：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt; (Get-Command ssh).Path
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;C:\Windows\System32\OpenSSH\ssh.exe
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;(Get-Command gpg).Path
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;C:\Users\liwenshen\scoop\apps\gpg\current\bin\gpg.exe
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;PS C:\Users\liwenshen&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;然后根据上面的路径，指定Git使用的程序：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git config --global core.sshCommand C:\\Windows\\System32\\OpenSSH\\ssh.exe
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git config --global gpg.program C:\\Users\\liwenshen\\scoop\\apps\\gpg\\current\\bin\\gpg.exe
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="密钥情况"&gt;密钥情况&lt;/h2&gt;
&lt;p&gt;查看密钥的基本情况：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt; gpg -k
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;C:\Users\liwenshen\scoop\apps\gpg\current\home\pubring.kbx
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;----------------------------------------------------------
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pub ed25519/82EF9F38F02BCA14 &lt;span style="color:#ae81ff"&gt;2023&lt;/span&gt;-&lt;span style="color:#ae81ff"&gt;06&lt;/span&gt;-&lt;span style="color:#ae81ff"&gt;04&lt;/span&gt; [&lt;span style="color:#66d9ef"&gt;C&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Key fingerprint = &lt;span style="color:#ae81ff"&gt;7460&lt;/span&gt; 79A9 &lt;span style="color:#ae81ff"&gt;2447&lt;/span&gt; E38D 1AF8 A7ED 82EF 9F38 F02B CA14
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Keygrip = 11C9D151D94FD5C4F5612CF1D79FB735E9EE08C6
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;uid [&lt;span style="color:#66d9ef"&gt;ultimate&lt;/span&gt;] liwenshen (git) &amp;lt;git@liwenshen.com&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sub ed25519/1B3B3B59E5427BF0 &lt;span style="color:#ae81ff"&gt;2023&lt;/span&gt;-&lt;span style="color:#ae81ff"&gt;06&lt;/span&gt;-&lt;span style="color:#ae81ff"&gt;04&lt;/span&gt; [&lt;span style="color:#66d9ef"&gt;SA&lt;/span&gt;] [expires&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2025&lt;/span&gt;-&lt;span style="color:#ae81ff"&gt;06&lt;/span&gt;-&lt;span style="color:#ae81ff"&gt;03&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Keygrip = E57DFA139A46760699AB482039CC3443EB0EAB22
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sub cv25519/9556AA6A5BCDE6EC &lt;span style="color:#ae81ff"&gt;2023&lt;/span&gt;-&lt;span style="color:#ae81ff"&gt;06&lt;/span&gt;-&lt;span style="color:#ae81ff"&gt;04&lt;/span&gt; [&lt;span style="color:#66d9ef"&gt;E&lt;/span&gt;] [expires&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2025&lt;/span&gt;-&lt;span style="color:#ae81ff"&gt;06&lt;/span&gt;-&lt;span style="color:#ae81ff"&gt;03&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Keygrip = 4DCA3FE1E8779FF9D1A6C773BD66E6CF8BE0FA63
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在Git签名中，需要使用S(Signing签名)类型子密钥。
在SSH连接认证中，需要使用A(Authenticating认证)类型子密钥。&lt;/p&gt;
&lt;p&gt;可以看到，在我的情况下，一个主密钥下挂两个子密钥。
其中第一个子密钥符合签名和认证的要求。因此就用这个子密钥进行SSH认证和Git签名。&lt;/p&gt;
&lt;p&gt;这个子密钥对应的ID是&lt;code&gt;1B3B3B59E5427BF0&lt;/code&gt;，Keygrip是&lt;code&gt;E57DFA139A46760699AB482039CC3443EB0EAB22&lt;/code&gt;。
还需要记一下主密钥的ID是&lt;code&gt;82EF9F38F02BCA14&lt;/code&gt;，后面需要用到这3个值。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;如果密钥ID和Keygrip显示不完全。
可以在&lt;strong&gt;gpg.conf&lt;/strong&gt;中加上下列内容后，重新执行&lt;code&gt;gpg -k&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;keyid-format long&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;fingerprint&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;with-keygrip&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;或者直接执行&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;gpg -k --keyid-format long --fingerprint --with-keygrip
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/blockquote&gt;
&lt;h1 id="ssh-认证"&gt;SSH 认证&lt;/h1&gt;
&lt;h2 id="gpg子密钥的ssh公钥"&gt;GPG子密钥的SSH公钥&lt;/h2&gt;
&lt;p&gt;使用子密钥的ID，导出子密钥的SSH格式公钥，将其粘贴至Github或者服务器等SSH远程服务器上。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt; gpg --export-ssh-key 1B3B3B59E5427BF0
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJXESXaFJse2EE4IJ8BVbwSoSLezT2uAHIDVz4N3mkhi openpgp&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt;0xE5427BF0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="gpg-agent"&gt;GPG agent&lt;/h2&gt;
&lt;p&gt;GPG Agent是SSH Agent的实现，可以将SSH登录的过程中的认证环节，委托给GPG进行认证。
因此想要在SSH连接中使用GPG密钥，需要先配置GPG agent。&lt;/p&gt;
&lt;h3 id="配置gpg-agent"&gt;配置GPG agent&lt;/h3&gt;
&lt;p&gt;网络上许多文章，都有用到wsl-ssh-pageant，还要书写各种的powershell启动脚本进行配置。
实际上没有必要，gnupg-w32已经内置了对Windows OpenSSH的支持。
只需要在&lt;strong&gt;gpg-agent.conf&lt;/strong&gt;中加上下列内容就可以开启支持。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;enable-ssh-support&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;enable-putty-support&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;enable-win32-openssh-support&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;然后将用来进行SSH认证的密钥的Keygrip写入&lt;strong&gt;sshcontrol&lt;/strong&gt;文件，如：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-txt" data-lang="txt"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;E57DFA139A46760699AB482039CC3443EB0EAB22
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;警告：&lt;strong&gt;sshcontrol&lt;/strong&gt;文件里新增Keygrip后务必换行，且新一行不能有空格！！！&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="运行gpg-agent"&gt;运行GPG Agent&lt;/h3&gt;
&lt;p&gt;以下两条命令，任选一条，启动GPG agent&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;gpgconf --launch gpg-agent
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;gpg-connect-agent /bye
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;以下两条命令，任选一条，可以关闭GPG agent&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;gpgconf --kill gpg-agent
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;gpg-connect-agent killagent /bye
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;为了自动启动GPG agent，可以将GPG agent启动命令，加入计划任务中，并设置开机启动。&lt;/p&gt;
&lt;h2 id="测试ssh"&gt;测试SSH&lt;/h2&gt;
&lt;p&gt;启动GPG agent后，执行命令，查询SSH能否正常使用GPG密钥。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt; ssh-add -L
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJXESXaFJse2EE4IJ8BVbwSoSLezT2uAHIDVz4N3mkhi (none)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;可以看到，这个公钥和上文&lt;code&gt;gpg --export-ssh-key&lt;/code&gt;导出的公钥是一致的，说明SSH已经可以使用GPG的子密钥了。&lt;/p&gt;
&lt;p&gt;再测试一下SSH连接Github或者其他远程服务器，出现下列相似内容说明SSH已经认证成功。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt; ssh -vT git@github.com
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;debug1&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; Next authentication method&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; publickey
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;debug1&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; Offering public key&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; (none) ED25519 SHA256&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt;6aRZPwhjxDNgVIaXwaE9US9ZvUON3kzepoNH976w5vY agent
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;debug1&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; Server accepts key&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; (none) ED25519 SHA256&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt;6aRZPwhjxDNgVIaXwaE9US9ZvUON3kzepoNH976w5vY agent
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;debug1&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; Authentication succeeded (publickey).
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Hi liwenshen2000! You&lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;ve successfully authenticated, but GitHub does not provide shell access.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;如果出现下列报错，请优先检查上文涉及的配置文件内容，如换行符类型，末尾是否换行等。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt; ssh-add -L
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;error fetching identities&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; agent refused operation
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/blockquote&gt;
&lt;h1 id="git-签名"&gt;Git 签名&lt;/h1&gt;
&lt;h2 id="gpg密钥的gpg公钥"&gt;GPG密钥的GPG公钥&lt;/h2&gt;
&lt;p&gt;将GPG密钥的公钥导出。
然后将输出内容粘贴至Github或者其他远程代码库等GPG配置中。
就可以让代码库在推送代码后，认可这个GPG密钥签名的commits和tags。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt; gpg --armor --export 82EF9F38F02BCA14
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-----BEGIN PGP PUBLIC KEY BLOCK-----
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-----END PGP PUBLIC KEY BLOCK-----
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;导出时是导出整个GPG密钥的公钥，包括主密钥和子密钥，因此无论使用的是主密钥ID还是子密钥ID，输出结果都是一样的。&lt;/p&gt;
&lt;h2 id="配置git"&gt;配置Git&lt;/h2&gt;
&lt;p&gt;配置Git的GPG签名较为简单，首先在Git里设置用于签名的密钥的ID：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git config --global user.signingkey 1B3B3B59E5427BF0!
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;我这里指定了一个具体的GPG密钥进行签名，因此需要在在密钥ID的末尾加上感叹号。
也可以使用主密钥，让Git自动选择其下的签名密钥用于签名，那么可以设置成主密钥的ID，不必加感叹号：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git config --global user.signingkey 82EF9F38F02BCA14
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;配置默认对所有commits和tags签名&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git config --global commit.gpgsign true
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git config --global tag.gpgSign true
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="延长密钥ttl"&gt;延长密钥TTL&lt;/h2&gt;
&lt;p&gt;在日常使用各种git软件中的过程中，后台会使用ssh定期pull仓库，这时候频繁会弹出密钥过期的提示，可以在&lt;strong&gt;gpg-agent.conf&lt;/strong&gt;加上以下配置，延长密钥的TTL。&lt;/p&gt;
&lt;p&gt;输入一次密码有效1天，7天强制要求输入一次密码。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# default-cache-ttl 86400&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# max-cache-ttl 604800&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;default-cache-ttl-ssh 86400&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;max-cache-ttl-ssh 604800&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id="后记"&gt;后记&lt;/h1&gt;
&lt;p&gt;实际上在Windows上使用GPG密钥进行Git签名和SSH认证，还是较为繁琐的。&lt;/p&gt;
&lt;p&gt;推荐使用&lt;a href="https://gpgfrontend.bktus.com/" target="_blank" rel="noopener"&gt;GpgFrontend&lt;/a&gt;等GUI前端进行GPG密钥生成和管理。&lt;/p&gt;
&lt;p&gt;如果仅仅是为了在Github上给自己加上一个小绿勾，可以使用更为简单易用的SSH密钥进行签名和认证&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;。&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;&lt;a href="https://docs.github.com/zh/authentication/managing-commit-signature-verification/telling-git-about-your-signing-key#telling-git-about-your-ssh-key" target="_blank" rel="noopener"&gt;Github文档-使用SSH密钥签名&lt;/a&gt;&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description></item><item><title>Mt7621 Vlan</title><link>https://www.liwenshen.com/post/mt7621-vlan/</link><pubDate>Wed, 23 Feb 2022 11:24:57 +0800</pubDate><guid>https://www.liwenshen.com/post/mt7621-vlan/</guid><description>&lt;p&gt;首先要明白VLAN是什么，VLAN本质上就是对网络上数据包进行标记和隔离，在二层生效（IP层）。&lt;/p&gt;
&lt;h4 id="vlan常识"&gt;VLAN常识&lt;/h4&gt;
&lt;p&gt;下面是一些术语。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;VLAN ID (vid)&lt;/code&gt; 标记数据流所用的ID，取值为1~4094，用来划分网络，只有VID相同，才能认为他们在同一个网络。
&lt;ul&gt;
&lt;li&gt;0和4095是保留值，不要使用。&lt;/li&gt;
&lt;li&gt;1是较为特殊值的值，不要轻易使用，除非你知道你自己再干什么。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Priority Code Point (prio)&lt;/code&gt; 包的优先级，取值为0（最低）~7（最高），我们并不关心这个值。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tag&lt;/code&gt; 字面翻译为 &lt;code&gt;标记&lt;/code&gt;，有多重含义，注意融汇贯通。
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;tag&lt;/code&gt; 作动词，指对包打上tag头&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tag&lt;/code&gt; 作形容词，意味着该包已经被打上tag头。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tag&lt;/code&gt; 作名词，指该包所携带的tag头，主要包括vid和prio。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;untag&lt;/code&gt; 与&lt;code&gt;tag&lt;/code&gt;含义相反
&lt;ul&gt;
&lt;li&gt;作名词，指将被打上tag的数据流的tag去掉。&lt;/li&gt;
&lt;li&gt;作形容词，指该包还未被tag。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Port VLAN ID (PVID)&lt;/code&gt; 每个端口（网口）特有的VID。
&lt;ul&gt;
&lt;li&gt;从该端口接受不带tag的包，都会被打上vid等于pvid的tag。&lt;/li&gt;
&lt;li&gt;vid等于pvid的tag的包从该端口发送，都会被untag。&lt;/li&gt;
&lt;li&gt;对于其他包，根据安全设置，要么原样流入流出，要么被丢弃。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;一些常识：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;VLAN32指由tag头中vid为32的包所构成的网络，其余数字同理。&lt;/li&gt;
&lt;li&gt;终端设备（普通电脑，手机，机顶盒等）未经特殊设置，只会使用untag的包。&lt;/li&gt;
&lt;li&gt;这里的流入流出，是站在交换机角度而言：包从某个端口流入，指交换机接受到包。&lt;/li&gt;
&lt;li&gt;网络包流入交换机后，必定携带tag，默认情况下端口的PVID值为1，这也是上文说不要轻易使用的原因。&lt;/li&gt;
&lt;li&gt;MT7621内部包括一个5口交换机，实际上上却有7个端口，其中两个CPU端口，具体情况看下面。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="vlan观察"&gt;VLAN观察&lt;/h4&gt;
&lt;p&gt;先ssh进入路由器运行命令，可以看见。
我使用的是Newifi3进行讲解。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;RT-N56U_B1 /home/root&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&lt;span style="color:#75715e"&gt;# switch vlan dump&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; vid portmap eg-tag eg-con stag ivl fid
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; 1111--1 uuuu--t &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; -
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt; ----1-1 ----u-t &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; -
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;4&lt;/span&gt; ---11-1 ---tt-t &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; -
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;PVID:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;port pvid prio matrix
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; 1111--1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; 1111--1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; 1111--1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; 11111-1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;4&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; ---11-1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;5&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; ------1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;6&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; 11111-1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;首先看 &lt;code&gt;PVID:&lt;/code&gt; 下的表格。&lt;/p&gt;
&lt;p&gt;在&lt;code&gt;port&lt;/code&gt;一列可以看见，路由器有0~6个端口7个端口。
根据观察，猜测和查询资料，&lt;strong&gt;0~6端口分别对应为LAN4，LAN3，LAN2，LAN1，WAN，CPU1，CPU2&lt;/strong&gt;。
如果不确定，可以使用&lt;code&gt;switch dump&lt;/code&gt;命令进行猜测和估计。&lt;/p&gt;
&lt;p&gt;显然，&lt;code&gt;pvid&lt;/code&gt;即每一个端口对应的pvid，无需赘述。
值得注意的是，&lt;code&gt;matrix&lt;/code&gt;的配置方式，&lt;code&gt;matrix&lt;/code&gt;，指当前端口需要和其他哪些端口进行关联，后面讲解如何计算。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;两个CPU端口某些路由器（如K2P）上可能分为CPU(LAN)和CPU(WAN)，后面再讲解区别。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;回头看上面命令返回结果的第一大部分，表头为vid, portmap，eg-tag的表格，只关注前三列，无需关注后4列。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; vid portmap eg-tag
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; 1111--1 uuuu--t
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt; ----1-1 ----u-t
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;4&lt;/span&gt; ---11-1 ---tt-t
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;每一行指明了对应VID的VLAN的行为方式。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;portmap&lt;/code&gt; 从左到右，分别指明当前VLAN是否使用的端口0~6。&lt;code&gt;1&lt;/code&gt; 表示使用该端口，&lt;code&gt;-&lt;/code&gt; 表示不使用该端口。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;eg-tag&lt;/code&gt; 从左到右，分别指明当前VLAN使用端口的模式。&lt;code&gt;u&lt;/code&gt; 表示untag，&lt;code&gt;t&lt;/code&gt; 表示tag，&lt;code&gt;-&lt;/code&gt; 表示不使用。&lt;code&gt;ut&lt;/code&gt; 应该与portmap中 &lt;code&gt;1&lt;/code&gt; 对应。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;h6 id="以vid为1的一行举例"&gt;以vid为1的一行举例：&lt;/h6&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;portmap&lt;/code&gt; 说明了VLAN1使用LAN1&lt;del&gt;4接口和CPU2接口。即LAN1&lt;/del&gt;4口和CPU2口。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;eg-tag&lt;/code&gt; 说明了对应端口的行为方式，
&lt;ul&gt;
&lt;li&gt;LAN1&lt;del&gt;4接受untag的包，进入交换机后会被认为是VLAN1的包。VLAN1的包在这LAN1&lt;/del&gt;4流出时会被untag。&lt;/li&gt;
&lt;li&gt;CPU2允许tag的VLAN1的数据流入流出。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;实际上，这构成一个4口局域网局域网。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;h6 id="以vid为2的一行举例"&gt;以vid为2的一行举例：&lt;/h6&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;portmap&lt;/code&gt; 说明了VLAN2使用WAN接口和CPU2接口。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;eg-tag&lt;/code&gt; 说明了对应端口的行为方式：
&lt;ul&gt;
&lt;li&gt;WAN接受untag的包，进入交换机后会被认为是VLAN2的包。VLAN2的包在WAN流出时会被untag&lt;/li&gt;
&lt;li&gt;CPU2允许tag的VLAN2的数据流入流出&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;实际上，这构成了公网到路由器的连接。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;公网数据包从WAN经VLAN2进入CPU，经过处理（拨号，防火墙，NAT等），再经过VLAN1发出，使得连接LAN1~4的设备可以接受互联网数据，反之同理。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;注意：每一个端口只能有一个untag的VLAN，并且要和pvid对应&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;回头看，&lt;code&gt;matrix&lt;/code&gt; 参数设置，即设置当前端口可能与哪些端口进行数据交换。
计算方式为，只需要对&lt;code&gt;portmap&lt;/code&gt;中当前端口不为0的行，进行按位取或。&lt;/p&gt;
&lt;h4 id="vlan配置"&gt;VLAN配置&lt;/h4&gt;
&lt;p&gt;VLAN主要有以下几条命令&lt;/p&gt;
&lt;h5 id="配置各个vlan"&gt;配置各个VLAN&lt;/h5&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;switch vlan set &lt;span style="color:#f92672"&gt;[&lt;/span&gt;vid&lt;span style="color:#f92672"&gt;]&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;portmap&lt;span style="color:#f92672"&gt;]&lt;/span&gt; &amp;lt;stag&amp;gt; &amp;lt;eg_con&amp;gt; &amp;lt;eg_tag&amp;gt; - set vlan id and associated member
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;用于配置下面表头为 &lt;code&gt;vid portmap eg-tag eg-con stag ivl fid&lt;/code&gt; 的表格内容。
其中&lt;code&gt;&amp;lt;stag&amp;gt; &amp;lt;eg_con&amp;gt;&lt;/code&gt;皆设为0。&lt;/p&gt;
&lt;h5 id="配置端口pvid"&gt;配置端口pvid&lt;/h5&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;switch pvid &lt;span style="color:#f92672"&gt;[&lt;/span&gt;port&lt;span style="color:#f92672"&gt;]&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;pvid&lt;span style="color:#f92672"&gt;]&lt;/span&gt; - set pvid on port 0~6
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;用于修改端口的pvid的值。&lt;/p&gt;
&lt;h5 id="配置端口matrix"&gt;配置端口matrix&lt;/h5&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;switch reg r &lt;span style="color:#f92672"&gt;[&lt;/span&gt;offset&lt;span style="color:#f92672"&gt;]&lt;/span&gt; - register read from offset
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;switch reg w &lt;span style="color:#f92672"&gt;[&lt;/span&gt;offset&lt;span style="color:#f92672"&gt;]&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;value&lt;span style="color:#f92672"&gt;]&lt;/span&gt; - register write value to offset
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这里的 &lt;code&gt;offset&lt;/code&gt; 取值为0x2004，0x2104，0x2204，&amp;hellip;0x2604。注意只改变右起第3位的值，分别对应端口0~6的寄存器地址。&lt;/p&gt;
&lt;p&gt;而寄存器内值 &lt;code&gt;value&lt;/code&gt; 类似为&lt;code&gt;0x4f0003&lt;/code&gt;，其中，右起5,6位即为matrix的值。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;注意：matrix值在输入时应当反序，例如，LAN4口matrix值为&lt;code&gt;1111--1&lt;/code&gt;，那么我们对应的二进制反序后应为&lt;code&gt;1001111&lt;/code&gt;，十六进制值为&lt;code&gt;4f&lt;/code&gt;，故输入命令：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;switch reg w 0x2004 0x4f0003
&lt;/code&gt;&lt;/pre&gt;&lt;/blockquote&gt;
&lt;h3&gt;&lt;/h3&gt;
&lt;p&gt;我的配置脚本如下：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;switch vlan set &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1111001&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; uuuu--t
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;switch reg w 0x2004 0x4f0003
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;switch reg w 0x2104 0x4f0003
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;switch reg w 0x2204 0x4f0003
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;switch reg w 0x2304 0x5f0003
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;switch reg w 0x2604 0x5f0003
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;switch pvid &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="ap"&gt;AP&lt;/h2&gt;</description></item><item><title>配置Maven和Gradle镜像加速下载</title><link>https://www.liwenshen.com/post/gradle-mirror/</link><pubDate>Fri, 18 Jun 2021 16:33:03 +0800</pubDate><guid>https://www.liwenshen.com/post/gradle-mirror/</guid><description>&lt;p&gt;无论是普通Java项目还是Android项目，往往都在使用Gradle作为构建工具。但由于无论是Maven仓库，还是gradle releases二进制文件都在国外，因此下载一般都很慢，因此需要使用国内镜像替换这些源。&lt;/p&gt;
&lt;p&gt;因为Jcenter宣布关站（只读），我们只需使用阿里云的public仓库同时使用Maven Central和Jcenter仓库。&lt;/p&gt;
&lt;h3 id="maven配置镜像源"&gt;Maven配置镜像源&lt;/h3&gt;
&lt;p&gt;如果构建工具是Maven，那么修改&lt;code&gt;用户目录/.m2/settings.xml&lt;/code&gt;文件，设置为阿里云镜像：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;settings&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;xmlns=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;http://maven.apache.org/SETTINGS/1.0.0&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;xmlns:xsi=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;http://www.w3.org/2001/XMLSchema-instance&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;xsi:schemaLocation=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;http://maven.apache.org/SETTINGS/1.0.0 https://maven.apache.org/xsd/settings-1.0.0.xsd&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;localRepository/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;interactiveMode/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;offline/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;pluginGroups/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;servers/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;mirrors&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;mirror&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;aliyunmaven&lt;span style="color:#f92672"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;mirrorOf&amp;gt;&lt;/span&gt;*&lt;span style="color:#f92672"&gt;&amp;lt;/mirrorOf&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;name&amp;gt;&lt;/span&gt;阿里云公共仓库&lt;span style="color:#f92672"&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;url&amp;gt;&lt;/span&gt;https://maven.aliyun.com/repository/public&lt;span style="color:#f92672"&gt;&amp;lt;/url&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;/mirror&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;/mirrors&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;proxies/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;profiles/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;activeProfiles/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;/settings&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="gradle配置镜像源"&gt;Gradle配置镜像源&lt;/h3&gt;
&lt;p&gt;如果是Gradle作为构建工具，比如Android项目，那么就需要修改项目的gradle配置。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-gradle" data-lang="gradle"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; repositories &lt;span style="color:#f92672"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// mavenCentral()
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// jcenter()
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// google()
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; maven &lt;span style="color:#f92672"&gt;{&lt;/span&gt; url &lt;span style="color:#e6db74"&gt;&amp;#39;https://maven.aliyun.com/repository/google/&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; maven &lt;span style="color:#f92672"&gt;{&lt;/span&gt; url &lt;span style="color:#e6db74"&gt;&amp;#39;https://maven.aliyun.com/repository/public/&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="gradle-releases镜像"&gt;Gradle Releases镜像&lt;/h3&gt;
&lt;p&gt;每次打开一个Gradle的新项目时，都要下载一个Gradle的bin或all包，有时速度极为缓慢。&lt;/p&gt;
&lt;p&gt;打开&lt;code&gt;gradle/wrapper/gradle-wrapper.properties&lt;/code&gt;文件，修改其中的&lt;code&gt;distributionUrl&lt;/code&gt;键值对，保持后面的文件名不变，将原网址中替换成&lt;a href="https://mirrors.tencent.com/" target="_blank" rel="noopener"&gt;腾讯云gradle镜像&lt;/a&gt;地址，重新打开项目即可。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-properties" data-lang="properties"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;distributionUrl&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;https\://mirrors.cloud.tencent.com/gradle/gradle-6.5-all.zip&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item></channel></rss>