<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>性能优化 on 吾爱主机</title><link>https://blog.waihost.com/tags/%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/</link><description>Recent content in 性能优化 on 吾爱主机</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Sat, 01 Aug 2026 00:00:00 +0800</lastBuildDate><atom:link href="https://blog.waihost.com/tags/%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/index.xml" rel="self" type="application/rss+xml"/><item><title>PostgreSQL Autovacuum：触发机制、表膨胀与参数调优</title><link>https://blog.waihost.com/posts/postgresql-autovacuum-bloat-tuning/</link><pubDate>Sat, 01 Aug 2026 00:00:00 +0800</pubDate><guid>https://blog.waihost.com/posts/postgresql-autovacuum-bloat-tuning/</guid><description>&lt;img src="https://blog.waihost.com/images/covers/postgresql-autovacuum-bloat-tuning.svg" alt="Featured image of post PostgreSQL Autovacuum：触发机制、表膨胀与参数调优" /&gt;&lt;p&gt;PostgreSQL 使用 MVCC 让读写尽量互不阻塞，但它也带来一个容易被忽略的代价：&lt;code&gt;UPDATE&lt;/code&gt; 和 &lt;code&gt;DELETE&lt;/code&gt; 不会立刻从物理文件中移除旧版本。旧元组只有在不再可能被任何事务看到后，才能由 &lt;code&gt;VACUUM&lt;/code&gt; 清理并把空间标记为可复用。&lt;/p&gt;
&lt;p&gt;Autovacuum 不是“可有可无的后台整理器”，而是 PostgreSQL 稳定运行的基础设施。它同时承担回收死元组、更新优化器统计信息、维护可见性映射以及防止事务 ID 回卷等工作。本文从触发公式、监控 SQL和调优方法三个角度，解释如何避免表膨胀与查询性能逐步恶化。&lt;/p&gt;
&lt;h2 id="一为什么会产生死元组"&gt;&lt;a href="#%e4%b8%80%e4%b8%ba%e4%bb%80%e4%b9%88%e4%bc%9a%e4%ba%a7%e7%94%9f%e6%ad%bb%e5%85%83%e7%bb%84" class="header-anchor"&gt;&lt;/a&gt;一、为什么会产生死元组
&lt;/h2&gt;&lt;p&gt;假设执行：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;UPDATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;SET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;paid&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1001&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;PostgreSQL 通常不会在原位置覆盖旧行，而是创建新版本，并让旧版本在满足可见性条件前继续存在。这样，较早开始的事务仍可读取它应该看到的快照。&lt;/p&gt;
&lt;p&gt;当旧版本不再对任何事务可见时，它就成为 dead tuple。若清理速度长期低于写入速度，会出现：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;表和索引占用空间持续增加；&lt;/li&gt;
&lt;li&gt;顺序扫描需要读取更多数据页；&lt;/li&gt;
&lt;li&gt;缓存命中效率下降，磁盘 I/O 增加；&lt;/li&gt;
&lt;li&gt;优化器统计信息过旧，可能选择错误执行计划；&lt;/li&gt;
&lt;li&gt;维护操作耗时越来越长。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;需要注意：普通 &lt;code&gt;VACUUM&lt;/code&gt; 主要把空间交还给表内部复用，通常不会立即缩小操作系统看到的文件。&lt;code&gt;VACUUM FULL&lt;/code&gt; 会重写整张表并归还空间，但需要额外磁盘空间和排他锁，因此不适合作为日常维护手段。&lt;/p&gt;
&lt;h2 id="二autovacuum-的触发公式"&gt;&lt;a href="#%e4%ba%8cautovacuum-%e7%9a%84%e8%a7%a6%e5%8f%91%e5%85%ac%e5%bc%8f" class="header-anchor"&gt;&lt;/a&gt;二、Autovacuum 的触发公式
&lt;/h2&gt;&lt;p&gt;对更新和删除产生的死元组，触发阈值可近似理解为：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;vacuum 触发数 = autovacuum_vacuum_threshold
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; + autovacuum_vacuum_scale_factor × 表估算行数
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;PostgreSQL 18 的默认值为：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;autovacuum_vacuum_threshold = 50
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;autovacuum_vacuum_scale_factor = 0.2
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;一张估算为 1000 万行的表，默认需要约 200 万个更新或删除元组才触发一次 vacuum：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;50 + 0.2 × 10,000,000 = 2,000,050
&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" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;analyze 触发数 = autovacuum_analyze_threshold
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; + autovacuum_analyze_scale_factor × 表估算行数
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;默认 &lt;code&gt;autovacuum_analyze_threshold&lt;/code&gt; 为 50，&lt;code&gt;autovacuum_analyze_scale_factor&lt;/code&gt; 为 0.1。&lt;/p&gt;
&lt;h2 id="三先监控再调参数"&gt;&lt;a href="#%e4%b8%89%e5%85%88%e7%9b%91%e6%8e%a7%e5%86%8d%e8%b0%83%e5%8f%82%e6%95%b0" class="header-anchor"&gt;&lt;/a&gt;三、先监控，再调参数
&lt;/h2&gt;&lt;h3 id="1-找出死元组较多的表"&gt;&lt;a href="#1-%e6%89%be%e5%87%ba%e6%ad%bb%e5%85%83%e7%bb%84%e8%be%83%e5%a4%9a%e7%9a%84%e8%a1%a8" class="header-anchor"&gt;&lt;/a&gt;1. 找出死元组较多的表
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;schemaname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;relname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;n_live_tup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;n_dead_tup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;n_dead_tup&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NULLIF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n_live_tup&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;n_dead_tup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;dead_pct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;last_autovacuum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;last_autoanalyze&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;autovacuum_count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;autoanalyze_count&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pg_stat_user_tables&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;ORDER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;n_dead_tup&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;LIMIT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;不要只盯着 &lt;code&gt;dead_pct&lt;/code&gt;：百万行大表的 5% 可能已经很多，而几十行小表的 50% 通常影响有限。应同时观察绝对数量、增长速度、表尺寸和业务延迟。&lt;/p&gt;
&lt;h3 id="2-查看表与索引大小"&gt;&lt;a href="#2-%e6%9f%a5%e7%9c%8b%e8%a1%a8%e4%b8%8e%e7%b4%a2%e5%bc%95%e5%a4%a7%e5%b0%8f" class="header-anchor"&gt;&lt;/a&gt;2. 查看表与索引大小
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nspname&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;schema_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;relname&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;table_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pg_size_pretty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pg_table_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;oid&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;table_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pg_size_pretty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pg_indexes_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;oid&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;indexes_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pg_size_pretty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pg_total_relation_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;oid&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;total_size&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pg_class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;JOIN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pg_namespace&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;oid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;relnamespace&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;relkind&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;r&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AND&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nspname&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NOT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;pg_catalog&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;information_schema&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;ORDER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pg_total_relation_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;oid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;LIMIT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;pg_stat_user_tables&lt;/code&gt; 中的元组数是统计估算，不是严格实时计数。累计统计可能有短暂延迟，而且默认在同一事务中读取时会缓存到事务结束，因此排障时应避免在长事务里反复查询后误判数据没有变化。&lt;/p&gt;
&lt;h3 id="3-检查-autovacuum-是否正在工作"&gt;&lt;a href="#3-%e6%a3%80%e6%9f%a5-autovacuum-%e6%98%af%e5%90%a6%e6%ad%a3%e5%9c%a8%e5%b7%a5%e4%bd%9c" class="header-anchor"&gt;&lt;/a&gt;3. 检查 Autovacuum 是否正在工作
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;datname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;relid&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;regclass&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;table_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;phase&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;heap_blks_total&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;heap_blks_scanned&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;heap_blks_vacuumed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index_vacuum_count&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pg_stat_progress_vacuum&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;同时检查全局配置：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SHOW&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;autovacuum&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SHOW&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;track_counts&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SHOW&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;autovacuum_max_workers&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SHOW&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;autovacuum_naptime&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SHOW&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;autovacuum_vacuum_cost_delay&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SHOW&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;autovacuum_vacuum_cost_limit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="四针对热点大表做局部调优"&gt;&lt;a href="#%e5%9b%9b%e9%92%88%e5%af%b9%e7%83%ad%e7%82%b9%e5%a4%a7%e8%a1%a8%e5%81%9a%e5%b1%80%e9%83%a8%e8%b0%83%e4%bc%98" class="header-anchor"&gt;&lt;/a&gt;四、针对热点大表做局部调优
&lt;/h2&gt;&lt;p&gt;相比一开始就修改全局配置，更稳妥的方式是为写入最频繁的表设置存储参数：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;SET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;autovacuum_vacuum_scale_factor&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;02&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;autovacuum_vacuum_threshold&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;autovacuum_analyze_scale_factor&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;02&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;autovacuum_analyze_threshold&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;若 &lt;code&gt;orders&lt;/code&gt; 有 1000 万行，新的 vacuum 触发值约为：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;1000 + 0.02 × 10,000,000 = 201,000
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这比默认约 200 万行更及时，同时不会让所有表都采用同样激进的策略。调整后至少持续观察一个完整业务周期：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;n_dead_tup&lt;/code&gt; 是否形成可控波动而非持续上升；&lt;/li&gt;
&lt;li&gt;autovacuum 的执行次数与单次持续时间；&lt;/li&gt;
&lt;li&gt;数据盘 IOPS、吞吐和延迟；&lt;/li&gt;
&lt;li&gt;SQL 的 P95/P99 延迟；&lt;/li&gt;
&lt;li&gt;表和索引尺寸是否趋于稳定。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="五为什么调低阈值仍可能无效"&gt;&lt;a href="#%e4%ba%94%e4%b8%ba%e4%bb%80%e4%b9%88%e8%b0%83%e4%bd%8e%e9%98%88%e5%80%bc%e4%bb%8d%e5%8f%af%e8%83%bd%e6%97%a0%e6%95%88" class="header-anchor"&gt;&lt;/a&gt;五、为什么“调低阈值”仍可能无效
&lt;/h2&gt;&lt;h3 id="1-长事务阻止清理"&gt;&lt;a href="#1-%e9%95%bf%e4%ba%8b%e5%8a%a1%e9%98%bb%e6%ad%a2%e6%b8%85%e7%90%86" class="header-anchor"&gt;&lt;/a&gt;1. 长事务阻止清理
&lt;/h3&gt;&lt;p&gt;只要某个老快照仍可能看见旧版本，VACUUM 就不能安全移除它。可先排查长时间未结束的事务：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;usename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;xact_start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;xact_start&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;xact_age&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;wait_event_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;wait_event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;left&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pg_stat_activity&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;xact_start&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NOT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;ORDER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;xact_start&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;应用连接池中的 &lt;code&gt;idle in transaction&lt;/code&gt;、未提交的批处理、长期保持的报表事务，都是常见根因。此时继续降低 scale factor，只会让 vacuum 更频繁地尝试，却未必能回收空间。&lt;/p&gt;
&lt;h3 id="2-清理能力跟不上写入速度"&gt;&lt;a href="#2-%e6%b8%85%e7%90%86%e8%83%bd%e5%8a%9b%e8%b7%9f%e4%b8%8d%e4%b8%8a%e5%86%99%e5%85%a5%e9%80%9f%e5%ba%a6" class="header-anchor"&gt;&lt;/a&gt;2. 清理能力跟不上写入速度
&lt;/h3&gt;&lt;p&gt;Autovacuum 使用基于成本的延迟机制来降低后台维护对前台业务的 I/O 冲击。如果多张大表同时高频更新，worker 数量、成本限额或磁盘能力可能成为瓶颈。&lt;/p&gt;
&lt;p&gt;调优顺序建议是：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;先解决长事务和异常写入；&lt;/li&gt;
&lt;li&gt;针对热点表降低触发阈值；&lt;/li&gt;
&lt;li&gt;确认磁盘有余量后，再评估 worker 数量和成本参数；&lt;/li&gt;
&lt;li&gt;每次只改一组参数，并保留前后监控数据。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;不要只为“让 vacuum 尽快结束”就无限提高成本限额，否则维护 I/O 可能直接影响在线查询。&lt;/p&gt;
&lt;h3 id="3-误把内部可复用空间当成故障"&gt;&lt;a href="#3-%e8%af%af%e6%8a%8a%e5%86%85%e9%83%a8%e5%8f%af%e5%a4%8d%e7%94%a8%e7%a9%ba%e9%97%b4%e5%bd%93%e6%88%90%e6%95%85%e9%9a%9c" class="header-anchor"&gt;&lt;/a&gt;3. 误把内部可复用空间当成故障
&lt;/h3&gt;&lt;p&gt;普通 VACUUM 后文件不缩小，并不表示它没有工作。若表仍会持续写入，保留并复用这些空闲空间往往比频繁执行 &lt;code&gt;VACUUM FULL&lt;/code&gt; 更合理。&lt;/p&gt;
&lt;p&gt;只有在一次性大规模删除、确认未来不会很快重新增长，并且能够安排锁表维护窗口时，才考虑 &lt;code&gt;VACUUM FULL&lt;/code&gt;、&lt;code&gt;CLUSTER&lt;/code&gt; 或其他表重写方案。执行前必须预留接近表及索引重建所需的额外空间。&lt;/p&gt;
&lt;h2 id="六生产调优清单"&gt;&lt;a href="#%e5%85%ad%e7%94%9f%e4%ba%a7%e8%b0%83%e4%bc%98%e6%b8%85%e5%8d%95" class="header-anchor"&gt;&lt;/a&gt;六、生产调优清单
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;保持 autovacuum 开启，不要把关闭它当成性能优化。&lt;/li&gt;
&lt;li&gt;用 &lt;code&gt;pg_stat_user_tables&lt;/code&gt; 建立死元组数量、比例和清理时间的趋势图。&lt;/li&gt;
&lt;li&gt;优先为热点大表设置表级参数，避免粗暴修改全局阈值。&lt;/li&gt;
&lt;li&gt;同时监控磁盘 I/O 和业务延迟，防止维护任务挤压前台负载。&lt;/li&gt;
&lt;li&gt;排查长事务、复制槽和其他可能维持旧快照的因素。&lt;/li&gt;
&lt;li&gt;将普通 VACUUM 作为日常机制，把表重写当作有维护窗口的例外操作。&lt;/li&gt;
&lt;li&gt;定期检查事务 ID 年龄，不能只关注磁盘膨胀。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="总结"&gt;&lt;a href="#%e6%80%bb%e7%bb%93" class="header-anchor"&gt;&lt;/a&gt;总结
&lt;/h2&gt;&lt;p&gt;Autovacuum 的本质不是“定时清垃圾”，而是根据表的变更规模动态触发的一组维护任务。真正有效的优化，不是简单把参数调得更激进，而是让触发频率、清理吞吐和业务写入速度达到平衡。&lt;/p&gt;
&lt;p&gt;面对表膨胀时，应先回答三个问题：死元组为什么持续产生、为什么未被及时清理、普通 VACUUM 回收的空间是否已经被业务复用。把这些问题与长事务、I/O 能力和表级阈值结合起来分析，通常比直接执行 &lt;code&gt;VACUUM FULL&lt;/code&gt; 更安全，也更接近根因。&lt;/p&gt;
&lt;h2 id="参考资料"&gt;&lt;a href="#%e5%8f%82%e8%80%83%e8%b5%84%e6%96%99" class="header-anchor"&gt;&lt;/a&gt;参考资料
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://www.postgresql.org/docs/current/routine-vacuuming.html" target="_blank" rel="noopener"
 &gt;PostgreSQL 18 官方文档：Routine Vacuuming&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.postgresql.org/docs/current/runtime-config-vacuum.html" target="_blank" rel="noopener"
 &gt;PostgreSQL 18 官方文档：Vacuuming 配置参数&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.postgresql.org/docs/current/monitoring-stats.html" target="_blank" rel="noopener"
 &gt;PostgreSQL 18 官方文档：累计统计系统&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.postgresql.org/docs/current/sql-vacuum.html" target="_blank" rel="noopener"
 &gt;PostgreSQL 18 官方文档：VACUUM&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>PostgreSQL B-tree 索引：结构、扫描方式与工程实践</title><link>https://blog.waihost.com/posts/postgresql-btree-index-scan-practice/</link><pubDate>Fri, 24 Jul 2026 00:00:00 +0800</pubDate><guid>https://blog.waihost.com/posts/postgresql-btree-index-scan-practice/</guid><description>&lt;img src="https://blog.waihost.com/images/covers/postgresql-btree-index-scan-practice.svg" alt="Featured image of post PostgreSQL B-tree 索引：结构、扫描方式与工程实践" /&gt;&lt;p&gt;线上 SQL 变慢时，第一反应往往是“加个索引”。但在 PostgreSQL 里，索引不是“有就快”的开关：默认的 &lt;strong&gt;B-tree&lt;/strong&gt; 适合哪些谓词？多列索引为什么“左边优先”？为什么 &lt;code&gt;EXPLAIN&lt;/code&gt; 里会出现 &lt;code&gt;Index Scan&lt;/code&gt;、&lt;code&gt;Bitmap Heap Scan&lt;/code&gt; 或 &lt;code&gt;Index Only Scan&lt;/code&gt;？部分索引、&lt;code&gt;INCLUDE&lt;/code&gt; 覆盖列、&lt;code&gt;CREATE INDEX CONCURRENTLY&lt;/code&gt; 又分别解决什么工程问题？&lt;/p&gt;
&lt;p&gt;本文基于 PostgreSQL 官方文档（当前文档站对应 &lt;strong&gt;PostgreSQL 18&lt;/strong&gt; 手册章节），从机制讲到可复现实验与排错清单，帮助你把索引用在真正该用的地方。&lt;/p&gt;
&lt;h2 id="问题背景没有索引时数据库在做什么"&gt;&lt;a href="#%e9%97%ae%e9%a2%98%e8%83%8c%e6%99%af%e6%b2%a1%e6%9c%89%e7%b4%a2%e5%bc%95%e6%97%b6%e6%95%b0%e6%8d%ae%e5%ba%93%e5%9c%a8%e5%81%9a%e4%bb%80%e4%b9%88" class="header-anchor"&gt;&lt;/a&gt;问题背景：没有索引时，数据库在做什么
&lt;/h2&gt;&lt;p&gt;官方索引导论用了一个很直观的例子：表 &lt;code&gt;test1(id, content)&lt;/code&gt; 上频繁执行：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;test1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;constant&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果没有预先准备，系统只能 &lt;strong&gt;逐行扫描整表&lt;/strong&gt; 找匹配行；行数很大而命中很少时，这显然低效。若在 &lt;code&gt;id&lt;/code&gt; 上维护索引，定位匹配行可以走更高效的路径（例如在搜索树中只走几层），而不必通读整本书式的堆表。&lt;/p&gt;
&lt;p&gt;关键工程含义：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;索引要提前“预见”查询会怎样查找数据——这是 DBA/开发者的职责，而不是数据库自动替你猜对所有场景。&lt;/li&gt;
&lt;li&gt;索引创建后会随表的增删改自动维护，但也会带来 &lt;strong&gt;写放大与存储开销&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;规划器是否选用索引，依赖统计信息；因此要定期 &lt;code&gt;ANALYZE&lt;/code&gt;，并学会用 &lt;code&gt;EXPLAIN&lt;/code&gt; 验证真实计划。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="核心原理为什么日常几乎都是-b-tree"&gt;&lt;a href="#%e6%a0%b8%e5%bf%83%e5%8e%9f%e7%90%86%e4%b8%ba%e4%bb%80%e4%b9%88%e6%97%a5%e5%b8%b8%e5%87%a0%e4%b9%8e%e9%83%bd%e6%98%af-b-tree" class="header-anchor"&gt;&lt;/a&gt;核心原理：为什么日常几乎都是 B-tree
&lt;/h2&gt;&lt;p&gt;PostgreSQL 提供多种索引访问方法：&lt;code&gt;B-tree&lt;/code&gt;、&lt;code&gt;Hash&lt;/code&gt;、&lt;code&gt;GiST&lt;/code&gt;、&lt;code&gt;SP-GiST&lt;/code&gt;、&lt;code&gt;GIN&lt;/code&gt;、&lt;code&gt;BRIN&lt;/code&gt;，以及扩展如 &lt;code&gt;bloom&lt;/code&gt;。&lt;br&gt;
&lt;strong&gt;&lt;code&gt;CREATE INDEX&lt;/code&gt; 默认创建 B-tree&lt;/strong&gt;，因为它覆盖了最常见的相等与范围查询场景。其它类型需要显式 &lt;code&gt;USING method&lt;/code&gt;，例如：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;INDEX&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;table&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;USING&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HASH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;column&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="b-tree-能加速哪些谓词"&gt;&lt;a href="#b-tree-%e8%83%bd%e5%8a%a0%e9%80%9f%e5%93%aa%e4%ba%9b%e8%b0%93%e8%af%8d" class="header-anchor"&gt;&lt;/a&gt;B-tree 能加速哪些谓词
&lt;/h3&gt;&lt;p&gt;官方说明：B-tree 可处理 &lt;strong&gt;可排序数据&lt;/strong&gt; 上的相等与范围查询。规划器会在索引列参与下列比较时考虑使用 B-tree：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;运算符 / 形态&lt;/th&gt;
					&lt;th&gt;说明&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;&amp;lt;&lt;/code&gt; &lt;code&gt;&amp;lt;=&lt;/code&gt; &lt;code&gt;=&lt;/code&gt; &lt;code&gt;&amp;gt;=&lt;/code&gt; &lt;code&gt;&amp;gt;&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;基本比较&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;BETWEEN&lt;/code&gt; / &lt;code&gt;IN&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;可视为上述比较的组合&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;IS NULL&lt;/code&gt; / &lt;code&gt;IS NOT NULL&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;也可用于 B-tree&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;LIKE&lt;/code&gt; / &lt;code&gt;~&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;仅当模式为常量且 &lt;strong&gt;锚定在字符串开头&lt;/strong&gt;，如 &lt;code&gt;col LIKE 'foo%'&lt;/code&gt;、&lt;code&gt;col ~ '^foo'&lt;/code&gt;；&lt;code&gt;'%bar'&lt;/code&gt; 这类前后缀/中间匹配不行&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;补充：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;非 &lt;code&gt;C&lt;/code&gt; locale 时，模式匹配类查询可能需要特殊 &lt;strong&gt;operator class&lt;/strong&gt; 才能用索引（见手册“Operator Classes and Operator Families”相关章节）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ILIKE&lt;/code&gt; / &lt;code&gt;~*&lt;/code&gt; 更受限：模式通常需要以“不受大小写转换影响的字符”开头。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hash 索引&lt;/strong&gt; 只存 32-bit 哈希码，因此 &lt;strong&gt;只能处理简单相等比较&lt;/strong&gt;，不能替代 B-tree 的范围与排序能力。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="只有-b-tree-能带着顺序吐行"&gt;&lt;a href="#%e5%8f%aa%e6%9c%89-b-tree-%e8%83%bd%e5%b8%a6%e7%9d%80%e9%a1%ba%e5%ba%8f%e5%90%90%e8%a1%8c" class="header-anchor"&gt;&lt;/a&gt;只有 B-tree 能“带着顺序”吐行
&lt;/h3&gt;&lt;p&gt;索引除了找行，还可能直接按指定顺序交付结果，从而避免额外排序。官方明确：在当前支持的索引类型中，&lt;strong&gt;只有 B-tree 能产生有序输出&lt;/strong&gt;；其它类型返回匹配行的顺序是未指定、实现相关的。&lt;/p&gt;
&lt;p&gt;默认 B-tree 条目按 &lt;strong&gt;升序、NULLS LAST&lt;/strong&gt; 存储（相等时用表 TID 作决胜列）。因此：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;正向扫描满足 &lt;code&gt;ORDER BY x&lt;/code&gt;（即 &lt;code&gt;ASC NULLS LAST&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;反向扫描可满足 &lt;code&gt;ORDER BY x DESC&lt;/code&gt;（对应 &lt;code&gt;DESC NULLS FIRST&lt;/code&gt; 等组合规则）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;工程上特别有价值的形态是 &lt;strong&gt;&lt;code&gt;ORDER BY ... LIMIT n&lt;/code&gt;&lt;/strong&gt;：显式排序往往要处理全部候选才能取前 n 行；若有匹配的 B-tree，可直接取前 n 行，无需扫完剩余数据。&lt;/p&gt;
&lt;h3 id="唯一性约束如何落地"&gt;&lt;a href="#%e5%94%af%e4%b8%80%e6%80%a7%e7%ba%a6%e6%9d%9f%e5%a6%82%e4%bd%95%e8%90%bd%e5%9c%b0" class="header-anchor"&gt;&lt;/a&gt;唯一性约束如何落地
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;目前只有 B-tree 可声明为 unique&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;默认情况下，唯一列中的 &lt;strong&gt;多个 NULL 不视为相等&lt;/strong&gt;（允许多个 NULL）；&lt;code&gt;NULLS NOT DISTINCT&lt;/code&gt; 会把 NULL 当相等处理。&lt;/li&gt;
&lt;li&gt;定义 &lt;code&gt;PRIMARY KEY&lt;/code&gt; / &lt;code&gt;UNIQUE&lt;/code&gt; 约束时，PostgreSQL &lt;strong&gt;会自动创建&lt;/strong&gt; 对应唯一索引，无需再手写一份重复索引。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="扫描方式同一条-sql三种常见走路方式"&gt;&lt;a href="#%e6%89%ab%e6%8f%8f%e6%96%b9%e5%bc%8f%e5%90%8c%e4%b8%80%e6%9d%a1-sql%e4%b8%89%e7%a7%8d%e5%b8%b8%e8%a7%81%e8%b5%b0%e8%b7%af%e6%96%b9%e5%bc%8f" class="header-anchor"&gt;&lt;/a&gt;扫描方式：同一条 SQL，三种常见“走路方式”
&lt;/h2&gt;&lt;p&gt;PostgreSQL 的索引是 &lt;strong&gt;二级索引（secondary index）&lt;/strong&gt;：索引与堆表（heap）分开存储。普通索引扫描通常既读索引，又回表读堆；匹配的索引项可能相邻，但对应堆行可能散落各处，带来随机 I/O。&lt;/p&gt;
&lt;h3 id="1index-scan点查--高选择性范围"&gt;&lt;a href="#1index-scan%e7%82%b9%e6%9f%a5--%e9%ab%98%e9%80%89%e6%8b%a9%e6%80%a7%e8%8c%83%e5%9b%b4" class="header-anchor"&gt;&lt;/a&gt;1）Index Scan：点查 / 高选择性范围
&lt;/h3&gt;&lt;p&gt;适合“索引快速锁定少量行，再回表取完整行”的路径。&lt;code&gt;EXPLAIN&lt;/code&gt; 中常见 &lt;code&gt;Index Scan using ...&lt;/code&gt;。&lt;/p&gt;
&lt;h3 id="2bitmap-index-scan--bitmap-heap-scan中等选择性或组合条件"&gt;&lt;a href="#2bitmap-index-scan--bitmap-heap-scan%e4%b8%ad%e7%ad%89%e9%80%89%e6%8b%a9%e6%80%a7%e6%88%96%e7%bb%84%e5%90%88%e6%9d%a1%e4%bb%b6" class="header-anchor"&gt;&lt;/a&gt;2）Bitmap Index Scan + Bitmap Heap Scan：中等选择性或组合条件
&lt;/h3&gt;&lt;p&gt;当需要组合多个索引、或单次索引扫描无法直接表达某些 &lt;code&gt;OR&lt;/code&gt;/&lt;code&gt;AND&lt;/code&gt; 形态时，系统可：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;扫描一个或多个索引，在内存中生成 &lt;strong&gt;bitmap&lt;/strong&gt;（标记可能命中的堆位置）&lt;/li&gt;
&lt;li&gt;对 bitmap 做 &lt;code&gt;AND&lt;/code&gt; / &lt;code&gt;OR&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;再按 &lt;strong&gt;物理顺序&lt;/strong&gt; 访问堆表（&lt;code&gt;Bitmap Heap Scan&lt;/code&gt;）&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;官方指出：按物理序访问会 &lt;strong&gt;丢失原索引顺序&lt;/strong&gt;，因此后续若需要排序，可能再加 &lt;code&gt;Sort&lt;/code&gt;。这也解释了：bitmap 路径擅长降低随机 I/O，但不一定能顺便满足 &lt;code&gt;ORDER BY&lt;/code&gt;。&lt;/p&gt;
&lt;h3 id="3index-only-scan尽量不回表"&gt;&lt;a href="#3index-only-scan%e5%b0%bd%e9%87%8f%e4%b8%8d%e5%9b%9e%e8%a1%a8" class="header-anchor"&gt;&lt;/a&gt;3）Index Only Scan：尽量不回表
&lt;/h3&gt;&lt;p&gt;Index-only scan 的目标是 &lt;strong&gt;只靠索引回答查询&lt;/strong&gt;。官方给出两个基本前提：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;索引类型支持&lt;/strong&gt; index-only scan：B-tree &lt;strong&gt;始终支持&lt;/strong&gt;；GiST/SP-GiST 仅部分 operator class；GIN 等通常不支持（索引项往往只存原值的一部分）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;查询只引用索引中存有的列&lt;/strong&gt;。例如索引在 &lt;code&gt;(x, y)&lt;/code&gt; 上：
&lt;ul&gt;
&lt;li&gt;可用：&lt;code&gt;SELECT x, y FROM tab WHERE x = 'key';&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;不可用（还要读 &lt;code&gt;z&lt;/code&gt;）：&lt;code&gt;SELECT x, z FROM tab WHERE x = 'key';&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;即便列都在索引里，PostgreSQL 仍要做 &lt;strong&gt;MVCC 可见性&lt;/strong&gt; 判断。可见性信息不在索引项里，而在堆中；因此引入 &lt;strong&gt;visibility map（可见性映射）&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;若堆页对应 all-visible 位已设置，可认为行对当前/未来事务可见，&lt;strong&gt;无需再访问堆元组&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;若未设置，仍需回表检查可见性——此时相对普通 Index Scan 的收益会明显下降&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;因此：Index Only Scan “物理上可能”，但 &lt;strong&gt;只有当表中相当比例堆页的 all-visible 位有效时&lt;/strong&gt; 才更划算。对“大量行很少变化”的表，这类扫描非常实用；写很频繁、可见性图难以保持的表，收益会打折。&lt;/p&gt;
&lt;h3 id="覆盖索引与-include"&gt;&lt;a href="#%e8%a6%86%e7%9b%96%e7%b4%a2%e5%bc%95%e4%b8%8e-include" class="header-anchor"&gt;&lt;/a&gt;覆盖索引与 &lt;code&gt;INCLUDE&lt;/code&gt;
&lt;/h3&gt;&lt;p&gt;为了让高频查询更容易走 index-only，可以做 &lt;strong&gt;covering index（覆盖索引）&lt;/strong&gt;：让索引包含查询需要返回/过滤的列。PostgreSQL 允许用 &lt;code&gt;INCLUDE&lt;/code&gt; 添加 &lt;strong&gt;非键（non-key）列&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;非键列 &lt;strong&gt;不能&lt;/strong&gt; 用于索引扫描的查找条件，也不参与唯一/排他约束判定&lt;/li&gt;
&lt;li&gt;但 index-only scan 可直接从索引叶子返回这些列，无需回表&lt;/li&gt;
&lt;li&gt;&lt;code&gt;INCLUDE&lt;/code&gt; 列会增大索引、复制数据；宽列要谨慎&lt;/li&gt;
&lt;li&gt;带 non-key 列的 B-tree &lt;strong&gt;不会使用 deduplication&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;表达式不能作为 &lt;code&gt;INCLUDE&lt;/code&gt; 列（因为无法用于 index-only scan）&lt;/li&gt;
&lt;li&gt;当前 B-tree / GiST / SP-GiST 支持该特性；叶子存 INCLUDE 值，上层导航项不包含它们&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 键列用于查找，INCLUDE 列服务于覆盖返回
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;INDEX&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;orders_user_created_cover&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;INCLUDE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="多列索引部分索引选型比多建几个更重要"&gt;&lt;a href="#%e5%a4%9a%e5%88%97%e7%b4%a2%e5%bc%95%e9%83%a8%e5%88%86%e7%b4%a2%e5%bc%95%e9%80%89%e5%9e%8b%e6%af%94%e5%a4%9a%e5%bb%ba%e5%87%a0%e4%b8%aa%e6%9b%b4%e9%87%8d%e8%a6%81" class="header-anchor"&gt;&lt;/a&gt;多列索引、部分索引：选型比“多建几个”更重要
&lt;/h2&gt;&lt;h3 id="多列-b-tree最左前缀与效率"&gt;&lt;a href="#%e5%a4%9a%e5%88%97-b-tree%e6%9c%80%e5%b7%a6%e5%89%8d%e7%bc%80%e4%b8%8e%e6%95%88%e7%8e%87" class="header-anchor"&gt;&lt;/a&gt;多列 B-tree：最左前缀与效率
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;索引最多 &lt;strong&gt;32 列&lt;/strong&gt;（含 &lt;code&gt;INCLUDE&lt;/code&gt;；编译期可改 &lt;code&gt;pg_config_manual.h&lt;/code&gt;）。&lt;/li&gt;
&lt;li&gt;多键列目前由 B-tree、GiST、GIN、BRIN 等支持（与能否 &lt;code&gt;INCLUDE&lt;/code&gt; 是两件独立的事）。&lt;/li&gt;
&lt;li&gt;多列 B-tree 可用于涉及任意列子集的条件，但 &lt;strong&gt;在前导（最左）列上有约束时最高效&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;精确规则（官方表述）：对前导列的 &lt;strong&gt;相等约束&lt;/strong&gt;，加上第一个没有相等约束的列上的 &lt;strong&gt;不等式约束&lt;/strong&gt;，总是用于限制扫描的索引区间；更右侧列上的条件仍会在索引内检查（可减少回表），但不一定缩小扫描区间。&lt;/li&gt;
&lt;li&gt;新版本还有 &lt;strong&gt;skip scan&lt;/strong&gt; 优化：在合适基数下，可对缺少常规相等约束的中间列做内部动态相等约束，减少读取区间——但这是优化，不是让你随意打乱“左前缀”设计习惯。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;经验对照：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;查询形态&lt;/th&gt;
					&lt;th&gt;更合适的索引直觉&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;总是 &lt;code&gt;WHERE a = ? AND b = ?&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;(a, b)&lt;/code&gt; 复合索引&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;有时只有 &lt;code&gt;a&lt;/code&gt;，有时只有 &lt;code&gt;b&lt;/code&gt;，有时两者&lt;/td&gt;
					&lt;td&gt;可能两个单列 + 让规划器组合；或评估 skip scan / 复合&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;ORDER BY a, b LIMIT n&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;与排序方向一致的 &lt;code&gt;(a, b)&lt;/code&gt; B-tree&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;只查“活跃订单”等小子集&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;部分索引&lt;/strong&gt; 往往比全表索引更划算&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="部分索引只索引值得索引的子集"&gt;&lt;a href="#%e9%83%a8%e5%88%86%e7%b4%a2%e5%bc%95%e5%8f%aa%e7%b4%a2%e5%bc%95%e5%80%bc%e5%be%97%e7%b4%a2%e5%bc%95%e7%9a%84%e5%ad%90%e9%9b%86" class="header-anchor"&gt;&lt;/a&gt;部分索引：只索引“值得索引”的子集
&lt;/h3&gt;&lt;p&gt;部分索引建立在表的一个子集上，子集由谓词（&lt;code&gt;WHERE&lt;/code&gt;）定义，索引 &lt;strong&gt;只包含满足谓词的行&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;官方给出的核心动机：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;避免给常见值建索引&lt;/strong&gt;：若某值占比很高，查它本来也未必走索引，放进索引只会白白增大体积、拖慢写路径。&lt;/li&gt;
&lt;li&gt;缩小索引 → 命中索引的查询更快，且很多更新不必维护该索引。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;典型场景：访问日志中组织内网 IP 占比极高，但排查更关心外部 IP：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;INDEX&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;access_log_client_ip_ix&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;access_log&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client_ip&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NOT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;client_ip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;inet&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;192.168.100.0&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AND&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;client_ip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;inet&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;192.168.100.255&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;只有查询条件也落在“被索引子集”内时，规划器才能使用该部分索引；条件与谓词冲突时不会用。&lt;/p&gt;
&lt;h2 id="可复现实验从建表到读懂-explain"&gt;&lt;a href="#%e5%8f%af%e5%a4%8d%e7%8e%b0%e5%ae%9e%e9%aa%8c%e4%bb%8e%e5%bb%ba%e8%a1%a8%e5%88%b0%e8%af%bb%e6%87%82-explain" class="header-anchor"&gt;&lt;/a&gt;可复现实验：从建表到读懂 EXPLAIN
&lt;/h2&gt;&lt;p&gt;下面用一套小实验把机制落到命令（在任意 PostgreSQL ≥ 14 环境可跑；语句本身与官方语义一致）。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 1) 准备数据
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;DROP&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;EXISTS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;demo_orders&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;demo_orders&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bigserial&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;PRIMARY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NOT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NOT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;numeric&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NOT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;timestamptz&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NOT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;DEFAULT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;INTO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;demo_orders&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;)::&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;ARRAY&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;paid&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;pending&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;cancelled&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;paid&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;paid&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)::&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;round&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;)::&lt;/span&gt;&lt;span class="nb"&gt;numeric&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;365&lt;/span&gt;&lt;span class="p"&gt;)::&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39; days&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)::&lt;/span&gt;&lt;span class="nb"&gt;interval&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;generate_series&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;ANALYZE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;demo_orders&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 2) 无业务二级索引时看计划（主键本身是 B-tree 唯一索引）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;EXPLAIN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;ANALYZE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;BUFFERS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;demo_orders&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 3) 建立常用复合索引 + 部分索引 + 覆盖索引
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;INDEX&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CONCURRENTLY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;demo_orders_user_created_idx&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;demo_orders&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;INDEX&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CONCURRENTLY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;demo_orders_pending_idx&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;demo_orders&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;pending&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;INDEX&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CONCURRENTLY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;demo_orders_user_cover_idx&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;demo_orders&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;INCLUDE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;ANALYZE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;demo_orders&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 4) 对比计划节点名称
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;EXPLAIN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;ANALYZE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;BUFFERS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;demo_orders&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;ORDER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;LIMIT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;EXPLAIN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;ANALYZE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;BUFFERS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;demo_orders&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;pending&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AND&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;interval&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;7 days&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;EXPLAIN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;ANALYZE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;BUFFERS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;demo_orders&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 若统计与可见性合适，可能出现 Index Only Scan（依赖 INCLUDE 覆盖 + visibility map）
&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;ol&gt;
&lt;li&gt;&lt;strong&gt;节点类型&lt;/strong&gt;：&lt;code&gt;Seq Scan&lt;/code&gt; / &lt;code&gt;Index Scan&lt;/code&gt; / &lt;code&gt;Bitmap Index Scan&lt;/code&gt; + &lt;code&gt;Bitmap Heap Scan&lt;/code&gt; / &lt;code&gt;Index Only Scan&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;实际行数 vs 估算行数&lt;/strong&gt;（&lt;code&gt;rows&lt;/code&gt;）：偏差大 → 统计过期或谓词难估，先 &lt;code&gt;ANALYZE&lt;/code&gt;，再考虑扩展统计&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Buffers&lt;/strong&gt;：命中共享缓冲还是读盘；Index Only Scan 是否仍有大量 heap fetches（可见性图不给力）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;是否多了不必要的 Sort&lt;/strong&gt;：排序方向与索引 &lt;code&gt;(ASC/DESC, NULLS ...)&lt;/code&gt; 是否一致&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="生产变更create-index-concurrently"&gt;&lt;a href="#%e7%94%9f%e4%ba%a7%e5%8f%98%e6%9b%b4create-index-concurrently" class="header-anchor"&gt;&lt;/a&gt;生产变更：&lt;code&gt;CREATE INDEX CONCURRENTLY&lt;/code&gt;
&lt;/h2&gt;&lt;p&gt;普通 &lt;code&gt;CREATE INDEX&lt;/code&gt; 会对表加写锁：其它事务仍可读，但 &lt;strong&gt;INSERT/UPDATE/DELETE 会阻塞到索引建完&lt;/strong&gt;。大表上这可能长达数小时，生产不可接受。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;CREATE INDEX CONCURRENTLY&lt;/code&gt; 的官方要点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;构建过程 &lt;strong&gt;不获取会阻止并发写&lt;/strong&gt; 的锁（允许正常 DML 继续）&lt;/li&gt;
&lt;li&gt;代价：通常要 &lt;strong&gt;两次表扫描&lt;/strong&gt;，并等待可能修改/使用该索引的相关事务结束，&lt;strong&gt;总工作量更大、耗时更长&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;过程中索引会先以 &lt;strong&gt;INVALID&lt;/strong&gt; 状态进入系统目录；成功结束后才标为可用&lt;/li&gt;
&lt;li&gt;若扫描阶段失败（死锁、唯一冲突等），命令失败但可能留下 &lt;strong&gt;INVALID 索引&lt;/strong&gt;：查询会忽略它，却仍要为写维护它 → 推荐 &lt;code&gt;DROP&lt;/code&gt; 后重试 &lt;code&gt;CREATE INDEX CONCURRENTLY&lt;/code&gt;（或 &lt;code&gt;REINDEX INDEX CONCURRENTLY&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CREATE INDEX CONCURRENTLY&lt;/code&gt; &lt;strong&gt;不能&lt;/strong&gt; 放在事务块里；分区表并发构建有额外限制（可先对分区并发建，再非并发建分区父索引以缩短写锁定窗口）&lt;/li&gt;
&lt;li&gt;同一表上 &lt;strong&gt;同时只能有一个&lt;/strong&gt; concurrent index build&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;INDEX&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CONCURRENTLY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sales_quantity_index&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sales_table&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="常见坑与排查清单"&gt;&lt;a href="#%e5%b8%b8%e8%a7%81%e5%9d%91%e4%b8%8e%e6%8e%92%e6%9f%a5%e6%b8%85%e5%8d%95" class="header-anchor"&gt;&lt;/a&gt;常见坑与排查清单
&lt;/h2&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;现象&lt;/th&gt;
					&lt;th&gt;常见原因&lt;/th&gt;
					&lt;th&gt;怎么查 / 怎么办&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;建了索引仍 &lt;code&gt;Seq Scan&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;选择性差、统计过期、函数包住列导致无法匹配、成本模型认为全表更便宜&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;EXPLAIN&lt;/code&gt;；&lt;code&gt;ANALYZE&lt;/code&gt;；避免 &lt;code&gt;WHERE func(col)=...&lt;/code&gt;（改表达式索引）；用真实参数看计划&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;LIKE '%x%'&lt;/code&gt; 不走 B-tree&lt;/td&gt;
					&lt;td&gt;B-tree 需要前缀锚定模式&lt;/td&gt;
					&lt;td&gt;换 &lt;code&gt;pg_trgm&lt;/code&gt; + GIN/GiST 等（另一条路线，不是默认 B-tree）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;多列索引“像没用”&lt;/td&gt;
					&lt;td&gt;谓词没落在最左列，或类型/排序规则不匹配&lt;/td&gt;
					&lt;td&gt;检查前导列是否有相等/范围条件；核对 &lt;code&gt;ASC/DESC NULLS&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;期望 Index Only Scan 却回表很多&lt;/td&gt;
					&lt;td&gt;查询列未覆盖；visibility map 中 all-visible 比例低&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;INCLUDE&lt;/code&gt; 覆盖；减少无意义更新；关注 vacuum 是否跟上&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;写变慢、膨胀&lt;/td&gt;
					&lt;td&gt;索引过多、宽 INCLUDE、低选择性索引&lt;/td&gt;
					&lt;td&gt;用 &lt;code&gt;pg_stat_user_indexes&lt;/code&gt; 看扫描次数；删冗余；部分索引收窄&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;CONCURRENTLY&lt;/code&gt; 后有 &lt;code&gt;INVALID&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;构建失败留下残留&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;\d table&lt;/code&gt; 确认；&lt;code&gt;DROP INDEX&lt;/code&gt; 后重建&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;唯一约束“允许多个 NULL”&lt;/td&gt;
					&lt;td&gt;默认 NULL 不相等&lt;/td&gt;
					&lt;td&gt;需要业务唯一含 NULL 时用 &lt;code&gt;NULLS NOT DISTINCT&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;辅助观测：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 索引是否被使用（需打开 stats）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;schemaname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;relname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;indexrelname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;idx_scan&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;idx_tup_read&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;idx_tup_fetch&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pg_stat_user_indexes&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;ORDER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;idx_scan&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ASC&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;LIMIT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 无效索引排查（概念：concurrent 失败残留）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;relname&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;indisvalid&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pg_index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;JOIN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pg_class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;oid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;indexrelid&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NOT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;indisvalid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="工程实践建议可直接落地"&gt;&lt;a href="#%e5%b7%a5%e7%a8%8b%e5%ae%9e%e8%b7%b5%e5%bb%ba%e8%ae%ae%e5%8f%af%e7%9b%b4%e6%8e%a5%e8%90%bd%e5%9c%b0" class="header-anchor"&gt;&lt;/a&gt;工程实践建议（可直接落地）
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;先写清访问路径&lt;/strong&gt;：等值 / 范围 / 排序 / 覆盖返回列 / 是否只查子集。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;默认从 B-tree 开始&lt;/strong&gt;：范围、排序、唯一约束几乎都靠它；Hash 仅在“只要相等且理解其限制”时考虑。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;复合索引按过滤+排序设计列序&lt;/strong&gt;：高频等值列靠左，范围/排序列靠右；方向与 &lt;code&gt;ORDER BY&lt;/code&gt; 对齐。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;热点子集优先部分索引&lt;/strong&gt;：状态机里的 &lt;code&gt;pending&lt;/code&gt;、软删除后的“有效行”、租户内活跃数据等。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;高频只读投影再谈 &lt;code&gt;INCLUDE&lt;/code&gt;&lt;/strong&gt;：覆盖收益要压过索引变大与 dedup 失效的代价。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;生产建索引用 &lt;code&gt;CONCURRENTLY&lt;/code&gt;，并处理 INVALID 残留&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;用 &lt;code&gt;EXPLAIN (ANALYZE, BUFFERS)&lt;/code&gt; 闭环&lt;/strong&gt;，而不是用“感觉”判断索引是否生效。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="总结"&gt;&lt;a href="#%e6%80%bb%e7%bb%93" class="header-anchor"&gt;&lt;/a&gt;总结
&lt;/h2&gt;&lt;p&gt;PostgreSQL 索引体系的核心不是“类型清单”，而是三层决策：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;访问方法&lt;/strong&gt;：日常以 B-tree 承载相等、范围、排序与唯一性；Hash/GIN/GiST/BRIN 解决更专门的谓词与数据形态。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;扫描算子&lt;/strong&gt;：Index Scan 精确定位，Bitmap 组合与减少随机 I/O，Index Only Scan 在覆盖列 + 可见性图友好时避免回表。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;工程形态&lt;/strong&gt;：多列最左前缀、部分索引缩子集、&lt;code&gt;INCLUDE&lt;/code&gt; 做覆盖、&lt;code&gt;CONCURRENTLY&lt;/code&gt; 保障在线变更。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;把这三层和 &lt;code&gt;EXPLAIN&lt;/code&gt; 对上，索引才会从“玄学加速”变成可验证的性能工具。&lt;/p&gt;
&lt;h2 id="参考资料"&gt;&lt;a href="#%e5%8f%82%e8%80%83%e8%b5%84%e6%96%99" class="header-anchor"&gt;&lt;/a&gt;参考资料
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;a class="link" href="https://www.postgresql.org/docs/current/indexes-intro.html" target="_blank" rel="noopener"
 &gt;PostgreSQL 18 文档：Indexes — Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.postgresql.org/docs/current/indexes-types.html" target="_blank" rel="noopener"
 &gt;PostgreSQL 18 文档：Index Types（B-Tree / Hash / GiST / GIN / BRIN）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.postgresql.org/docs/current/indexes-multicolumn.html" target="_blank" rel="noopener"
 &gt;PostgreSQL 18 文档：Multicolumn Indexes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.postgresql.org/docs/current/indexes-ordering.html" target="_blank" rel="noopener"
 &gt;PostgreSQL 18 文档：Indexes and ORDER BY&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.postgresql.org/docs/current/indexes-bitmap-scans.html" target="_blank" rel="noopener"
 &gt;PostgreSQL 18 文档：Combining Multiple Indexes（bitmap 组合）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.postgresql.org/docs/current/indexes-unique.html" target="_blank" rel="noopener"
 &gt;PostgreSQL 18 文档：Unique Indexes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.postgresql.org/docs/current/indexes-partial.html" target="_blank" rel="noopener"
 &gt;PostgreSQL 18 文档：Partial Indexes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.postgresql.org/docs/current/indexes-index-only-scans.html" target="_blank" rel="noopener"
 &gt;PostgreSQL 18 文档：Index-Only Scans and Covering Indexes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.postgresql.org/docs/current/sql-createindex.html" target="_blank" rel="noopener"
 &gt;PostgreSQL 18 文档：CREATE INDEX（含 CONCURRENTLY / INCLUDE）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.postgresql.org/docs/current/using-explain.html" target="_blank" rel="noopener"
 &gt;PostgreSQL 18 文档：Using EXPLAIN&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>HTTP 条件请求与缓存协商：ETag、Last-Modified 与 Cache-Control 工程实践</title><link>https://blog.waihost.com/posts/http-conditional-requests-cache-negotiation/</link><pubDate>Wed, 15 Jul 2026 00:00:00 +0800</pubDate><guid>https://blog.waihost.com/posts/http-conditional-requests-cache-negotiation/</guid><description>&lt;img src="https://blog.waihost.com/images/covers/http-conditional-requests-cache-negotiation.svg" alt="Featured image of post HTTP 条件请求与缓存协商：ETag、Last-Modified 与 Cache-Control 工程实践" /&gt;&lt;p&gt;很多系统的“慢”并不在业务逻辑，而在重复传输不变的响应体。浏览器、CDN、反向代理都可能持有一份旧副本；真正关键的是：&lt;strong&gt;这份副本还能不能直接用？如果不能直接用，能否只问服务器“变了没有”？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;HTTP 把这件事拆成两层：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;新鲜度（freshness）&lt;/strong&gt;：缓存还在不在有效期内，决定要不要立刻回源；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;验证（validation）&lt;/strong&gt;：过期或必须再确认时，用条件请求做协商，成功则返回 &lt;code&gt;304 Not Modified&lt;/code&gt;，避免再次传输实体。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;本文基于 HTTP 语义规范（RFC 9110）与 HTTP 缓存规范（RFC 9111），结合 Nginx 能力，讲清楚 &lt;code&gt;Cache-Control&lt;/code&gt;、&lt;code&gt;ETag&lt;/code&gt; / &lt;code&gt;Last-Modified&lt;/code&gt;、&lt;code&gt;If-None-Match&lt;/code&gt; / &lt;code&gt;If-Modified-Since&lt;/code&gt; 的协作关系，以及可落地的配置与排错方法。&lt;/p&gt;
&lt;h2 id="一先分清存下来--可以直接用"&gt;&lt;a href="#%e4%b8%80%e5%85%88%e5%88%86%e6%b8%85%e5%ad%98%e4%b8%8b%e6%9d%a5--%e5%8f%af%e4%bb%a5%e7%9b%b4%e6%8e%a5%e7%94%a8" class="header-anchor"&gt;&lt;/a&gt;一、先分清：存下来 ≠ 可以直接用
&lt;/h2&gt;&lt;p&gt;RFC 9111 把缓存决策大致分成：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;阶段&lt;/th&gt;
					&lt;th&gt;问题&lt;/th&gt;
					&lt;th&gt;关键概念&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;存储（store）&lt;/td&gt;
					&lt;td&gt;响应能不能进缓存&lt;/td&gt;
					&lt;td&gt;方法、状态码、&lt;code&gt;no-store&lt;/code&gt;、&lt;code&gt;private&lt;/code&gt;、&lt;code&gt;Authorization&lt;/code&gt; 等&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;新鲜度（fresh）&lt;/td&gt;
					&lt;td&gt;已存副本是否仍 fresh&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;max-age&lt;/code&gt; / &lt;code&gt;s-maxage&lt;/code&gt; / &lt;code&gt;Expires&lt;/code&gt; / Age&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;验证（validate）&lt;/td&gt;
					&lt;td&gt;stale 后能否复用&lt;/td&gt;
					&lt;td&gt;条件请求 + 校验器（validator）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;复用（reuse）&lt;/td&gt;
					&lt;td&gt;最终回什么&lt;/td&gt;
					&lt;td&gt;200 全量、304 无 body、或回源新响应&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;常见误解：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;no-cache&lt;/code&gt; 不是“禁止缓存”&lt;/strong&gt;。它表示：可以用存储副本，但&lt;strong&gt;必须先成功验证&lt;/strong&gt;后才能拿去满足其他请求。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;no-store&lt;/code&gt; 才是“不要存”&lt;/strong&gt;。缓存不得存储该请求/响应的任何部分，也不能拿它去满足别的请求。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;private&lt;/code&gt; 不是“浏览器也不能缓存”&lt;/strong&gt;。它限制的是&lt;strong&gt;共享缓存&lt;/strong&gt;（CDN、公司代理）：响应面向单个用户；私有缓存（浏览器）仍可在规则允许下存储。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;把语义说清楚，后面的工程配置才不会互相打架。&lt;/p&gt;
&lt;h2 id="二新鲜度cache-control-的核心指令"&gt;&lt;a href="#%e4%ba%8c%e6%96%b0%e9%b2%9c%e5%ba%a6cache-control-%e7%9a%84%e6%a0%b8%e5%bf%83%e6%8c%87%e4%bb%a4" class="header-anchor"&gt;&lt;/a&gt;二、新鲜度：Cache-Control 的核心指令
&lt;/h2&gt;&lt;h3 id="1-响应侧最常用指令"&gt;&lt;a href="#1-%e5%93%8d%e5%ba%94%e4%be%a7%e6%9c%80%e5%b8%b8%e7%94%a8%e6%8c%87%e4%bb%a4" class="header-anchor"&gt;&lt;/a&gt;1. 响应侧最常用指令
&lt;/h3&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;指令&lt;/th&gt;
					&lt;th&gt;含义（工程视角）&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;max-age=N&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;响应在 age &amp;gt; N 秒后视为 stale&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;s-maxage=N&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;只对共享缓存&lt;/strong&gt;覆盖 &lt;code&gt;max-age&lt;/code&gt;/&lt;code&gt;Expires&lt;/code&gt;；并带有必须再验证后才能复用 stale 的语义&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;public&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;明确允许缓存（例如带 &lt;code&gt;Authorization&lt;/code&gt; 的响应在共享缓存中的场景）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;private&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;禁止共享缓存存储（或限定字段）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;no-cache&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;使用前必须验证&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;no-store&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;不要存储&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;must-revalidate&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;一旦 stale，&lt;strong&gt;不得&lt;/strong&gt;在未验证成功前复用；断连时更应报错而不是偷偷给 stale&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;一个可记的分层：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-http" data-lang="http"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;# 静态资源：可公开、长期缓存（内容指纹化后）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;Cache-Control: public, max-age=31536000, immutable
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;# HTML 入口：可缓存但每次用前确认
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;Cache-Control: no-cache
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;# 登录态 API：不要被中间层存
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;Cache-Control: private, no-store
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;# 对一致性要求高的共享缓存内容
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;Cache-Control: public, max-age=60, must-revalidate
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="2-age-与看起来刚生成"&gt;&lt;a href="#2-age-%e4%b8%8e%e7%9c%8b%e8%b5%b7%e6%9d%a5%e5%88%9a%e7%94%9f%e6%88%90" class="header-anchor"&gt;&lt;/a&gt;2. Age 与“看起来刚生成”
&lt;/h3&gt;&lt;p&gt;响应里的 &lt;code&gt;Age&lt;/code&gt; 表示估计已在缓存中停留的秒数。共享缓存命中时，客户端看到的“年龄”往往不是 0。排查“为什么我设了 &lt;code&gt;max-age=60&lt;/code&gt;，却总在提前回源”时，要同时看：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;源站给出的 &lt;code&gt;Cache-Control&lt;/code&gt; / &lt;code&gt;Expires&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;中间层是否改写了头&lt;/li&gt;
&lt;li&gt;响应是否已有较大 &lt;code&gt;Age&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="3-启发式新鲜度别依赖没写过期时间也碰巧能缓存"&gt;&lt;a href="#3-%e5%90%af%e5%8f%91%e5%bc%8f%e6%96%b0%e9%b2%9c%e5%ba%a6%e5%88%ab%e4%be%9d%e8%b5%96%e6%b2%a1%e5%86%99%e8%bf%87%e6%9c%9f%e6%97%b6%e9%97%b4%e4%b9%9f%e7%a2%b0%e5%b7%a7%e8%83%bd%e7%bc%93%e5%ad%98" class="header-anchor"&gt;&lt;/a&gt;3. 启发式新鲜度：别依赖“没写过期时间也碰巧能缓存”
&lt;/h3&gt;&lt;p&gt;没有显式过期信息时，部分缓存会用启发式算法估算 fresh 时间。这在不同实现间差异大，生产环境应&lt;strong&gt;显式给出 freshness&lt;/strong&gt;，不要赌默认行为。&lt;/p&gt;
&lt;h2 id="三校验器etag-与-last-modified"&gt;&lt;a href="#%e4%b8%89%e6%a0%a1%e9%aa%8c%e5%99%a8etag-%e4%b8%8e-last-modified" class="header-anchor"&gt;&lt;/a&gt;三、校验器：ETag 与 Last-Modified
&lt;/h2&gt;&lt;p&gt;验证阶段依赖&lt;strong&gt;表示元数据（representation metadata）&lt;/strong&gt;，最常见两类：&lt;/p&gt;
&lt;h3 id="1-last-modified"&gt;&lt;a href="#1-last-modified" class="header-anchor"&gt;&lt;/a&gt;1. &lt;code&gt;Last-Modified&lt;/code&gt;
&lt;/h3&gt;&lt;p&gt;源站认为所选表示&lt;strong&gt;最后修改&lt;/strong&gt;的时间（HTTP-date）。例如：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-http" data-lang="http"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;Last-Modified: Tue, 15 Nov 1994 12:45:26 GMT
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;优点：实现简单，文件系统/对象存储天然具备 mtime。&lt;br&gt;
局限：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;秒级精度，一秒内多次变更可能撞车；&lt;/li&gt;
&lt;li&gt;时钟回拨、多副本 mtime 不一致会误判；&lt;/li&gt;
&lt;li&gt;内容没变但元数据变了时，语义可能不够准。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="2-etag实体标签"&gt;&lt;a href="#2-etag%e5%ae%9e%e4%bd%93%e6%a0%87%e7%ad%be" class="header-anchor"&gt;&lt;/a&gt;2. &lt;code&gt;ETag&lt;/code&gt;（实体标签）
&lt;/h3&gt;&lt;p&gt;&lt;code&gt;ETag&lt;/code&gt; 是源站为某个表示分配的不透明标签：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-http" data-lang="http"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;ETag: &amp;#34;xyzzy&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;ETag: W/&amp;#34;xyzzy&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;强校验器（strong）&lt;/strong&gt;：表示数据（以及会影响 200 内容的关键元数据）变化时标签应变化。适合字节级精确比较。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;弱校验器（weak，&lt;code&gt;W/&lt;/code&gt; 前缀）&lt;/strong&gt;：语义等价即可，即使字节不完全相同。生成成本更低，但比较能力更弱。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;RFC 9110 明确：在不便存修改时间、一秒精度不够、或修改时间维护不一致时，&lt;strong&gt;ETag 通常比 Last-Modified 更可靠&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;工程上常见生成策略：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;策略&lt;/th&gt;
					&lt;th&gt;适用&lt;/th&gt;
					&lt;th&gt;注意&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;内容哈希（SHA-256 截断）&lt;/td&gt;
					&lt;td&gt;静态文件、构建产物&lt;/td&gt;
					&lt;td&gt;强 ETag，成本随体积上升&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;版本号 / 修订号&lt;/td&gt;
					&lt;td&gt;业务资源、CMS&lt;/td&gt;
					&lt;td&gt;发布流程必须保证“内容变则版本变”&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;inode + size + mtime&lt;/td&gt;
					&lt;td&gt;传统文件服务器&lt;/td&gt;
					&lt;td&gt;迁移/复制后可能变，跨机一致性差&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;弱 ETag（模板渲染摘要）&lt;/td&gt;
					&lt;td&gt;HTML 片段语义不变&lt;/td&gt;
					&lt;td&gt;不要拿去当字节级一致性保证&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="四条件请求如何问变了没有"&gt;&lt;a href="#%e5%9b%9b%e6%9d%a1%e4%bb%b6%e8%af%b7%e6%b1%82%e5%a6%82%e4%bd%95%e9%97%ae%e5%8f%98%e4%ba%86%e6%b2%a1%e6%9c%89" class="header-anchor"&gt;&lt;/a&gt;四、条件请求：如何问“变了没有”
&lt;/h2&gt;&lt;h3 id="1-if-none-match优先基于-etag"&gt;&lt;a href="#1-if-none-match%e4%bc%98%e5%85%88%e5%9f%ba%e4%ba%8e-etag" class="header-anchor"&gt;&lt;/a&gt;1. &lt;code&gt;If-None-Match&lt;/code&gt;（优先，基于 ETag）
&lt;/h3&gt;&lt;p&gt;客户端/缓存带上已存表示的 ETag：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-http" data-lang="http"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;GET&lt;/span&gt; &lt;span class="nn"&gt;/app.js&lt;/span&gt; &lt;span class="kr"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Host&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="l"&gt;example.com&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;If-None-Match&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="l"&gt;&amp;#34;33a64df551425fcc55e4d42a148795d9f25f89d4&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;源站比较当前 ETag：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;匹配&lt;/strong&gt;（表示未变）→ &lt;code&gt;304 Not Modified&lt;/code&gt;，通常无 body；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;不匹配&lt;/strong&gt; → &lt;code&gt;200 OK&lt;/code&gt; + 新表示（含新的 ETag）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;对 &lt;code&gt;If-None-Match&lt;/code&gt;，规范要求使用&lt;strong&gt;弱比较函数&lt;/strong&gt;：即使只有弱 ETag，也可用于缓存验证。&lt;/p&gt;
&lt;h3 id="2-if-modified-since基于时间"&gt;&lt;a href="#2-if-modified-since%e5%9f%ba%e4%ba%8e%e6%97%b6%e9%97%b4" class="header-anchor"&gt;&lt;/a&gt;2. &lt;code&gt;If-Modified-Since&lt;/code&gt;（基于时间）
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-http" data-lang="http"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;GET&lt;/span&gt; &lt;span class="nn"&gt;/style.css&lt;/span&gt; &lt;span class="kr"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;If-Modified-Since&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="l"&gt;Sat, 29 Oct 1994 19:43:31 GMT&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;h3 id="3-两者同时出现时谁说了算"&gt;&lt;a href="#3-%e4%b8%a4%e8%80%85%e5%90%8c%e6%97%b6%e5%87%ba%e7%8e%b0%e6%97%b6%e8%b0%81%e8%af%b4%e4%ba%86%e7%ae%97" class="header-anchor"&gt;&lt;/a&gt;3. 两者同时出现时谁说了算？
&lt;/h3&gt;&lt;p&gt;RFC 9110 规定：若请求里带了 &lt;code&gt;If-None-Match&lt;/code&gt;，接收方&lt;strong&gt;必须忽略&lt;/strong&gt; &lt;code&gt;If-Modified-Since&lt;/code&gt;——因为实体标签被认为是更准确的替代条件。两者同时出现多是兼容历史客户端；服务端实现应按规范优先级处理。&lt;/p&gt;
&lt;h3 id="4-304-not-modified-到底省了什么"&gt;&lt;a href="#4-304-not-modified-%e5%88%b0%e5%ba%95%e7%9c%81%e4%ba%86%e4%bb%80%e4%b9%88" class="header-anchor"&gt;&lt;/a&gt;4. &lt;code&gt;304 Not Modified&lt;/code&gt; 到底省了什么
&lt;/h3&gt;&lt;p&gt;&lt;code&gt;304&lt;/code&gt; 表示：若没有条件约束，这次本应是 &lt;code&gt;200&lt;/code&gt;；但条件为假（未修改），客户端应继续使用本地已存表示。&lt;/p&gt;
&lt;p&gt;源站仍应带上在同等 &lt;code&gt;200&lt;/code&gt; 中会发送的关键头（如 &lt;code&gt;ETag&lt;/code&gt;、&lt;code&gt;Date&lt;/code&gt;、&lt;code&gt;Content-Location&lt;/code&gt; 等），以便缓存&lt;strong&gt;刷新元数据&lt;/strong&gt;（freshen），而不只是“空响应”。&lt;/p&gt;
&lt;p&gt;流量层面：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;首次：200 + 完整 body（例如 800KB）
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;之后未变更：304 + 几乎无 body（通常几百字节头）
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;对静态资源、移动端弱网、跨境回源，收益非常明显。&lt;/p&gt;
&lt;h2 id="五一张图串起完整路径"&gt;&lt;a href="#%e4%ba%94%e4%b8%80%e5%bc%a0%e5%9b%be%e4%b8%b2%e8%b5%b7%e5%ae%8c%e6%95%b4%e8%b7%af%e5%be%84" class="header-anchor"&gt;&lt;/a&gt;五、一张图串起完整路径
&lt;/h2&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Client/CDN 本地有副本？
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; | 无 -----&amp;gt; 普通 GET -----&amp;gt; 200 + body + Cache-Control + ETag/LM
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; | 有且 fresh -----&amp;gt; 直接复用（不回源）
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; | 有但 stale / 或 no-cache
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; v
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 条件请求：If-None-Match / If-Modified-Since
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; +--&amp;gt; 未变：304（刷新头，复用旧 body）
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; +--&amp;gt; 已变：200（新 body + 新校验器）
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;记住两句话：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;fresh 解决“要不要问”&lt;/strong&gt;；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;validator 解决“问了以后要不要传 body”&lt;/strong&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;只有 &lt;code&gt;max-age&lt;/code&gt; 没有校验器，过期后往往只能全量回源；只有 ETag 没有合理 freshness，会变成“每次都协商”，仍然浪费 RTT。&lt;/p&gt;
&lt;h2 id="六工程实践按资源类型给策略"&gt;&lt;a href="#%e5%85%ad%e5%b7%a5%e7%a8%8b%e5%ae%9e%e8%b7%b5%e6%8c%89%e8%b5%84%e6%ba%90%e7%b1%bb%e5%9e%8b%e7%bb%99%e7%ad%96%e7%95%a5" class="header-anchor"&gt;&lt;/a&gt;六、工程实践：按资源类型给策略
&lt;/h2&gt;&lt;h3 id="1-带内容哈希的静态资源最推荐"&gt;&lt;a href="#1-%e5%b8%a6%e5%86%85%e5%ae%b9%e5%93%88%e5%b8%8c%e7%9a%84%e9%9d%99%e6%80%81%e8%b5%84%e6%ba%90%e6%9c%80%e6%8e%a8%e8%8d%90" class="header-anchor"&gt;&lt;/a&gt;1. 带内容哈希的静态资源（最推荐）
&lt;/h3&gt;&lt;p&gt;构建后文件名含 hash，例如 &lt;code&gt;app.3f2a1c.js&lt;/code&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-nginx" data-lang="nginx"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;location&lt;/span&gt; &lt;span class="s"&gt;/static/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 文件名已指纹化，可大胆长缓存
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;expires&lt;/span&gt; &lt;span class="s"&gt;365d&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;add_header&lt;/span&gt; &lt;span class="s"&gt;Cache-Control&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;public,&lt;/span&gt; &lt;span class="s"&gt;max-age=31536000,&lt;/span&gt; &lt;span class="s"&gt;immutable&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&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;ul&gt;
&lt;li&gt;&lt;strong&gt;URL 变 = 内容变&lt;/strong&gt;，可用超长 &lt;code&gt;max-age&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;immutable&lt;/code&gt; 提示浏览器在 freshness 内不必做启发式再验证（支持情况因客户端而异，但不妨碍给出）；&lt;/li&gt;
&lt;li&gt;旧 URL 自然失效，无需纠结主动 purge 每一个文件。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="2-html--入口文档"&gt;&lt;a href="#2-html--%e5%85%a5%e5%8f%a3%e6%96%87%e6%a1%a3" class="header-anchor"&gt;&lt;/a&gt;2. HTML / 入口文档
&lt;/h3&gt;&lt;p&gt;入口常引用带 hash 的静态资源，自身却要尽快看到新版本：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-http" data-lang="http"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;Cache-Control: no-cache
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;ETag: &amp;#34;html-rev-42&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;或短 &lt;code&gt;max-age&lt;/code&gt; + 强制再验证：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-http" data-lang="http"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;Cache-Control: max-age=0, must-revalidate
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;ETag: &amp;#34;html-rev-42&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这样浏览器/边缘仍可存副本，但使用前会走条件请求；HTML 未变则 304，变了才下新文档。&lt;/p&gt;
&lt;h3 id="3-个性化或含凭证的-api"&gt;&lt;a href="#3-%e4%b8%aa%e6%80%a7%e5%8c%96%e6%88%96%e5%90%ab%e5%87%ad%e8%af%81%e7%9a%84-api" class="header-anchor"&gt;&lt;/a&gt;3. 个性化或含凭证的 API
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-http" data-lang="http"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;Cache-Control: private, no-store
&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;ul&gt;
&lt;li&gt;&lt;code&gt;private&lt;/code&gt; 避免 CDN 把用户 A 的响应给用户 B；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;no-store&lt;/code&gt; 进一步降低敏感信息落盘风险。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;注意：&lt;code&gt;no-store&lt;/code&gt; &lt;strong&gt;不是&lt;/strong&gt;完备隐私方案（恶意或失控缓存可能不遵守），敏感数据仍要靠鉴权、最小化返回字段和传输安全。&lt;/p&gt;
&lt;h3 id="4-可被-cdn-缓存的公共-api--配置"&gt;&lt;a href="#4-%e5%8f%af%e8%a2%ab-cdn-%e7%bc%93%e5%ad%98%e7%9a%84%e5%85%ac%e5%85%b1-api--%e9%85%8d%e7%bd%ae" class="header-anchor"&gt;&lt;/a&gt;4. 可被 CDN 缓存的公共 API / 配置
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-http" data-lang="http"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;Cache-Control: public, s-maxage=30, max-age=10
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;ETag: &amp;#34;cfg-20260715-1&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;浏览器短缓存（&lt;code&gt;max-age&lt;/code&gt;）；&lt;/li&gt;
&lt;li&gt;CDN 稍长（&lt;code&gt;s-maxage&lt;/code&gt;）；&lt;/li&gt;
&lt;li&gt;过期后用 ETag 协商，降低源站带宽。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Nginx 反代缓存可配合：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-nginx" data-lang="nginx"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;proxy_cache_path&lt;/span&gt; &lt;span class="s"&gt;/var/cache/nginx&lt;/span&gt; &lt;span class="s"&gt;levels=1:2&lt;/span&gt; &lt;span class="s"&gt;keys_zone=api_cache:50m&lt;/span&gt; &lt;span class="s"&gt;inactive=10m&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;location&lt;/span&gt; &lt;span class="s"&gt;/api/public/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;proxy_cache&lt;/span&gt; &lt;span class="s"&gt;api_cache&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;proxy_cache_valid&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="s"&gt;30s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 过期后用条件请求回源校验
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;proxy_cache_revalidate&lt;/span&gt; &lt;span class="no"&gt;on&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 更新期间允许短暂提供 stale，降低源站抖动影响
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;proxy_cache_use_stale&lt;/span&gt; &lt;span class="s"&gt;updating&lt;/span&gt; &lt;span class="s"&gt;error&lt;/span&gt; &lt;span class="s"&gt;timeout&lt;/span&gt; &lt;span class="s"&gt;http_500&lt;/span&gt; &lt;span class="s"&gt;http_502&lt;/span&gt; &lt;span class="s"&gt;http_503&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://upstream&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;proxy_cache_revalidate on&lt;/code&gt; 的作用，正是在缓存过期后启用 &lt;code&gt;If-Modified-Since&lt;/code&gt; / &lt;code&gt;If-None-Match&lt;/code&gt; 条件回源——这与 RFC 里的 validation 模型一致。&lt;/p&gt;
&lt;h2 id="七服务端如何正确实现条件-get"&gt;&lt;a href="#%e4%b8%83%e6%9c%8d%e5%8a%a1%e7%ab%af%e5%a6%82%e4%bd%95%e6%ad%a3%e7%a1%ae%e5%ae%9e%e7%8e%b0%e6%9d%a1%e4%bb%b6-get" class="header-anchor"&gt;&lt;/a&gt;七、服务端如何正确实现条件 GET
&lt;/h2&gt;&lt;p&gt;以伪代码说明最小正确逻辑：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle_get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;etag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strong_etag&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;# 例如 &amp;#39;&amp;#34;%s&amp;#34;&amp;#39; % sha256(body)[:16]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;last_mod&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;last_modified_http_date&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 1) 优先处理 If-None-Match&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;inm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;If-None-Match&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;inm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;etag_matches&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;etag&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="c1"&gt;# 按弱比较规则实现&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response_304&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;etag&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;etag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;last_modified&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;last_mod&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response_200&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;etag&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;etag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;last_modified&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;last_mod&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 2) 否则再看 If-Modified-Since&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;ims&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;If-Modified-Since&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ims&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;modified_after&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ims&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response_304&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;etag&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;etag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;last_modified&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;last_mod&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response_200&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;etag&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;etag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;last_modified&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;last_mod&lt;/span&gt;&lt;span class="p"&gt;)&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;ol&gt;
&lt;li&gt;&lt;strong&gt;同一表示的 ETag 必须稳定&lt;/strong&gt;：相同内容不要每次随机；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;内容变更必须换 ETag&lt;/strong&gt;：漏更新会造成“改了却一直 304”；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;Vary&lt;/code&gt; 要配对&lt;/strong&gt;：若响应随 &lt;code&gt;Accept-Encoding&lt;/code&gt; / &lt;code&gt;Authorization&lt;/code&gt; / 自定义头变化，必须正确 &lt;code&gt;Vary&lt;/code&gt;，否则缓存会串内容；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;压缩前后一致性&lt;/strong&gt;：对 gzip/br 内容，ETag 策略要与表示选择一致，避免“协商错表示”；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;不要手动拼错弱标记&lt;/strong&gt;：&lt;code&gt;W/&lt;/code&gt; 大小写敏感，格式为 &lt;code&gt;W/&amp;quot;tag&amp;quot;&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="八常见坑与排查清单"&gt;&lt;a href="#%e5%85%ab%e5%b8%b8%e8%a7%81%e5%9d%91%e4%b8%8e%e6%8e%92%e6%9f%a5%e6%b8%85%e5%8d%95" class="header-anchor"&gt;&lt;/a&gt;八、常见坑与排查清单
&lt;/h2&gt;&lt;h3 id="坑-1no-cache-和-no-store-用反"&gt;&lt;a href="#%e5%9d%91-1no-cache-%e5%92%8c-no-store-%e7%94%a8%e5%8f%8d" class="header-anchor"&gt;&lt;/a&gt;坑 1：&lt;code&gt;no-cache&lt;/code&gt; 和 &lt;code&gt;no-store&lt;/code&gt; 用反
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;想“每次确认后再用本地副本” → &lt;code&gt;no-cache&lt;/code&gt;（或 &lt;code&gt;max-age=0, must-revalidate&lt;/code&gt;）+ ETag；&lt;/li&gt;
&lt;li&gt;想“别存敏感响应” → &lt;code&gt;no-store&lt;/code&gt;（通常再加 &lt;code&gt;private&lt;/code&gt;）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="坑-2只配了长缓存没有内容指纹"&gt;&lt;a href="#%e5%9d%91-2%e5%8f%aa%e9%85%8d%e4%ba%86%e9%95%bf%e7%bc%93%e5%ad%98%e6%b2%a1%e6%9c%89%e5%86%85%e5%ae%b9%e6%8c%87%e7%ba%b9" class="header-anchor"&gt;&lt;/a&gt;坑 2：只配了长缓存，没有内容指纹
&lt;/h3&gt;&lt;p&gt;&lt;code&gt;Cache-Control: max-age=31536000&lt;/code&gt; 配在&lt;strong&gt;固定 URL&lt;/strong&gt; 的 &lt;code&gt;app.js&lt;/code&gt; 上，发版后用户会长时间用旧脚本。正确做法是 &lt;strong&gt;hash 文件名&lt;/strong&gt; 或主动缩短 freshness 并保证能 purge。&lt;/p&gt;
&lt;h3 id="坑-3etag-在多实例间不一致"&gt;&lt;a href="#%e5%9d%91-3etag-%e5%9c%a8%e5%a4%9a%e5%ae%9e%e4%be%8b%e9%97%b4%e4%b8%8d%e4%b8%80%e8%87%b4" class="header-anchor"&gt;&lt;/a&gt;坑 3：ETag 在多实例间不一致
&lt;/h3&gt;&lt;p&gt;滚动发布时实例 A/B 对同一文件算出不同 ETag（例如混入了进程启动时间），客户端会在 200/304 间抖动，缓存命中率暴跌。校验器生成必须&lt;strong&gt;内容决定论&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id="坑-4中间层剥掉了校验器或改写了-cache-control"&gt;&lt;a href="#%e5%9d%91-4%e4%b8%ad%e9%97%b4%e5%b1%82%e5%89%a5%e6%8e%89%e4%ba%86%e6%a0%a1%e9%aa%8c%e5%99%a8%e6%88%96%e6%94%b9%e5%86%99%e4%ba%86-cache-control" class="header-anchor"&gt;&lt;/a&gt;坑 4：中间层剥掉了校验器或改写了 Cache-Control
&lt;/h3&gt;&lt;p&gt;排查命令：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 看源站&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -sI https://origin.example.com/static/app.js &lt;span class="p"&gt;|&lt;/span&gt; sed -n &lt;span class="s1"&gt;&amp;#39;1,20p&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 看经 CDN/Nginx 后&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -sI https://www.example.com/static/app.js &lt;span class="p"&gt;|&lt;/span&gt; sed -n &lt;span class="s1"&gt;&amp;#39;1,20p&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 模拟条件请求&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;ETAG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;#34;abc123&amp;#34;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -sI -H &lt;span class="s2"&gt;&amp;#34;If-None-Match: &lt;/span&gt;&lt;span class="nv"&gt;$ETAG&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; https://www.example.com/static/app.js &lt;span class="p"&gt;|&lt;/span&gt; sed -n &lt;span class="s1"&gt;&amp;#39;1,15p&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;对比：&lt;code&gt;ETag&lt;/code&gt;、&lt;code&gt;Cache-Control&lt;/code&gt;、&lt;code&gt;Age&lt;/code&gt;、&lt;code&gt;CF-Cache-Status&lt;/code&gt;/&lt;code&gt;X-Cache&lt;/code&gt; 等是否符合预期。第二次应看到 &lt;code&gt;304&lt;/code&gt;（在 ETag 仍匹配时）。&lt;/p&gt;
&lt;h3 id="坑-5api-被共享缓存误缓存"&gt;&lt;a href="#%e5%9d%91-5api-%e8%a2%ab%e5%85%b1%e4%ba%ab%e7%bc%93%e5%ad%98%e8%af%af%e7%bc%93%e5%ad%98" class="header-anchor"&gt;&lt;/a&gt;坑 5：API 被共享缓存误缓存
&lt;/h3&gt;&lt;p&gt;带 cookie/authorization 的响应若缺少 &lt;code&gt;private&lt;/code&gt;/&lt;code&gt;no-store&lt;/code&gt;，再叠加错误的 &lt;code&gt;public&lt;/code&gt;，可能把用户数据缓存到边缘。对用户相关响应默认保守，对真正公共的 GET 再显式放开。&lt;/p&gt;
&lt;h3 id="坑-6304-后客户端仍像没更新样式"&gt;&lt;a href="#%e5%9d%91-6304-%e5%90%8e%e5%ae%a2%e6%88%b7%e7%ab%af%e4%bb%8d%e5%83%8f%e6%b2%a1%e6%9b%b4%e6%96%b0%e6%a0%b7%e5%bc%8f" class="header-anchor"&gt;&lt;/a&gt;坑 6：304 后客户端仍像“没更新样式”
&lt;/h3&gt;&lt;p&gt;常见是 HTML 长缓存，或 Service Worker / 应用内二次缓存。协议层 304 只保证&lt;strong&gt;这一跳 HTTP 缓存协商&lt;/strong&gt;；还要检查：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;HTML 的 Cache-Control；&lt;/li&gt;
&lt;li&gt;SW 的 &lt;code&gt;cache.addAll&lt;/code&gt; 策略；&lt;/li&gt;
&lt;li&gt;应用本地 localStorage 里的资源清单。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="九选型小结"&gt;&lt;a href="#%e4%b9%9d%e9%80%89%e5%9e%8b%e5%b0%8f%e7%bb%93" class="header-anchor"&gt;&lt;/a&gt;九、选型小结
&lt;/h2&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;目标&lt;/th&gt;
					&lt;th&gt;推荐组合&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;最大化静态资源性能&lt;/td&gt;
					&lt;td&gt;内容哈希 URL + 长 &lt;code&gt;max-age&lt;/code&gt; + &lt;code&gt;public&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;入口文档尽快更新&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;no-cache&lt;/code&gt; 或短 max-age + ETag + 304&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;公共只读 API 降源站负载&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;s-maxage&lt;/code&gt; + ETag + CDN &lt;code&gt;proxy_cache_revalidate&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;用户私有数据&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;private, no-store&lt;/code&gt;，默认不进共享缓存&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;过期后仍要强一致&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;must-revalidate&lt;/code&gt; / &lt;code&gt;proxy-revalidate&lt;/code&gt; / &lt;code&gt;s-maxage&lt;/code&gt; 语义&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;原则：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;用 freshness 控制回源频率&lt;/strong&gt;；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;用 validator 控制回源体积&lt;/strong&gt;；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;用 private/no-store 控制安全边界&lt;/strong&gt;；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;用 URL 指纹解决“长缓存与发布”的矛盾&lt;/strong&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="总结"&gt;&lt;a href="#%e6%80%bb%e7%bb%93" class="header-anchor"&gt;&lt;/a&gt;总结
&lt;/h2&gt;&lt;p&gt;HTTP 缓存不是“加一个 Redis”或“在 Nginx 里 &lt;code&gt;proxy_cache on&lt;/code&gt;”这么简单，它是一套有严格语义的协议机制：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Cache-Control&lt;/code&gt; 回答&lt;strong&gt;能不能存、能存多久、过期后能不能直接用&lt;/strong&gt;；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ETag&lt;/code&gt; / &lt;code&gt;Last-Modified&lt;/code&gt; 回答&lt;strong&gt;如何证明表示是否变化&lt;/strong&gt;；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;If-None-Match&lt;/code&gt; / &lt;code&gt;If-Modified-Since&lt;/code&gt; 与 &lt;code&gt;304&lt;/code&gt; 回答&lt;strong&gt;如何在确认未变时省略 body&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;把这三层设计对齐后，静态站点、前后端分离入口、CDN 边缘缓存和公共 API 都能在&lt;strong&gt;带宽、延迟、一致性&lt;/strong&gt;之间取得可解释的平衡。下次再看到“用户一直是旧页面”或“源站带宽无故升高”，先把响应头和一次条件请求抓出来——答案通常就在 &lt;code&gt;Cache-Control&lt;/code&gt; 与校验器的协作里。&lt;/p&gt;
&lt;h2 id="参考资料"&gt;&lt;a href="#%e5%8f%82%e8%80%83%e8%b5%84%e6%96%99" class="header-anchor"&gt;&lt;/a&gt;参考资料
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;IETF RFC 9111：&lt;a class="link" href="https://www.rfc-editor.org/rfc/rfc9111" target="_blank" rel="noopener"
 &gt;HTTP Caching&lt;/a&gt;（新鲜度、验证、&lt;code&gt;Cache-Control&lt;/code&gt; 指令语义）&lt;/li&gt;
&lt;li&gt;IETF RFC 9110：&lt;a class="link" href="https://www.rfc-editor.org/rfc/rfc9110" target="_blank" rel="noopener"
 &gt;HTTP Semantics&lt;/a&gt;（&lt;code&gt;ETag&lt;/code&gt;、&lt;code&gt;Last-Modified&lt;/code&gt;、&lt;code&gt;If-None-Match&lt;/code&gt;、&lt;code&gt;If-Modified-Since&lt;/code&gt;、&lt;code&gt;304 Not Modified&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;MDN：&lt;a class="link" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching" target="_blank" rel="noopener"
 &gt;HTTP caching&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;MDN：&lt;a class="link" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag" target="_blank" rel="noopener"
 &gt;ETag&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;MDN：&lt;a class="link" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-None-Match" target="_blank" rel="noopener"
 &gt;If-None-Match&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;MDN：&lt;a class="link" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control" target="_blank" rel="noopener"
 &gt;Cache-Control&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Nginx 文档：&lt;a class="link" href="https://nginx.org/en/docs/http/ngx_http_headers_module.html" target="_blank" rel="noopener"
 &gt;ngx_http_headers_module&lt;/a&gt;（&lt;code&gt;expires&lt;/code&gt; / &lt;code&gt;add_header&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;Nginx 文档：&lt;a class="link" href="https://nginx.org/en/docs/http/ngx_http_proxy_module.html" target="_blank" rel="noopener"
 &gt;ngx_http_proxy_module&lt;/a&gt;（&lt;code&gt;proxy_cache_revalidate&lt;/code&gt;、&lt;code&gt;proxy_cache_use_stale&lt;/code&gt;、&lt;code&gt;proxy_cache_valid&lt;/code&gt;）&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Redis 缓存穿透、击穿与雪崩：原理、场景与工程解法</title><link>https://blog.waihost.com/posts/redis-cache-penetration-breakdown-avalanche/</link><pubDate>Mon, 13 Jul 2026 00:00:00 +0800</pubDate><guid>https://blog.waihost.com/posts/redis-cache-penetration-breakdown-avalanche/</guid><description>&lt;img src="https://blog.waihost.com/images/covers/redis-cache-penetration-breakdown-avalanche.svg" alt="Featured image of post Redis 缓存穿透、击穿与雪崩：原理、场景与工程解法" /&gt;&lt;p&gt;在高并发系统里，Redis 常常被放在数据库前面做读加速。一旦缓存设计不当，问题往往不是“Redis 慢一点”，而是数据库被瞬间打穿。工程上最常见、也最容易被混为一谈的三类故障是：&lt;strong&gt;缓存穿透&lt;/strong&gt;、&lt;strong&gt;缓存击穿&lt;/strong&gt;和&lt;strong&gt;缓存雪崩&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;这三类问题听起来相似，根因却不同：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;问题&lt;/th&gt;
					&lt;th&gt;典型特征&lt;/th&gt;
					&lt;th&gt;直接伤害&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;缓存穿透&lt;/td&gt;
					&lt;td&gt;查的是不存在的数据，缓存和 DB 都没有&lt;/td&gt;
					&lt;td&gt;恶意或异常请求反复打到 DB&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;缓存击穿&lt;/td&gt;
					&lt;td&gt;某个热点 key 刚好过期&lt;/td&gt;
					&lt;td&gt;同一时刻大量请求打到 DB&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;缓存雪崩&lt;/td&gt;
					&lt;td&gt;大量 key 同时失效，或缓存层整体不可用&lt;/td&gt;
					&lt;td&gt;系统级读流量直接压垮 DB&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;本文从现象、根因、可落地方案三层拆开，并结合 Redis 官方能力（&lt;code&gt;SET&lt;/code&gt; 的 &lt;code&gt;NX/EX&lt;/code&gt;、&lt;code&gt;EXPIRE&lt;/code&gt;、淘汰策略、分布式锁模式）给出可复用实践。&lt;/p&gt;
&lt;h2 id="一先把请求路径画清楚"&gt;&lt;a href="#%e4%b8%80%e5%85%88%e6%8a%8a%e8%af%b7%e6%b1%82%e8%b7%af%e5%be%84%e7%94%bb%e6%b8%85%e6%a5%9a" class="header-anchor"&gt;&lt;/a&gt;一、先把请求路径画清楚
&lt;/h2&gt;&lt;p&gt;一个典型读路径是：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Client -&amp;gt; App -&amp;gt; Redis -&amp;gt; (miss) -&amp;gt; Database -&amp;gt; write back Redis -&amp;gt; response
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;缓存层的价值在于：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;把高频读从数据库剥离出来；&lt;/li&gt;
&lt;li&gt;用更低延迟响应热点数据；&lt;/li&gt;
&lt;li&gt;给数据库争取容量和稳定性。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;因此，任何“绕过缓存直接打 DB”的路径，都要被当成故障设计点来处理。&lt;/p&gt;
&lt;h2 id="二缓存穿透查不存在的数据"&gt;&lt;a href="#%e4%ba%8c%e7%bc%93%e5%ad%98%e7%a9%bf%e9%80%8f%e6%9f%a5%e4%b8%8d%e5%ad%98%e5%9c%a8%e7%9a%84%e6%95%b0%e6%8d%ae" class="header-anchor"&gt;&lt;/a&gt;二、缓存穿透：查不存在的数据
&lt;/h2&gt;&lt;h3 id="1-现象"&gt;&lt;a href="#1-%e7%8e%b0%e8%b1%a1" class="header-anchor"&gt;&lt;/a&gt;1. 现象
&lt;/h3&gt;&lt;p&gt;请求的 key 在业务上不存在，例如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;用户 ID &lt;code&gt;-1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;商品 ID &lt;code&gt;999999999&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;被爬虫扫出来的非法参数&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;此时 Redis 没有缓存，数据库也查不到记录。如果应用只在“查到结果时才写缓存”，那么每次请求都会落到数据库。&lt;/p&gt;
&lt;h3 id="2-根因"&gt;&lt;a href="#2-%e6%a0%b9%e5%9b%a0" class="header-anchor"&gt;&lt;/a&gt;2. 根因
&lt;/h3&gt;&lt;p&gt;穿透的本质不是“缓存过期”，而是：&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&lt;strong&gt;空结果没有被负缓存（negative caching）保护。&lt;/strong&gt;&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;h3 id="3-解法"&gt;&lt;a href="#3-%e8%a7%a3%e6%b3%95" class="header-anchor"&gt;&lt;/a&gt;3. 解法
&lt;/h3&gt;&lt;h4 id="方案-a缓存空对象最常用"&gt;&lt;a href="#%e6%96%b9%e6%a1%88-a%e7%bc%93%e5%ad%98%e7%a9%ba%e5%af%b9%e8%b1%a1%e6%9c%80%e5%b8%b8%e7%94%a8" class="header-anchor"&gt;&lt;/a&gt;方案 A：缓存空对象（最常用）
&lt;/h4&gt;&lt;p&gt;查库为空时，也写入一个短 TTL 的空值标记：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 伪命令：空结果缓存 60 秒&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;SET product:999999999 __NULL__ EX &lt;span class="m"&gt;60&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;应用读到 &lt;code&gt;__NULL__&lt;/code&gt; 时直接返回“不存在”，不再访问数据库。&lt;/p&gt;
&lt;p&gt;注意：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TTL 不要太长，避免真实数据刚创建后长时间读不到；&lt;/li&gt;
&lt;li&gt;空值要有统一约定，避免和正常业务值冲突；&lt;/li&gt;
&lt;li&gt;对写路径做缓存失效/更新，保证最终一致。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="方案-b布隆过滤器bloom-filter前置拦截"&gt;&lt;a href="#%e6%96%b9%e6%a1%88-b%e5%b8%83%e9%9a%86%e8%bf%87%e6%bb%a4%e5%99%a8bloom-filter%e5%89%8d%e7%bd%ae%e6%8b%a6%e6%88%aa" class="header-anchor"&gt;&lt;/a&gt;方案 B：布隆过滤器（Bloom Filter）前置拦截
&lt;/h4&gt;&lt;p&gt;对“可能存在”的 ID 集合做布隆过滤器：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;数据写入时同步/异步加入过滤器；&lt;/li&gt;
&lt;li&gt;查询前先判断“一定不存在还是可能存在”；&lt;/li&gt;
&lt;li&gt;一定不存在则直接拒绝，不打 Redis/DB。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;适合海量 ID、爬虫扫描、开放查询接口。代价是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;有极低误判率（把不存在判断成可能存在）；&lt;/li&gt;
&lt;li&gt;需要维护过滤器更新与扩容。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="方案-c参数与权限校验前置"&gt;&lt;a href="#%e6%96%b9%e6%a1%88-c%e5%8f%82%e6%95%b0%e4%b8%8e%e6%9d%83%e9%99%90%e6%a0%a1%e9%aa%8c%e5%89%8d%e7%bd%ae" class="header-anchor"&gt;&lt;/a&gt;方案 C：参数与权限校验前置
&lt;/h4&gt;&lt;p&gt;很多穿透其实是脏流量：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;非数字 ID&lt;/li&gt;
&lt;li&gt;超范围分页&lt;/li&gt;
&lt;li&gt;未登录却访问私有资源&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在网关/应用入口直接拦截，比把压力交给缓存更划算。&lt;/p&gt;
&lt;h2 id="三缓存击穿热点-key-刚好失效"&gt;&lt;a href="#%e4%b8%89%e7%bc%93%e5%ad%98%e5%87%bb%e7%a9%bf%e7%83%ad%e7%82%b9-key-%e5%88%9a%e5%a5%bd%e5%a4%b1%e6%95%88" class="header-anchor"&gt;&lt;/a&gt;三、缓存击穿：热点 key 刚好失效
&lt;/h2&gt;&lt;h3 id="1-现象-1"&gt;&lt;a href="#1-%e7%8e%b0%e8%b1%a1-1" class="header-anchor"&gt;&lt;/a&gt;1. 现象
&lt;/h3&gt;&lt;p&gt;某个非常热的 key（首页配置、秒杀库存视图、热门商品详情）在某一瞬间过期。&lt;br&gt;
下一毫秒，成千上万请求同时看到 miss，一起去查数据库，形成“单点热点打穿”。&lt;/p&gt;
&lt;h3 id="2-根因-1"&gt;&lt;a href="#2-%e6%a0%b9%e5%9b%a0-1" class="header-anchor"&gt;&lt;/a&gt;2. 根因
&lt;/h3&gt;&lt;p&gt;击穿关注的是：&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&lt;strong&gt;单个热点 key 过期窗口内的并发回源。&lt;/strong&gt;&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;它和穿透不同：数据本来存在，只是缓存刚好空了。&lt;/p&gt;
&lt;h3 id="3-解法-1"&gt;&lt;a href="#3-%e8%a7%a3%e6%b3%95-1" class="header-anchor"&gt;&lt;/a&gt;3. 解法
&lt;/h3&gt;&lt;h4 id="方案-a互斥重建singleflight--分布式锁"&gt;&lt;a href="#%e6%96%b9%e6%a1%88-a%e4%ba%92%e6%96%a5%e9%87%8d%e5%bb%basingleflight--%e5%88%86%e5%b8%83%e5%bc%8f%e9%94%81" class="header-anchor"&gt;&lt;/a&gt;方案 A：互斥重建（singleflight / 分布式锁）
&lt;/h4&gt;&lt;p&gt;只有一个请求负责回源重建，其他请求等待或短暂失败重试。&lt;/p&gt;
&lt;p&gt;Redis 官方 &lt;code&gt;SET&lt;/code&gt; 支持原子条件写入，可用 &lt;code&gt;NX + EX&lt;/code&gt; 做互斥：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 抢重建锁，5 秒自动过期，防止进程崩溃后死锁&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;SET rebuild:lock:product:1001 &lt;span class="m"&gt;1&lt;/span&gt; NX EX &lt;span class="m"&gt;5&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;ol&gt;
&lt;li&gt;读缓存 miss；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SET lock NX EX&lt;/code&gt; 抢锁；&lt;/li&gt;
&lt;li&gt;抢到锁的请求查 DB，写回缓存，释放锁；&lt;/li&gt;
&lt;li&gt;没抢到锁的请求短暂 sleep/重试读缓存，或返回旧兜底。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;官方文档明确：&lt;code&gt;SET key value NX EX seconds&lt;/code&gt; 是设置值并带过期时间的常用原子写法；&lt;code&gt;SETNX&lt;/code&gt; 在新代码中更推荐用 &lt;code&gt;SET ... NX&lt;/code&gt; 替代。&lt;/p&gt;
&lt;h4 id="方案-b逻辑过期logical-expire"&gt;&lt;a href="#%e6%96%b9%e6%a1%88-b%e9%80%bb%e8%be%91%e8%bf%87%e6%9c%9flogical-expire" class="header-anchor"&gt;&lt;/a&gt;方案 B：逻辑过期（logical expire）
&lt;/h4&gt;&lt;p&gt;缓存 value 内嵌业务过期时间，物理 key 不过期或过期很久：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;data&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1001&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;price&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;99&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;expireAt&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1720857600&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&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;ol&gt;
&lt;li&gt;未逻辑过期：直接返回；&lt;/li&gt;
&lt;li&gt;已逻辑过期：返回旧数据，同时异步触发重建。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;优点是用户几乎无感 miss；代价是要接受短暂脏读，并做好异步重建失败补偿。&lt;/p&gt;
&lt;h4 id="方案-c热点-key-永不过期--主动更新"&gt;&lt;a href="#%e6%96%b9%e6%a1%88-c%e7%83%ad%e7%82%b9-key-%e6%b0%b8%e4%b8%8d%e8%bf%87%e6%9c%9f--%e4%b8%bb%e5%8a%a8%e6%9b%b4%e6%96%b0" class="header-anchor"&gt;&lt;/a&gt;方案 C：热点 key 永不过期 + 主动更新
&lt;/h4&gt;&lt;p&gt;对真正的超级热点，不依赖 TTL 被动失效，而由写路径/定时任务主动更新。&lt;br&gt;
TTL 更适合普通数据；热点数据更适合“写时更新 + 监控”。&lt;/p&gt;
&lt;h2 id="四缓存雪崩大面积失效或缓存层故障"&gt;&lt;a href="#%e5%9b%9b%e7%bc%93%e5%ad%98%e9%9b%aa%e5%b4%a9%e5%a4%a7%e9%9d%a2%e7%a7%af%e5%a4%b1%e6%95%88%e6%88%96%e7%bc%93%e5%ad%98%e5%b1%82%e6%95%85%e9%9a%9c" class="header-anchor"&gt;&lt;/a&gt;四、缓存雪崩：大面积失效或缓存层故障
&lt;/h2&gt;&lt;h3 id="1-现象-2"&gt;&lt;a href="#1-%e7%8e%b0%e8%b1%a1-2" class="header-anchor"&gt;&lt;/a&gt;1. 现象
&lt;/h3&gt;&lt;p&gt;两类雪崩最常见：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;过期雪崩&lt;/strong&gt;：大量 key 在同一时间点过期；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可用性雪崩&lt;/strong&gt;：Redis 集群故障、网络分区、连接打满，导致缓存层整体不可用。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;结果都是数据库在短时间内接到远超容量的读请求。&lt;/p&gt;
&lt;h3 id="2-根因-2"&gt;&lt;a href="#2-%e6%a0%b9%e5%9b%a0-2" class="header-anchor"&gt;&lt;/a&gt;2. 根因
&lt;/h3&gt;&lt;p&gt;雪崩的核心不是某一个 key，而是：&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&lt;strong&gt;缓存保护面在同一时间失效，流量失去缓冲层。&lt;/strong&gt;&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;h3 id="3-解法-2"&gt;&lt;a href="#3-%e8%a7%a3%e6%b3%95-2" class="header-anchor"&gt;&lt;/a&gt;3. 解法
&lt;/h3&gt;&lt;h4 id="方案-attl-加随机抖动"&gt;&lt;a href="#%e6%96%b9%e6%a1%88-attl-%e5%8a%a0%e9%9a%8f%e6%9c%ba%e6%8a%96%e5%8a%a8" class="header-anchor"&gt;&lt;/a&gt;方案 A：TTL 加随机抖动
&lt;/h4&gt;&lt;p&gt;不要让同类数据使用完全相同的过期时间：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ttl = base_ttl + random(0, jitter)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;# 例如 3600 + random(0, 300)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这样可以把过期曲线打散，避免整点集体回源。&lt;/p&gt;
&lt;h4 id="方案-b多级缓存与本地缓存"&gt;&lt;a href="#%e6%96%b9%e6%a1%88-b%e5%a4%9a%e7%ba%a7%e7%bc%93%e5%ad%98%e4%b8%8e%e6%9c%ac%e5%9c%b0%e7%bc%93%e5%ad%98" class="header-anchor"&gt;&lt;/a&gt;方案 B：多级缓存与本地缓存
&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;本地 Caffeine/Guava Cache 挡住进程内重复读；&lt;/li&gt;
&lt;li&gt;Redis 作为分布式缓存；&lt;/li&gt;
&lt;li&gt;DB 作为最终数据源。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;本地缓存 TTL 更短，可显著降低 Redis 抖动对 DB 的传导。&lt;/p&gt;
&lt;h4 id="方案-c限流熔断降级"&gt;&lt;a href="#%e6%96%b9%e6%a1%88-c%e9%99%90%e6%b5%81%e7%86%94%e6%96%ad%e9%99%8d%e7%ba%a7" class="header-anchor"&gt;&lt;/a&gt;方案 C：限流、熔断、降级
&lt;/h4&gt;&lt;p&gt;缓存层异常时，应用不能无脑回源：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;对回源 QPS 做令牌桶/漏桶限流；&lt;/li&gt;
&lt;li&gt;数据库错误率升高时熔断；&lt;/li&gt;
&lt;li&gt;非核心接口返回兜底页/默认配置；&lt;/li&gt;
&lt;li&gt;核心读接口优先返回短暂旧数据。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="方案-d正确使用-redis-内存淘汰策略"&gt;&lt;a href="#%e6%96%b9%e6%a1%88-d%e6%ad%a3%e7%a1%ae%e4%bd%bf%e7%94%a8-redis-%e5%86%85%e5%ad%98%e6%b7%98%e6%b1%b0%e7%ad%96%e7%95%a5" class="header-anchor"&gt;&lt;/a&gt;方案 D：正确使用 Redis 内存淘汰策略
&lt;/h4&gt;&lt;p&gt;当 Redis 用作缓存时，官方建议配置最大内存和淘汰策略，以便内存触顶时按策略驱逐 key，而不是写失败或无规划崩溃。常见策略包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;allkeys-lru&lt;/code&gt; / &lt;code&gt;allkeys-lfu&lt;/code&gt;：在全部 key 中淘汰；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;volatile-lru&lt;/code&gt; / &lt;code&gt;volatile-lfu&lt;/code&gt; / &lt;code&gt;volatile-ttl&lt;/code&gt;：只在设置了过期时间的 key 中淘汰。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;关键点：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;缓存场景应显式设置 &lt;code&gt;maxmemory&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;按业务选择 LRU/LFU；&lt;/li&gt;
&lt;li&gt;监控 &lt;code&gt;evicted_keys&lt;/code&gt;、命中率、内存使用，避免“默默淘汰关键数据”却无人知晓。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;淘汰策略解决的是&lt;strong&gt;内存压力下的可持续性&lt;/strong&gt;，不能替代业务层的穿透/击穿防护，但能降低缓存层自身失控概率。&lt;/p&gt;
&lt;h2 id="五一套可落地的组合策略"&gt;&lt;a href="#%e4%ba%94%e4%b8%80%e5%a5%97%e5%8f%af%e8%90%bd%e5%9c%b0%e7%9a%84%e7%bb%84%e5%90%88%e7%ad%96%e7%95%a5" class="header-anchor"&gt;&lt;/a&gt;五、一套可落地的组合策略
&lt;/h2&gt;&lt;p&gt;真实系统很少只选一种方案，更常见的是分层组合：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;1. 入口校验：挡住非法参数
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;2. 布隆过滤器/存在性索引：挡住明显不存在的 key
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;3. Redis 读：命中则返回
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;4. miss 时：
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; - 热点 key：互斥重建或逻辑过期
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; - 普通 key：允许有限并发回源
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;5. DB 空结果：写短 TTL 空缓存
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;6. DB 有结果：写缓存，TTL 加抖动
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;7. 全链路：限流 + 熔断 + 监控告警
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="伪代码示例"&gt;&lt;a href="#%e4%bc%aa%e4%bb%a3%e7%a0%81%e7%a4%ba%e4%be%8b" class="header-anchor"&gt;&lt;/a&gt;伪代码示例
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_product&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;valid_id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;bloom&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;bloom&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;might_contain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;cached&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;product:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;product_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;cached&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;__NULL__&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;cached&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cached&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 热点互斥重建&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;locked&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;rebuild:lock:product:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;product_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nx&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;locked&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;get_product&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# 简化：重试读缓存&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;query_product&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;product:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;product_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;__NULL__&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;product:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;product_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3600&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;randint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;rebuild:lock:product:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;product_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&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;ul&gt;
&lt;li&gt;&lt;code&gt;SET ... NX EX&lt;/code&gt; 用于短锁，避免击穿时并发回源；&lt;/li&gt;
&lt;li&gt;空值缓存用于防穿透；&lt;/li&gt;
&lt;li&gt;TTL 抖动用于打散过期，降低雪崩概率。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="六排查清单线上怎么快速判断是哪一种"&gt;&lt;a href="#%e5%85%ad%e6%8e%92%e6%9f%a5%e6%b8%85%e5%8d%95%e7%ba%bf%e4%b8%8a%e6%80%8e%e4%b9%88%e5%bf%ab%e9%80%9f%e5%88%a4%e6%96%ad%e6%98%af%e5%93%aa%e4%b8%80%e7%a7%8d" class="header-anchor"&gt;&lt;/a&gt;六、排查清单：线上怎么快速判断是哪一种
&lt;/h2&gt;&lt;h3 id="1-先看指标"&gt;&lt;a href="#1-%e5%85%88%e7%9c%8b%e6%8c%87%e6%a0%87" class="header-anchor"&gt;&lt;/a&gt;1. 先看指标
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;Redis 命中率是否骤降&lt;/li&gt;
&lt;li&gt;Redis 连接数/超时是否升高&lt;/li&gt;
&lt;li&gt;数据库 QPS、慢查询、连接池是否打满&lt;/li&gt;
&lt;li&gt;是否集中在某几个 key，还是大面积 key&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="2-判断路径"&gt;&lt;a href="#2-%e5%88%a4%e6%96%ad%e8%b7%af%e5%be%84" class="header-anchor"&gt;&lt;/a&gt;2. 判断路径
&lt;/h3&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;观察&lt;/th&gt;
					&lt;th&gt;更可能是&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;大量非法/不存在 ID，DB 查空很多&lt;/td&gt;
					&lt;td&gt;穿透&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;单个热 key 流量尖峰，过期瞬间 DB 飙升&lt;/td&gt;
					&lt;td&gt;击穿&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;整点或批次任务后大面积 miss&lt;/td&gt;
					&lt;td&gt;过期雪崩&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Redis 超时/主从切换/集群异常同时发生&lt;/td&gt;
					&lt;td&gt;可用性雪崩&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="3-临时止血"&gt;&lt;a href="#3-%e4%b8%b4%e6%97%b6%e6%ad%a2%e8%a1%80" class="header-anchor"&gt;&lt;/a&gt;3. 临时止血
&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;对异常查询参数限流；&lt;/li&gt;
&lt;li&gt;对热点 key 预热并临时延长 TTL；&lt;/li&gt;
&lt;li&gt;打开空值缓存；&lt;/li&gt;
&lt;li&gt;给 DB 回源加总开关/配额；&lt;/li&gt;
&lt;li&gt;非核心读降级。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="七设计原则总结"&gt;&lt;a href="#%e4%b8%83%e8%ae%be%e8%ae%a1%e5%8e%9f%e5%88%99%e6%80%bb%e7%bb%93" class="header-anchor"&gt;&lt;/a&gt;七、设计原则总结
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;缓存不是数据库的影子，是保护层。&lt;/strong&gt; 任何 miss 路径都要有预算。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;空结果也是结果。&lt;/strong&gt; 不做负缓存，就容易被穿透。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;热点不能靠“碰巧没过期”。&lt;/strong&gt; 要有互斥重建、逻辑过期或主动更新。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;过期时间要工程化。&lt;/strong&gt; 相同 TTL 是雪崩温床，抖动是基本操作。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;缓存故障要有降级。&lt;/strong&gt; 没有限流和熔断的缓存架构，只是把风险延后到数据库。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可观测性比“感觉很快”更重要。&lt;/strong&gt; 命中率、回源 QPS、淘汰数、锁竞争都要可监控。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="八什么时候不该过度设计"&gt;&lt;a href="#%e5%85%ab%e4%bb%80%e4%b9%88%e6%97%b6%e5%80%99%e4%b8%8d%e8%af%a5%e8%bf%87%e5%ba%a6%e8%ae%be%e8%ae%a1" class="header-anchor"&gt;&lt;/a&gt;八、什么时候不该过度设计
&lt;/h2&gt;&lt;p&gt;不是所有系统都要上布隆过滤器 + 逻辑过期 + 多级缓存：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;读 QPS 不高、数据几乎都存在：做好空值缓存和基础 TTL 即可；&lt;/li&gt;
&lt;li&gt;写极少、读极多的配置类数据：适合主动更新 + 较长 TTL；&lt;/li&gt;
&lt;li&gt;强一致要求极高的余额/库存：缓存只能做辅助，核心以数据库/专用组件为准，并明确一致性模型。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;架构选择应匹配流量模型和一致性要求，而不是堆名词。&lt;/p&gt;
&lt;h2 id="总结"&gt;&lt;a href="#%e6%80%bb%e7%bb%93" class="header-anchor"&gt;&lt;/a&gt;总结
&lt;/h2&gt;&lt;p&gt;缓存穿透、击穿、雪崩并不是三个神秘黑话，而是三条清晰的失败路径：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;穿透&lt;/strong&gt;：不存在的数据反复打穿 DB；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;击穿&lt;/strong&gt;：热点 key 失效瞬间被并发打穿；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;雪崩&lt;/strong&gt;：保护面大面积失效，流量失去缓冲。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;把问题定义清楚后，解法会自然收敛到：入口校验、空值缓存、互斥重建、TTL 抖动、淘汰策略、限流熔断与监控。&lt;br&gt;
Redis 提供了 &lt;code&gt;SET NX/EX&lt;/code&gt;、&lt;code&gt;EXPIRE&lt;/code&gt;、内存淘汰和分布式锁模式等基础能力，但真正决定系统稳不稳的，是你有没有把这些能力嵌进完整的读路径设计里。&lt;/p&gt;
&lt;h2 id="参考资料"&gt;&lt;a href="#%e5%8f%82%e8%80%83%e8%b5%84%e6%96%99" class="header-anchor"&gt;&lt;/a&gt;参考资料
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;Redis 官方文档：&lt;a class="link" href="https://redis.io/docs/latest/commands/set/" target="_blank" rel="noopener"
 &gt;&lt;code&gt;SET&lt;/code&gt; 命令&lt;/a&gt;（含 &lt;code&gt;NX&lt;/code&gt; / &lt;code&gt;EX&lt;/code&gt; 等选项）&lt;/li&gt;
&lt;li&gt;Redis 官方文档：&lt;a class="link" href="https://redis.io/docs/latest/commands/expire/" target="_blank" rel="noopener"
 &gt;&lt;code&gt;EXPIRE&lt;/code&gt; 命令&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Redis 官方文档：&lt;a class="link" href="https://redis.io/docs/latest/commands/setnx/" target="_blank" rel="noopener"
 &gt;&lt;code&gt;SETNX&lt;/code&gt; 命令&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Redis 官方文档：&lt;a class="link" href="https://redis.io/docs/latest/develop/reference/eviction/" target="_blank" rel="noopener"
 &gt;Key eviction 内存淘汰策略&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Redis 官方文档：&lt;a class="link" href="https://redis.io/docs/latest/develop/use/patterns/distributed-locks/" target="_blank" rel="noopener"
 &gt;Distributed locks 分布式锁模式&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>MySQL深度调优实战：从SQL到架构的全面优化</title><link>https://blog.waihost.com/posts/mysql-deep-optimization/</link><pubDate>Fri, 26 Jun 2026 17:00:00 +0000</pubDate><guid>https://blog.waihost.com/posts/mysql-deep-optimization/</guid><description>&lt;img src="https://blog.waihost.com/images/covers/mysql-deep-optimization.svg" alt="Featured image of post MySQL深度调优实战：从SQL到架构的全面优化" /&gt;&lt;h2 id="前言"&gt;&lt;a href="#%e5%89%8d%e8%a8%80" class="header-anchor"&gt;&lt;/a&gt;前言
&lt;/h2&gt;&lt;p&gt;MySQL 是最流行的关系型数据库之一，但在生产环境中，性能问题往往是开发和运维面临的最大挑战。本文将从 SQL 优化、索引设计、配置调优、架构优化四个维度，全面介绍 MySQL 性能调优的实战技巧。&lt;/p&gt;
&lt;h2 id="一sql-优化"&gt;&lt;a href="#%e4%b8%80sql-%e4%bc%98%e5%8c%96" class="header-anchor"&gt;&lt;/a&gt;一、SQL 优化
&lt;/h2&gt;&lt;h3 id="11-explain-执行计划分析"&gt;&lt;a href="#11-explain-%e6%89%a7%e8%a1%8c%e8%ae%a1%e5%88%92%e5%88%86%e6%9e%90" class="header-anchor"&gt;&lt;/a&gt;1.1 EXPLAIN 执行计划分析
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;EXPLAIN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AND&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Beijing&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;关键字段说明：&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;字段&lt;/th&gt;
					&lt;th&gt;说明&lt;/th&gt;
					&lt;th&gt;优化目标&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;type&lt;/td&gt;
					&lt;td&gt;访问类型&lt;/td&gt;
					&lt;td&gt;至少达到 range 级别&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;key&lt;/td&gt;
					&lt;td&gt;使用的索引&lt;/td&gt;
					&lt;td&gt;避免 NULL&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;rows&lt;/td&gt;
					&lt;td&gt;扫描行数&lt;/td&gt;
					&lt;td&gt;越小越好&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Extra&lt;/td&gt;
					&lt;td&gt;额外信息&lt;/td&gt;
					&lt;td&gt;避免 Using filesort/temporary&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;type 访问类型（从好到差）：&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;system&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;eq_ref&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ref&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ALL&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="12-索引优化技巧"&gt;&lt;a href="#12-%e7%b4%a2%e5%bc%95%e4%bc%98%e5%8c%96%e6%8a%80%e5%b7%a7" class="header-anchor"&gt;&lt;/a&gt;1.2 索引优化技巧
&lt;/h3&gt;&lt;h4 id="最左前缀原则"&gt;&lt;a href="#%e6%9c%80%e5%b7%a6%e5%89%8d%e7%bc%80%e5%8e%9f%e5%88%99" class="header-anchor"&gt;&lt;/a&gt;最左前缀原则
&lt;/h4&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 联合索引 (a, b, c)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- ✅ 命中索引
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;table&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;table&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AND&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;table&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AND&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AND&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- ❌ 无法使用索引
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;table&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;table&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;table&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AND&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="覆盖索引"&gt;&lt;a href="#%e8%a6%86%e7%9b%96%e7%b4%a2%e5%bc%95" class="header-anchor"&gt;&lt;/a&gt;覆盖索引
&lt;/h4&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 创建覆盖索引
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;INDEX&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;idx_name_age&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 查询只需要索引字段，无需回表
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;John&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="索引失效场景"&gt;&lt;a href="#%e7%b4%a2%e5%bc%95%e5%a4%b1%e6%95%88%e5%9c%ba%e6%99%af" class="header-anchor"&gt;&lt;/a&gt;索引失效场景
&lt;/h4&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- ❌ 函数操作
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;YEAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;create_time&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2024&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- ✅ 改为范围查询
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;create_time&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;2024-01-01&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AND&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;create_time&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;2025-01-01&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- ❌ 隐式类型转换
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;phone&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;13800138000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- ✅ 类型匹配
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;phone&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;13800138000&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- ❌ LIKE 左模糊
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;LIKE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;%John&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- ✅ 前缀匹配
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;LIKE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;John%&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- ❌ OR 条件
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;OR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;John&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- ✅ 使用 UNION
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;UNION&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;John&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="13-查询优化"&gt;&lt;a href="#13-%e6%9f%a5%e8%af%a2%e4%bc%98%e5%8c%96" class="header-anchor"&gt;&lt;/a&gt;1.3 查询优化
&lt;/h3&gt;&lt;h4 id="分页查询优化"&gt;&lt;a href="#%e5%88%86%e9%a1%b5%e6%9f%a5%e8%af%a2%e4%bc%98%e5%8c%96" class="header-anchor"&gt;&lt;/a&gt;分页查询优化
&lt;/h4&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- ❌ 慢查询（深分页）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ORDER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;LIMIT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1000000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- ✅ 延迟关联
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;INNER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;JOIN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ORDER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;LIMIT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1000000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- ✅ 游标分页
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1000000&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ORDER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;LIMIT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="join-优化"&gt;&lt;a href="#join-%e4%bc%98%e5%8c%96" class="header-anchor"&gt;&lt;/a&gt;JOIN 优化
&lt;/h4&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 确保关联字段有索引
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;INDEX&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;idx_user_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 小表驱动大表
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;INNER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;JOIN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="子查询优化"&gt;&lt;a href="#%e5%ad%90%e6%9f%a5%e8%af%a2%e4%bc%98%e5%8c%96" class="header-anchor"&gt;&lt;/a&gt;子查询优化
&lt;/h4&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- ❌ 子查询
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- ✅ JOIN 优化
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;INNER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;JOIN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="二索引设计原则"&gt;&lt;a href="#%e4%ba%8c%e7%b4%a2%e5%bc%95%e8%ae%be%e8%ae%a1%e5%8e%9f%e5%88%99" class="header-anchor"&gt;&lt;/a&gt;二、索引设计原则
&lt;/h2&gt;&lt;h3 id="21-索引设计规范"&gt;&lt;a href="#21-%e7%b4%a2%e5%bc%95%e8%ae%be%e8%ae%a1%e8%a7%84%e8%8c%83" class="header-anchor"&gt;&lt;/a&gt;2.1 索引设计规范
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 1. 选择区分度高的列
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;DISTINCT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;selectivity&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 区分度 &amp;gt; 0.1 才考虑建索引
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 2. 联合索引字段顺序
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 区分度高的字段在前
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 查询频率高的字段在前
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 排序字段在最后
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 3. 避免过多索引
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 每张表索引不超过 5 个
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 单个索引字段不超过 5 个
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="22-索引监控"&gt;&lt;a href="#22-%e7%b4%a2%e5%bc%95%e7%9b%91%e6%8e%a7" class="header-anchor"&gt;&lt;/a&gt;2.2 索引监控
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 查看索引使用情况
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;object_schema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;object_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;count_read&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;count_fetch&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;performance_schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;table_io_waits_summary_by_index_usage&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;object_schema&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;your_database&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;ORDER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;count_read&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 查看未使用的索引
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;object_schema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;object_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index_name&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;performance_schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;table_io_waits_summary_by_index_usage&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;object_schema&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;your_database&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AND&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index_name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NOT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AND&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;count_star&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="三配置调优"&gt;&lt;a href="#%e4%b8%89%e9%85%8d%e7%bd%ae%e8%b0%83%e4%bc%98" class="header-anchor"&gt;&lt;/a&gt;三、配置调优
&lt;/h2&gt;&lt;h3 id="31-innodb-buffer-pool"&gt;&lt;a href="#31-innodb-buffer-pool" class="header-anchor"&gt;&lt;/a&gt;3.1 InnoDB Buffer Pool
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# my.cnf&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[mysqld]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Buffer Pool 大小，建议物理内存的 70-80%&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;innodb_buffer_pool_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;8G&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Buffer Pool 实例数，减少锁竞争&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;innodb_buffer_pool_instances&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;8&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Buffer Pool 预热&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;innodb_buffer_pool_dump_at_shutdown&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;ON&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;innodb_buffer_pool_load_at_startup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;ON&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="32-日志配置"&gt;&lt;a href="#32-%e6%97%a5%e5%bf%97%e9%85%8d%e7%bd%ae" class="header-anchor"&gt;&lt;/a&gt;3.2 日志配置
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[mysqld]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Redo Log 大小&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;innodb_log_file_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;1G&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;innodb_log_buffer_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;64M&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 刷盘策略&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 1：每次提交都刷盘（最安全）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 2：每次提交写入OS缓存&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 0：每秒刷盘&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;innodb_flush_log_at_trx_commit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 双1配置（生产推荐）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;sync_binlog&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="33-连接配置"&gt;&lt;a href="#33-%e8%bf%9e%e6%8e%a5%e9%85%8d%e7%bd%ae" class="header-anchor"&gt;&lt;/a&gt;3.3 连接配置
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[mysqld]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 最大连接数&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;max_connections&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;500&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 连接超时&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;wait_timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;600&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;interactive_timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;600&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 线程缓存&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;thread_cache_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;64&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 表缓存&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;table_open_cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;4096&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;table_definition_cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;2048&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="34-查询缓存mysql-80-已移除"&gt;&lt;a href="#34-%e6%9f%a5%e8%af%a2%e7%bc%93%e5%ad%98mysql-80-%e5%b7%b2%e7%a7%bb%e9%99%a4" class="header-anchor"&gt;&lt;/a&gt;3.4 查询缓存（MySQL 8.0 已移除）
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# MySQL 5.7 及以下&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[mysqld]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;query_cache_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;0 # 建议关闭&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;query_cache_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="35-排序和临时表"&gt;&lt;a href="#35-%e6%8e%92%e5%ba%8f%e5%92%8c%e4%b8%b4%e6%97%b6%e8%a1%a8" class="header-anchor"&gt;&lt;/a&gt;3.5 排序和临时表
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[mysqld]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 排序缓冲区&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;sort_buffer_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;4M&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# JOIN 缓冲区&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;join_buffer_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;4M&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 临时表大小&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;tmp_table_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;64M&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;max_heap_table_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;64M&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 读缓冲区&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;read_buffer_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;2M&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;read_rnd_buffer_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;8M&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="四架构优化"&gt;&lt;a href="#%e5%9b%9b%e6%9e%b6%e6%9e%84%e4%bc%98%e5%8c%96" class="header-anchor"&gt;&lt;/a&gt;四、架构优化
&lt;/h2&gt;&lt;h3 id="41-读写分离"&gt;&lt;a href="#41-%e8%af%bb%e5%86%99%e5%88%86%e7%a6%bb" class="header-anchor"&gt;&lt;/a&gt;4.1 读写分离
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ┌─────────────┐
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ 应用程序 │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └──────┬──────┘
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ┌──────▼──────┐
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ 代理层 │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ (ProxySQL) │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └──────┬──────┘
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ┌──────────────┼──────────────┐
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ┌──────▼──────┐ ┌────▼────┐ ┌───────▼──────┐
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ Master │ │ Slave 1 │ │ Slave 2 │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ (写) │ │ (读) │ │ (读) │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └─────────────┘ └─────────┘ └──────────────┘
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;ProxySQL 配置示例：&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 添加后端MySQL服务器
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;INTO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mysql_servers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hostgroup_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;VALUES&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;10.0.0.1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3306&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;-- 写组
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;10.0.0.2&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3306&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;-- 读组
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;10.0.0.3&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3306&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;-- 读组
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 配置读写分离规则
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;INTO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mysql_query_rules&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rule_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;match_pattern&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;destination_hostgroup&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;VALUES&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;^SELECT.*FOR UPDATE&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;-- 写操作
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;^SELECT&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;-- 读操作
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="42-分库分表"&gt;&lt;a href="#42-%e5%88%86%e5%ba%93%e5%88%86%e8%a1%a8" class="header-anchor"&gt;&lt;/a&gt;4.2 分库分表
&lt;/h3&gt;&lt;h4 id="垂直分库"&gt;&lt;a href="#%e5%9e%82%e7%9b%b4%e5%88%86%e5%ba%93" class="header-anchor"&gt;&lt;/a&gt;垂直分库
&lt;/h4&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;用户库 (user_db)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── users
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── user_profiles
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;└── user_settings
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;订单库 (order_db)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── orders
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── order_items
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;└── payments
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;商品库 (product_db)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── products
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── categories
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;└── inventory
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="水平分表"&gt;&lt;a href="#%e6%b0%b4%e5%b9%b3%e5%88%86%e8%a1%a8" class="header-anchor"&gt;&lt;/a&gt;水平分表
&lt;/h4&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 按用户ID分表
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;orders_0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;-- user_id % 4 = 0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;orders_1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;-- user_id % 4 = 1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;orders_2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;-- user_id % 4 = 2
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;orders_3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;-- user_id % 4 = 3
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- ShardingSphere 配置
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;spring&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shardingsphere&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;datasource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;names&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ds0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;ds1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;spring&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shardingsphere&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sharding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tables&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;actual&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;data&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;nodes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ds$&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;orders_$&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;spring&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shardingsphere&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sharding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tables&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;table&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;strategy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sharding&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;column&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;spring&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shardingsphere&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sharding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tables&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;table&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;strategy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;algorithm&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;expression&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;orders_$&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="43-缓存策略"&gt;&lt;a href="#43-%e7%bc%93%e5%ad%98%e7%ad%96%e7%95%a5" class="header-anchor"&gt;&lt;/a&gt;4.3 缓存策略
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;┌──────────┐ ┌──────────┐ ┌──────────┐
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ 应用程序 │───▶│ Redis │───▶│ MySQL │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;└──────────┘ └──────────┘ └──────────┘
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ 1.查询缓存 │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │──────────────▶│ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ 2.返回缓存 │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │◀──────────────│ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ 3.缓存未命中│ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │──────────────────────────────▶│
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ 4.查询数据库│ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │◀──────────────────────────────│
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ 5.写入缓存 │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │──────────────▶│ │
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;缓存更新策略：&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Cache Aside Pattern&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Long&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 1. 先查缓存&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;user:&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 2. 缓存未命中，查数据库&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;userMapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;selectById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 3. 写入缓存&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="44-表设计优化"&gt;&lt;a href="#44-%e8%a1%a8%e8%ae%be%e8%ae%a1%e4%bc%98%e5%8c%96" class="header-anchor"&gt;&lt;/a&gt;4.4 表设计优化
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 1. 选择合适的数据类型
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- ❌ 不推荐
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;BIGINT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;INT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- ✅ 推荐
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;BIGINT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;UNSIGNED&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AUTO_INCREMENT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NOT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TINYINT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;UNSIGNED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TINYINT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NOT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;DEFAULT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;PRIMARY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;INDEX&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;idx_name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ENGINE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;InnoDB&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;DEFAULT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CHARSET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;utf8mb4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 2. 避免NULL
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 使用 NOT NULL + DEFAULT
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 3. 主键设计
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 使用自增ID作为主键
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 避免使用UUID作为主键（无序，影响插入性能）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="五监控与诊断"&gt;&lt;a href="#%e4%ba%94%e7%9b%91%e6%8e%a7%e4%b8%8e%e8%af%8a%e6%96%ad" class="header-anchor"&gt;&lt;/a&gt;五、监控与诊断
&lt;/h2&gt;&lt;h3 id="51-慢查询日志"&gt;&lt;a href="#51-%e6%85%a2%e6%9f%a5%e8%af%a2%e6%97%a5%e5%bf%97" class="header-anchor"&gt;&lt;/a&gt;5.1 慢查询日志
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[mysqld]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 开启慢查询日志&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;slow_query_log&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;ON&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;slow_query_log_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;/var/log/mysql/slow.log&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;long_query_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;1 # 超过1秒记录&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;log_queries_not_using_indexes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;ON&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 分析慢查询日志&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mysqldumpslow -s t -t &lt;span class="m"&gt;10&lt;/span&gt; /var/log/mysql/slow.log
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 使用 pt-query-digest&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pt-query-digest /var/log/mysql/slow.log &amp;gt; slow_report.txt
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="52-性能监控sql"&gt;&lt;a href="#52-%e6%80%a7%e8%83%bd%e7%9b%91%e6%8e%a7sql" class="header-anchor"&gt;&lt;/a&gt;5.2 性能监控SQL
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 查看当前连接
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SHOW&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PROCESSLIST&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 查看InnoDB状态
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SHOW&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ENGINE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;INNODB&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;STATUS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 查看表大小
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;table_schema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;table_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ROUND&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data_length&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;data_mb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ROUND&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index_length&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index_mb&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;information_schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tables&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;table_schema&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;your_database&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;ORDER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;data_length&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 查看索引使用统计
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;object_schema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;object_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;count_star&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;count_read&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;count_fetch&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;performance_schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;table_io_waits_summary_by_index_usage&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;object_schema&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;your_database&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="53-诊断工具"&gt;&lt;a href="#53-%e8%af%8a%e6%96%ad%e5%b7%a5%e5%85%b7" class="header-anchor"&gt;&lt;/a&gt;5.3 诊断工具
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Percona Toolkit&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 安装&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;apt install percona-toolkit
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 分析慢查询&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pt-query-digest /var/log/mysql/slow.log
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 检查重复/冗余索引&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pt-duplicate-key-checker --host&lt;span class="o"&gt;=&lt;/span&gt;localhost --user&lt;span class="o"&gt;=&lt;/span&gt;root --password&lt;span class="o"&gt;=&lt;/span&gt;xxx
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 查看索引使用情况&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pt-index-usage --host&lt;span class="o"&gt;=&lt;/span&gt;localhost --user&lt;span class="o"&gt;=&lt;/span&gt;root --password&lt;span class="o"&gt;=&lt;/span&gt;xxx /var/log/mysql/slow.log
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="六调优清单"&gt;&lt;a href="#%e5%85%ad%e8%b0%83%e4%bc%98%e6%b8%85%e5%8d%95" class="header-anchor"&gt;&lt;/a&gt;六、调优清单
&lt;/h2&gt;&lt;h3 id="61-sql-层面"&gt;&lt;a href="#61-sql-%e5%b1%82%e9%9d%a2" class="header-anchor"&gt;&lt;/a&gt;6.1 SQL 层面
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;input disabled="" type="checkbox"&gt; 所有查询都使用了合适的索引&lt;/li&gt;
&lt;li&gt;&lt;input disabled="" type="checkbox"&gt; 避免了 &lt;code&gt;SELECT *&lt;/code&gt;，只查询需要的字段&lt;/li&gt;
&lt;li&gt;&lt;input disabled="" type="checkbox"&gt; 分页查询使用了延迟关联&lt;/li&gt;
&lt;li&gt;&lt;input disabled="" type="checkbox"&gt; 避免了深分页（LIMIT offset 过大）&lt;/li&gt;
&lt;li&gt;&lt;input disabled="" type="checkbox"&gt; JOIN 查询关联字段有索引&lt;/li&gt;
&lt;li&gt;&lt;input disabled="" type="checkbox"&gt; 避免了索引失效的写法&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="62-索引层面"&gt;&lt;a href="#62-%e7%b4%a2%e5%bc%95%e5%b1%82%e9%9d%a2" class="header-anchor"&gt;&lt;/a&gt;6.2 索引层面
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;input disabled="" type="checkbox"&gt; 高频查询字段都有索引&lt;/li&gt;
&lt;li&gt;&lt;input disabled="" type="checkbox"&gt; 联合索引遵循最左前缀原则&lt;/li&gt;
&lt;li&gt;&lt;input disabled="" type="checkbox"&gt; 区分度低的字段不建索引&lt;/li&gt;
&lt;li&gt;&lt;input disabled="" type="checkbox"&gt; 定期清理未使用的索引&lt;/li&gt;
&lt;li&gt;&lt;input disabled="" type="checkbox"&gt; 使用覆盖索引减少回表&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="63-配置层面"&gt;&lt;a href="#63-%e9%85%8d%e7%bd%ae%e5%b1%82%e9%9d%a2" class="header-anchor"&gt;&lt;/a&gt;6.3 配置层面
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;input disabled="" type="checkbox"&gt; Buffer Pool 设置为物理内存的 70-80%&lt;/li&gt;
&lt;li&gt;&lt;input disabled="" type="checkbox"&gt; 开启慢查询日志&lt;/li&gt;
&lt;li&gt;&lt;input disabled="" type="checkbox"&gt; 设置合理的连接数&lt;/li&gt;
&lt;li&gt;&lt;input disabled="" type="checkbox"&gt; 配置合适的日志刷盘策略&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="64-架构层面"&gt;&lt;a href="#64-%e6%9e%b6%e6%9e%84%e5%b1%82%e9%9d%a2" class="header-anchor"&gt;&lt;/a&gt;6.4 架构层面
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;input disabled="" type="checkbox"&gt; 读写分离（读多写少场景）&lt;/li&gt;
&lt;li&gt;&lt;input disabled="" type="checkbox"&gt; 分库分表（数据量大场景）&lt;/li&gt;
&lt;li&gt;&lt;input disabled="" type="checkbox"&gt; 引入缓存层（热点数据）&lt;/li&gt;
&lt;li&gt;&lt;input disabled="" type="checkbox"&gt; 表设计规范化&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="总结"&gt;&lt;a href="#%e6%80%bb%e7%bb%93" class="header-anchor"&gt;&lt;/a&gt;总结
&lt;/h2&gt;&lt;p&gt;MySQL 调优是一个系统工程，需要从多个维度综合考虑：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;SQL 优化是基础&lt;/strong&gt;：写好 SQL 是最直接的优化手段&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;索引设计是核心&lt;/strong&gt;：合理的索引能提升查询性能数倍&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;配置调优是保障&lt;/strong&gt;：正确的配置能最大化硬件利用率&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;架构优化是根本&lt;/strong&gt;：当单机瓶颈时，架构扩展是必由之路&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;建议按照本文的调优清单逐步检查，结合监控数据持续优化。&lt;/p&gt;</description></item></channel></rss>