<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>数据库 on 方糖猫Blog</title>
    <link>https://ftmao.com/blog/tags/%E6%95%B0%E6%8D%AE%E5%BA%93/</link>
    <description>AI 视频、AI Agent 与工程实践笔记</description>
    <generator>Hugo</generator>
    <language>zh-CN</language>
    <lastBuildDate>Tue, 26 Oct 2021 10:30:00 +0800</lastBuildDate><atom:link href="https://ftmao.com/blog/tags/%E6%95%B0%E6%8D%AE%E5%BA%93/index.xml" rel="self" type="application/rss+xml" /><item>
      <title>MongoDB 从入门到精通：存储引擎、建模、索引、副本集、分片与事务实战</title>
      <link>https://ftmao.com/blog/posts/mongodb-beginner-to-mastery/</link>
      <pubDate>Tue, 26 Oct 2021 10:30:00 +0800</pubDate>
      <guid>https://ftmao.com/blog/posts/mongodb-beginner-to-mastery/</guid>
      <description>&lt;h1 id=&#34;mongodb-从入门到精通存储引擎建模索引副本集分片与事务实战&#34;&gt;MongoDB 从入门到精通：存储引擎、建模、索引、副本集、分片与事务实战&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;目标读者：用过 MySQL 之类的关系型数据库，现在要接 MongoDB 的同学——可能是接手存量系统，可能是新业务选型，也可能是想给自己的技术栈补上 NoSQL 这一块。&lt;br&gt;
本文不浪费篇幅在「什么是文档」这种名词解释上，直接从真正影响系统生死的东西讲起：存储引擎、一致性、高可用、性能。&lt;br&gt;
写这篇的契机：2021 年我所在团队的电商中台把部分业务从 MySQL 迁到 MongoDB，整个过程脱了一层皮。这篇是我希望迁移前有人能拍在桌上的笔记。&lt;br&gt;
文内版本基线：MongoDB 5.0（2021 年 7 月发布）、官方 Java 同步驱动 4.3、JDK 11。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;全文八篇，按真实的成长路径编排：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart LR
    A[&amp;#34;一 选型认知&amp;lt;br/&amp;gt;Mongo 适合什么&amp;#34;] --&amp;gt; B[&amp;#34;二 存储引擎&amp;lt;br/&amp;gt;WiredTiger 底牌&amp;#34;]
    B --&amp;gt; C[&amp;#34;三 数据建模&amp;lt;br/&amp;gt;核心战场&amp;#34;]
    C --&amp;gt; D[&amp;#34;四 索引与 explain&amp;lt;br/&amp;gt;调优基本功&amp;#34;]
    D --&amp;gt; E[&amp;#34;五 聚合管道&amp;lt;br/&amp;gt;服务端计算&amp;#34;]
    E --&amp;gt; F[&amp;#34;六 副本集&amp;lt;br/&amp;gt;高可用与读写语义&amp;#34;]
    F --&amp;gt; G[&amp;#34;七 事务与分片&amp;lt;br/&amp;gt;规模化与一致性&amp;#34;]
    G --&amp;gt; H[&amp;#34;八 运维与 AI 时代&amp;lt;br/&amp;gt;监控备份 少写代码&amp;#34;]
    style H fill:#e8f4e8&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;学习 MongoDB 要始终围绕三个问题：&lt;strong&gt;数据安不安全、查询快不快、流量扛不扛得住&lt;/strong&gt;。这篇的每一节，都是对这三问的某一次正面回答。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;第一篇-选型认知什么时候该用-mongodb什么时候别逞强&#34;&gt;第一篇 选型认知：什么时候该用 MongoDB，什么时候别逞强&lt;/h2&gt;
&lt;p&gt;第一课不是怎么用，是&lt;strong&gt;什么时候用&lt;/strong&gt;。用错场景的 MongoDB，比不用更可怕。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TD
    subgraph 适合[&amp;#34;MongoDB 的主场&amp;#34;]
        P[&amp;#34;商品中心&amp;lt;br/&amp;gt;类目属性千差万别&amp;lt;br/&amp;gt;文档模型天然契合&amp;#34;]
        C[&amp;#34;购物车/会话&amp;lt;br/&amp;gt;读多写多 结构灵活&amp;#34;]
        L[&amp;#34;行为日志/埋点&amp;lt;br/&amp;gt;写入量大 schema 常变&amp;lt;br/&amp;gt;TTL 索引自动过期&amp;#34;]
        CM[&amp;#34;CMS/配置中心&amp;lt;br/&amp;gt;页面配置 JSON 化&amp;#34;]
    end
    subgraph 谨慎[&amp;#34;需要精心设计的边界&amp;#34;]
        O[&amp;#34;订单/库存&amp;lt;br/&amp;gt;可用 但要事务+对账设计&amp;#34;]
        M[&amp;#34;消息/Feed 流&amp;lt;br/&amp;gt;可用 注意分片键设计&amp;#34;]
    end
    subgraph 别上[&amp;#34;别硬上 Mongo&amp;#34;]
        F[&amp;#34;账务/清结算&amp;lt;br/&amp;gt;强事务 强审计&amp;lt;br/&amp;gt;交给关系型&amp;#34;]
        S[&amp;#34;复杂搜索&amp;lt;br/&amp;gt;交给 Elasticsearch&amp;#34;]
        J[&amp;#34;重度多表关联分析&amp;lt;br/&amp;gt;交给 OLAP/数仓&amp;#34;]
    end
    style 适合 fill:#e8f4e8
    style 别上 fill:#fde8e8&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;一句话原则：&lt;strong&gt;MongoDB 适合「单实体自成一体、结构多变、读多」的在线业务；不适合「钱」「搜索」「重度关联分析」。&lt;/strong&gt; 判断依据不是性能 benchmark，而是数据访问模式——先把系统里 80% 的查询列出来，看它们能不能被「单集合 + 少量 $lookup」覆盖。覆盖不了，选型本身就有问题。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;思考与感悟之一&lt;/strong&gt;：数据库选型和框架选型完全不同。框架选错了可以重构，数据库选错了是「数据搬家」级别的灾难——停机迁移、双写对账、灰度切流，每一步都在钢丝上走。所以对「不做什么」的清单，要比「做什么」的清单更长。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;第二篇-wiredtiger理解存储引擎才配谈性能&#34;&gt;第二篇 WiredTiger：理解存储引擎，才配谈性能&lt;/h2&gt;
&lt;p&gt;很多人用 MongoDB 三年，不知道底下是 WiredTiger。这就像开了一辈子车不懂发动机——平时没事，出事时只能干瞪眼。这一篇先把「数据到底怎么存的」讲透，再回答那个面试必问的问题：&lt;strong&gt;MongoDB 为什么快，为什么扛得住大量读写。&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&#34;21-底层数据模型bson磁盘上到底存了什么&#34;&gt;2.1 底层数据模型：BSON，磁盘上到底存了什么&lt;/h3&gt;
&lt;p&gt;你在应用层操作的是「文档」（可以理解成 JSON），但磁盘上存的是 &lt;strong&gt;BSON（Binary JSON）&lt;/strong&gt;——一种二进制、自描述、长度前缀的编码格式：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart LR
    subgraph BSON[&amp;#34;一个 BSON 文档的字节布局&amp;#34;]
        L[&amp;#34;int32 总长度&amp;lt;br/&amp;gt;开头 4 字节&amp;#34;] --&amp;gt; E1[&amp;#34;元素1&amp;lt;br/&amp;gt;类型字节 + 字段名 + 值&amp;#34;]
        E1 --&amp;gt; E2[&amp;#34;元素2&amp;lt;br/&amp;gt;类型字节 + 字段名 + 值&amp;#34;]
        E2 --&amp;gt; EN[&amp;#34;...&amp;#34;]
        EN --&amp;gt; Z[&amp;#34;0x00 结束符&amp;#34;]
    end
    subgraph TYPES[&amp;#34;BSON 比 JSON 多出的类型&amp;#34;]
        T1[&amp;#34;ObjectId / Date / 正则&amp;#34;]
        T2[&amp;#34;int32 / int64 / double&amp;#34;]
        T3[&amp;#34;Decimal128 高精度十进制&amp;#34;]
        T4[&amp;#34;BinData 二进制 / 数组 / 内嵌文档&amp;#34;]
    end
    BSON -.-&amp;gt; TYPES&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;BSON 的设计直接回答了两个性能问题：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;长度前缀 + 类型字节 → 遍历时可以「跳过」字段。&lt;/strong&gt; 查询只取 &lt;code&gt;name&lt;/code&gt; 字段时，引擎不需要解析整个文档，读到字段长度直接指针后移——这就是为什么投影（projection）能省时间。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;类型丰富 → 不需要序列化转换。&lt;/strong&gt; JSON 只有 string/number/boolean，日期只能存字符串再解析；BSON 原生支持 Date、ObjectId、Decimal128（钱的精确计算就靠它）、二进制，省去了应用层和存储层之间大量的类型转换开销。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;更关键的是&lt;strong&gt;文档在磁盘上是连续存放的&lt;/strong&gt;：一个商品文档（含内嵌的 SKU 数组、图片列表）在磁盘上是&lt;strong&gt;一整块连续的字节&lt;/strong&gt;。这和关系型数据库形成鲜明对比——&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TD
    subgraph MYSQL[&amp;#34;MySQL 读一个完整商品&amp;#34;]
        A1[&amp;#34;product 表的 B+ 树页&amp;#34;] --&amp;gt; J[&amp;#34;JOIN&amp;#34;]
        A2[&amp;#34;sku 表的 B+ 树页&amp;lt;br/&amp;gt;磁盘另一处&amp;#34;] --&amp;gt; J
        A3[&amp;#34;image 表的 B+ 树页&amp;lt;br/&amp;gt;磁盘又一处&amp;#34;] --&amp;gt; J
        J --&amp;gt; R[&amp;#34;多次随机 I/O&amp;lt;br/&amp;gt;才能拼出一个商品&amp;#34;]
    end
    subgraph MONGO[&amp;#34;MongoDB 读一个完整商品&amp;#34;]
        B[&amp;#34;一次磁盘寻道&amp;lt;br/&amp;gt;读出连续的一整块 BSON&amp;#34;]
    end
    style MONGO fill:#e8f4e8&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;这就是 MongoDB 设计哲学里最重要的词：&lt;strong&gt;数据局部性（data locality）&lt;/strong&gt;。「一起读的数据，就存在一起」。嵌入建模之所以是 Mongo 的灵魂，底层原因就在这里——它不是风格偏好，是磁盘 I/O 决定的。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;追问：文档是定长连续存放的，那修改、变大之后怎么办？磁盘会不连续吗？&lt;/strong&gt; 这要分两个时代说。MMAPv1 时代（3.0 之前）文档存在定长槽位里，变大就得整体搬家（document move），旧槽位变空洞，磁盘碎片是当年最臭名昭著的问题。WiredTiger 时代换了个思路：&lt;strong&gt;存储单位是 B 树的页而不是文档&lt;/strong&gt;，文档是叶页里的一条 record；修改&lt;strong&gt;从不在原地写&lt;/strong&gt;——checkpoint 时用 copy-on-write 把改后的整页写到磁盘&lt;strong&gt;新位置&lt;/strong&gt;，旧块进空闲列表复用；文档长大导致页放不下就触发&lt;strong&gt;页分裂&lt;/strong&gt;，超大文档走 overflow 页。所以「连续」的准确含义是「逻辑上一条 record、一次 I/O 读取」，物理位置本来就会搬。代价有两个：长期高频更新会积累碎片（用 &lt;code&gt;db.collection.compact()&lt;/code&gt; 定期回收），以及&lt;strong&gt;频繁长大的文档会引发频繁页分裂&lt;/strong&gt;——这是第三篇「增长边界」判断的底层原因，时序类数据的标准解法是按时间桶预分配文档的 bucket 模式，让单文档尺寸保持稳定。&lt;/p&gt;
&lt;h3 id=&#34;22-为什么快读快与写快的底层原因&#34;&gt;2.2 为什么快：读快与写快的底层原因&lt;/h3&gt;
&lt;p&gt;「MongoDB 快」不能当口号喊，要能拆成具体机制。先写后读。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;写入为什么快——一条写请求的完整路径：&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart LR
    APP[&amp;#34;应用&amp;lt;br/&amp;gt;Java 驱动&amp;#34;] --&amp;gt; M[&amp;#34;mongod&amp;lt;br/&amp;gt;内存操作&amp;#34;]
    M --&amp;gt; C[&amp;#34;WiredTiger cache&amp;lt;br/&amp;gt;修改页标记为 dirty&amp;lt;br/&amp;gt;写操作到此即完成&amp;#34;]
    M --&amp;gt; J[&amp;#34;journal 日志&amp;lt;br/&amp;gt;顺序追加写&amp;lt;br/&amp;gt;磁盘顺序 I/O 极快&amp;#34;]
    C -.-&amp;gt;|&amp;#34;checkpoint&amp;lt;br/&amp;gt;默认每 60 秒&amp;lt;br/&amp;gt;批量刷盘&amp;#34;| D[&amp;#34;数据文件&amp;lt;br/&amp;gt;压缩后的 B 树页&amp;#34;]
    style C fill:#e8f4e8&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;四个叠加的机制：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;写内存即返回&lt;/strong&gt;。写操作修改的是 cache 里的页，标记 dirty 就完事，数据文件由 checkpoint 线程&lt;strong&gt;每 60 秒批量刷一次&lt;/strong&gt;——随机写被攒成了顺序批量写。这是「写快」的第一来源。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;journal 用顺序写保证持久性&lt;/strong&gt;。怕宕机丢内存里的数据？journal（WAL 日志）是&lt;strong&gt;顺序追加写&lt;/strong&gt;，磁盘的顺序写比随机写快几个数量级。&lt;code&gt;j: true&lt;/code&gt; 的写确认就是等 journal 落盘，而不是等数据文件落盘。&lt;/p&gt;
&lt;p&gt;这里必须正面回答一个所有人都会问的问题：&lt;strong&gt;「写入内存就返回，服务器挂了怎么办？」&lt;/strong&gt; 不会变成脏数据——cache 里丢掉的只是 dirty 页的内存副本，每次修改都已先追加进 journal；mongod 重启时以最后一次 checkpoint 为基线&lt;strong&gt;重放 journal&lt;/strong&gt;，把丢失的写补回数据文件。真正的风险窗口在确认时机：默认 &lt;code&gt;w:1&lt;/code&gt; 下 journal 每 ~100ms 才批量提交，断电最多丢「最近约 100ms 且未同步到从节点」的写（第六篇丢的 23 笔订单就是这么没的）；&lt;code&gt;j: true&lt;/code&gt; 等日志落盘，单节点宕机不丢；&lt;code&gt;w: majority&lt;/code&gt; 等多数派确认，机器物理损毁也不丢。&lt;strong&gt;丢不丢数据不是 MongoDB 决定的，是你选 writeConcern 档位时决定的&lt;/strong&gt;——这套「内存脏页异步刷盘 + 日志重放」和 MySQL InnoDB 的 redo log 是同一套思想。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;文档级并发&lt;/strong&gt;。更新只锁单个文档，100 个线程写 100 个不同文档完全不互斥。对比早期 MySQL MyISAM 的表锁、乃至行锁时代的间隙锁开销，文档锁的冲突概率天然更低。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;没有跨行约束的包袱&lt;/strong&gt;。无外键检查、无触发器、单文档原子性不需要 undo log（改一个文档就是改一整块，要么全改要么不改），写入路径短。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;读取为什么快——四道防线：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;局部性&lt;/strong&gt;：如上，嵌入建模让「读一个完整实体」= 一次磁盘寻道，没有 JOIN 的多次随机 I/O。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;缓存&lt;/strong&gt;：热数据常驻 WiredTiger cache（默认一半内存），命中内存的读在微秒级。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;B 树索引&lt;/strong&gt;：索引项 O(log n) 定位，千万级数据也就 4 层左右，加覆盖索引（第四篇）连文档都不用读。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;副本集读扩展&lt;/strong&gt;：&lt;code&gt;readPreference: secondary&lt;/code&gt; 把展示类读流量分摊到从节点，读吞吐随节点数线性扩展。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;大量读 + 大量写同时扛住的原因&lt;/strong&gt;：读写分离（从节点扛读）+ MVCC（读不阻塞写、写不阻塞读）+ 写内存化。这三件事同时成立，才撑得起电商大促「几十万读 QPS 的商品页 + 几万写 QPS 的库存扣减」同框的场景。还不够？分片把写入水平切到多个副本集，写吞吐同样线性扩展（第七篇）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;快的代价（必须说清楚，否则就是忽悠）&lt;/strong&gt;：写内存即返回意味着默认 &lt;code&gt;w:1&lt;/code&gt; 下宕机可能丢最后一小段数据（第六篇的丢单事故就是这么来的）；热数据依赖内存，内存小于工作集时性能断崖；没有 JOIN 意味着关联查询要自己扛。&lt;strong&gt;MongoDB 的快是「用设计取舍换来的快」，不是魔法。&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&#34;23-wiredtiger-你必须知道的四件事&#34;&gt;2.3 WiredTiger 你必须知道的四件事&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TD
    W[&amp;#34;WiredTiger 存储引擎&amp;lt;br/&amp;gt;MongoDB 3.2+ 默认&amp;#34;] --&amp;gt; A[&amp;#34;文档级并发控制&amp;lt;br/&amp;gt;多线程写同一集合不互斥&amp;lt;br/&amp;gt;只有同一文档冲突才重试&amp;#34;]
    W --&amp;gt; B[&amp;#34;压缩存储&amp;lt;br/&amp;gt;snappy 默认&amp;lt;br/&amp;gt;磁盘占用约为数据量的 30%-70%&amp;#34;]
    W --&amp;gt; C[&amp;#34;缓存机制&amp;lt;br/&amp;gt;cacheSizeGB 默认 = 50% × (内存 - 1GB)&amp;lt;br/&amp;gt;热数据全在内存里&amp;#34;]
    W --&amp;gt; D[&amp;#34;MVCC 多版本&amp;lt;br/&amp;gt;读不阻塞写 写不阻塞读&amp;lt;br/&amp;gt;snapshot 隔离&amp;#34;]&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;strong&gt;并发改同一个文档会串行重试&lt;/strong&gt;。这直接决定建模——秒杀库存如果把 100 个 SKU 的库存都嵌在一个商品文档里，大促时这个文档就是全站最热的锁热点。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;缓存即命&lt;/strong&gt;：WiredTiger cache 默认吃掉一半内存。生产上「Mongo 内存打满」往往不是泄漏，是 cache 在正常工作——看 &lt;code&gt;wiredTiger.cache&lt;/code&gt; 指标而不是看进程 RSS 下结论。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;压缩是把双刃剑&lt;/strong&gt;：省磁盘，费 CPU。写密集型日志库可换 zlib 更高压缩比，CPU 紧张时别乱调。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MVCC 的代价&lt;/strong&gt;：老版本数据要清理，长时间不提交的事务会压住清理线程，导致 cache 中 dirty data 堆积、写入被拖慢。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;踩坑故事 #1（cache 被大事务压垮）&lt;/strong&gt;：我们有个定时任务一次性更新 200 万条商品，跑的时候整个库延迟飙升。原因：巨型事务导致 WiredTiger 无法清理旧版本，cache 中 dirty bytes 超过阈值后应用线程被迫参与淘汰（eviction），写入集体变慢。解法：&lt;strong&gt;批量操作必须拆批&lt;/strong&gt;（每批 1000 条 + 短暂让出窗口），生产环境永远不开「不设限的大批量更新」。这条后来成了团队《MongoDB 使用红线》第一条。&lt;/p&gt;
&lt;h3 id=&#34;24-本地环境直接上副本集别玩单机&#34;&gt;2.4 本地环境：直接上副本集，别玩单机&lt;/h3&gt;
&lt;p&gt;单节点 MongoDB 不支持事务，也不具备生产形态。本地练手就用 Docker Compose 起一套三节点副本集：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# docker-compose.yml&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;version&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;3.8&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;services&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;mongo1&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;image&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;mongo:5.0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;command&lt;/span&gt;: [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;--replSet&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;rs0&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;--bind_ip_all&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;ports&lt;/span&gt;: [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;27017:27017&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;mongo2&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;image&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;mongo:5.0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;command&lt;/span&gt;: [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;--replSet&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;rs0&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;--bind_ip_all&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;mongo3&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;image&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;mongo:5.0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;command&lt;/span&gt;: [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;--replSet&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;rs0&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;--bind_ip_all&amp;#34;&lt;/span&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 初始化副本集（执行一次）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mongosh --eval &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;rs.initiate({
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;  _id: &amp;#34;rs0&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;  members: [
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    { _id: 0, host: &amp;#34;mongo1:27017&amp;#34; },
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    { _id: 1, host: &amp;#34;mongo2:27017&amp;#34; },
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    { _id: 2, host: &amp;#34;mongo3:27017&amp;#34; }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;  ]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;})&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;第三篇-数据建模核心战场没有之一&#34;&gt;第三篇 数据建模：核心战场，没有之一&lt;/h2&gt;
&lt;p&gt;MongoDB 精通与否，就看建模。SQL 建模有范式护体，Mongo 建模全靠经验与取舍——这也是 AI 最帮不上忙的部分（第八篇详谈）。&lt;/p&gt;
&lt;h3 id=&#34;31-唯一重要的问题嵌入还是引用&#34;&gt;3.1 唯一重要的问题：嵌入还是引用&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TD
    Q[&amp;#34;一份数据，嵌入父文档&amp;lt;br/&amp;gt;还是独立成集合？&amp;#34;] --&amp;gt; A{&amp;#34;三个判断&amp;#34;}
    A --&amp;gt; B[&amp;#34;读写边界&amp;lt;br/&amp;gt;总是一起读写吗？&amp;#34;]
    A --&amp;gt; C[&amp;#34;增长边界&amp;lt;br/&amp;gt;数量有上限吗？&amp;#34;]
    A --&amp;gt; D[&amp;#34;并发边界&amp;lt;br/&amp;gt;会被高并发独立更新吗？&amp;#34;]
    B --&amp;gt;|&amp;#34;是，且&amp;#34;| E[&amp;#34;嵌入&amp;lt;br/&amp;gt;如：订单内嵌 items 快照&amp;#34;]
    C --&amp;gt;|&amp;#34;否，无限增长&amp;#34;| R[&amp;#34;引用&amp;lt;br/&amp;gt;如：商品引用评论&amp;#34;]
    D --&amp;gt;|&amp;#34;是，热点&amp;#34;| R2[&amp;#34;引用&amp;lt;br/&amp;gt;如：SKU 库存独立成文档&amp;lt;br/&amp;gt;避免单文档成锁热点&amp;#34;]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;三个判断，每个都对应一个坑：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;读写边界&lt;/strong&gt;：订单的 &lt;code&gt;items&lt;/code&gt; 嵌入（下单时一次写入、之后整体读取）；但「收货地址」独立成集合——地址在下单、物流、售后多个流程被独立读写，嵌在用户文档里每次都要加载整个用户。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;增长边界&lt;/strong&gt;：单文档 16MB 硬上限。商品的「问大家」嵌进商品文档，爆款商品一年撑爆，连夜迁移。教训：&lt;strong&gt;文档不是仓库，是货架。&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;并发边界&lt;/strong&gt;：文档级锁决定了——&lt;strong&gt;凡是会被高并发写的数据，从父文档里拆出去&lt;/strong&gt;。秒杀库存必须一个 SKU 一个文档，绝不能嵌在商品里。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;32-电商核心建模订单快照与库存拆分&#34;&gt;3.2 电商核心建模：订单快照与库存拆分&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;erDiagram
    USER ||--o{ ORDER : &amp;#34;引用&amp;#34;
    PRODUCT ||--o{ SKU_STOCK : &amp;#34;拆出 独立高并发写&amp;#34;
    ORDER ||--|{ ORDER_ITEM : &amp;#34;内嵌 商品快照&amp;#34;
    PRODUCT ||--o{ COMMENT : &amp;#34;引用 无界增长&amp;#34;
    PRODUCT {
        ObjectId _id
        string name
        string categoryId
        array attrs &amp;#34;类目属性 灵活嵌入&amp;#34;
        string detailUrl &amp;#34;富文本放对象存储&amp;#34;
    }
    SKU_STOCK {
        ObjectId _id
        ObjectId productId
        string spec
        int stock &amp;#34;独立文档 原子扣减&amp;#34;
        int version &amp;#34;乐观锁&amp;#34;
    }
    ORDER {
        ObjectId _id
        ObjectId userId
        array items &amp;#34;名称/单价/图片快照&amp;#34;
        long totalAmount &amp;#34;单位: 分&amp;#34;
        string status
        date createdAt
    }&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;items&lt;/code&gt;。商品会改名改价下架，三个月后客服处理售后，看的必须是「成交时」的样子。快照冗余不是脏数据，是业务真相。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;金额用「分」，long 存&lt;/strong&gt;。浮点的 &lt;code&gt;199.00&lt;/code&gt; 元在 IEEE 754 世界里是 &lt;code&gt;198.999...&lt;/code&gt;，所有和钱有关的字段一律整数存分。这不是 Mongo 的问题，但 Mongo 的自由度会让人更容易犯懒。&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;思考与感悟之二&lt;/strong&gt;：SQL 建模回答「数据之间是什么关系」，Mongo 建模回答「数据以什么节奏被读写」。前者是静态的学问，后者是动态的学问——必须预测流量形状（谁是热点、谁会暴涨、谁一起出现）才能建好模。这就是为什么 Mongo 建模更依赖业务理解，也更难被自动化。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;第四篇-索引与-explain调优基本功没有捷径&#34;&gt;第四篇 索引与 explain：调优基本功，没有捷径&lt;/h2&gt;
&lt;h3 id=&#34;41-explain-是唯一标准答案&#34;&gt;4.1 explain 是唯一标准答案&lt;/h3&gt;
&lt;p&gt;任何「这条查询为什么慢」的讨论，不贴 explain 输出就是耍流氓。Java 里看执行计划：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// 在 Java 驱动里用 explain 需要走 Document 命令（或先在 mongosh 里验证）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// mongosh 中的写法：&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;db.&lt;span style=&#34;color:#a6e22e&#34;&gt;products&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;find&lt;/span&gt;({ categoryId: cid, status: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;on&amp;#34;&lt;/span&gt; })
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  .&lt;span style=&#34;color:#a6e22e&#34;&gt;sort&lt;/span&gt;({ price: &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;1 })
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  .&lt;span style=&#34;color:#a6e22e&#34;&gt;explain&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;executionStats&amp;#34;&lt;/span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;输出只盯四处：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TD
    A[&amp;#34;winningPlan.stage&amp;#34;] --&amp;gt; B{&amp;#34;COLLSCAN?&amp;#34;}
    B --&amp;gt;|&amp;#34;是&amp;#34;| C[&amp;#34;全表扫描&amp;lt;br/&amp;gt;立刻建索引&amp;#34;]
    B --&amp;gt;|&amp;#34;IXSCAN&amp;#34;| D[&amp;#34;看三个数字&amp;#34;]
    D --&amp;gt; E[&amp;#34;nReturned&amp;lt;br/&amp;gt;返回多少条&amp;#34;]
    D --&amp;gt; F[&amp;#34;totalDocsExamined&amp;lt;br/&amp;gt;读了多少文档&amp;#34;]
    D --&amp;gt; G[&amp;#34;totalKeysExamined&amp;lt;br/&amp;gt;扫了多少索引键&amp;#34;]
    E --&amp;gt; H{&amp;#34;理想: 三者接近&amp;lt;br/&amp;gt;危险: Examined 远大于 Returned&amp;#34;}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;h3 id=&#34;42-esr-法则与-java-建索引&#34;&gt;4.2 ESR 法则与 Java 建索引&lt;/h3&gt;
&lt;p&gt;复合索引字段顺序：&lt;strong&gt;Equality（等值）→ Sort（排序）→ Range（范围）&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;商品列表页查询：&lt;code&gt;{ categoryId: 等值, status: 等值 }&lt;/code&gt; + 按价格倒序，用 Java 驱动建索引：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// 依赖: org.mongodb:mongodb-driver-sync:4.3.x&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; com.mongodb.client.MongoClient;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; com.mongodb.client.MongoClients;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; com.mongodb.client.MongoCollection;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; com.mongodb.client.model.Indexes;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; org.bson.Document;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;IndexDemo&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;(String&lt;span style=&#34;color:#f92672&#34;&gt;[]&lt;/span&gt; args) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;try&lt;/span&gt; (MongoClient client &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; MongoClients.&lt;span style=&#34;color:#a6e22e&#34;&gt;create&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;mongodb://localhost:27017/?replicaSet=rs0&amp;#34;&lt;/span&gt;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            MongoCollection&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;Document&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; products &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; client.&lt;span style=&#34;color:#a6e22e&#34;&gt;getDatabase&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;shop&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;getCollection&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;products&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#75715e&#34;&gt;// ESR：categoryId/status 等值(E) 在前，price 排序(S) 在后&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            products.&lt;span style=&#34;color:#a6e22e&#34;&gt;createIndex&lt;/span&gt;(Indexes.&lt;span style=&#34;color:#a6e22e&#34;&gt;compoundIndex&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    Indexes.&lt;span style=&#34;color:#a6e22e&#34;&gt;ascending&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;categoryId&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    Indexes.&lt;span style=&#34;color:#a6e22e&#34;&gt;ascending&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;status&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    Indexes.&lt;span style=&#34;color:#a6e22e&#34;&gt;descending&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;price&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            ));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#75715e&#34;&gt;// 部分索引：只给在售商品建，索引体积砍半&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            products.&lt;span style=&#34;color:#a6e22e&#34;&gt;createIndex&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    Indexes.&lt;span style=&#34;color:#a6e22e&#34;&gt;ascending&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;price&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; com.&lt;span style=&#34;color:#a6e22e&#34;&gt;mongodb&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;client&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;model&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;IndexOptions&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                            .&lt;span style=&#34;color:#a6e22e&#34;&gt;partialFilterExpression&lt;/span&gt;(com.&lt;span style=&#34;color:#a6e22e&#34;&gt;mongodb&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;client&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;model&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Filters&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;eq&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;status&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;on&amp;#34;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#75715e&#34;&gt;// TTL 索引：埋点日志 7 天自动删除，省掉清理脚本&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            client.&lt;span style=&#34;color:#a6e22e&#34;&gt;getDatabase&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;shop&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;getCollection&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;access_logs&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    .&lt;span style=&#34;color:#a6e22e&#34;&gt;createIndex&lt;/span&gt;(Indexes.&lt;span style=&#34;color:#a6e22e&#34;&gt;ascending&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;createdAt&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                            &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; com.&lt;span style=&#34;color:#a6e22e&#34;&gt;mongodb&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;client&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;model&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;IndexOptions&lt;/span&gt;().&lt;span style=&#34;color:#a6e22e&#34;&gt;expireAfter&lt;/span&gt;(7L, java.&lt;span style=&#34;color:#a6e22e&#34;&gt;util&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;concurrent&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;TimeUnit&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;DAYS&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;注意：加了「价格区间筛选」后 &lt;code&gt;price&lt;/code&gt; 就从 Sort 变成 Range，一个索引满足不了两个需求——&lt;strong&gt;索引设计永远是针对具体查询集合的权衡，没有万能索引&lt;/strong&gt;。每个索引都拖慢写入，写多读少的日志库尤其敏感。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;踩坑故事 #2（32MB 排序内存墙）&lt;/strong&gt;：运营后台「全量商品按上架时间倒序」接口，商品过 10 万后直接报 &lt;code&gt;Sort exceeded memory limit of 33554432 bytes&lt;/code&gt;。Mongo 内存排序上限 32MB，超了就炸。解法不是调参数，是建索引让排序走索引（索引天然有序）。&lt;strong&gt;上线红线第二条：凡带 sort 的查询，必须 explain 验证走索引排序。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;踩坑故事 #3（正则索引进退两难）&lt;/strong&gt;：模糊搜索 &lt;code&gt;find({ name: /卫衣/ })&lt;/code&gt;，非前缀正则无法走索引，数据量上来后 P99 三秒起步。前缀匹配 &lt;code&gt;/^卫衣/&lt;/code&gt; 可以走索引，但中文搜索很少是前缀形态。2021 年的正经答案：&lt;strong&gt;Mongo 管数据，ES 管搜索&lt;/strong&gt;，用 oplog/canal 同步，别指望一个库包打天下。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;第五篇-聚合管道把计算下推给服务端&#34;&gt;第五篇 聚合管道：把计算下推给服务端&lt;/h2&gt;
&lt;p&gt;核心思维：「能在库里算完的，绝不拉回应用层」。拉 10 万条数据到 JVM 里 for 循环求和，是最常见的性能自杀。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart LR
    A[&amp;#34;$match&amp;lt;br/&amp;gt;筛: 本月已支付&amp;#34;] --&amp;gt; B[&amp;#34;$unwind&amp;lt;br/&amp;gt;拆: items 展开&amp;#34;]
    B --&amp;gt; C[&amp;#34;$group&amp;lt;br/&amp;gt;按商品汇总&amp;#34;]
    C --&amp;gt; D[&amp;#34;$sort&amp;lt;br/&amp;gt;销量倒序&amp;#34;]
    D --&amp;gt; E[&amp;#34;$limit&amp;lt;br/&amp;gt;Top 10&amp;#34;]
    E --&amp;gt; F[&amp;#34;热销榜&amp;lt;br/&amp;gt;一次出结果&amp;#34;]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;实战 Demo：本月热销商品 Top 10（Java 驱动写法）：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// demo01-top-sales：本月热销榜&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; com.mongodb.client.MongoClient;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; com.mongodb.client.MongoClients;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; com.mongodb.client.MongoCollection;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; com.mongodb.client.model.Accumulators;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; com.mongodb.client.model.Aggregates;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; com.mongodb.client.model.Filters;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; com.mongodb.client.model.Sorts;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; org.bson.Document;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; org.bson.conversions.Bson;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; java.util.ArrayList;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; java.util.Arrays;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; java.util.Date;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; java.util.List;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;TopSalesDemo&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;(String&lt;span style=&#34;color:#f92672&#34;&gt;[]&lt;/span&gt; args) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;try&lt;/span&gt; (MongoClient client &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; MongoClients.&lt;span style=&#34;color:#a6e22e&#34;&gt;create&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;mongodb://localhost:27017/?replicaSet=rs0&amp;#34;&lt;/span&gt;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            MongoCollection&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;Document&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; orders &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; client.&lt;span style=&#34;color:#a6e22e&#34;&gt;getDatabase&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;shop&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;getCollection&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;orders&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            Date monthStart &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; Date(); &lt;span style=&#34;color:#75715e&#34;&gt;// 实际取本月 1 号 0 点，此处从简&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            Date monthEnd &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; Date();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            List&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;Bson&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; pipeline &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Arrays.&lt;span style=&#34;color:#a6e22e&#34;&gt;asList&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#75715e&#34;&gt;// 第一道永远是 $match：能用索引，砍掉 99% 的数据&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    Aggregates.&lt;span style=&#34;color:#a6e22e&#34;&gt;match&lt;/span&gt;(Filters.&lt;span style=&#34;color:#a6e22e&#34;&gt;and&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                            Filters.&lt;span style=&#34;color:#a6e22e&#34;&gt;eq&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;status&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;paid&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                            Filters.&lt;span style=&#34;color:#a6e22e&#34;&gt;gte&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;createdAt&amp;#34;&lt;/span&gt;, monthStart),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                            Filters.&lt;span style=&#34;color:#a6e22e&#34;&gt;lt&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;createdAt&amp;#34;&lt;/span&gt;, monthEnd))),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    Aggregates.&lt;span style=&#34;color:#a6e22e&#34;&gt;unwind&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;$items&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    Aggregates.&lt;span style=&#34;color:#a6e22e&#34;&gt;group&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;$items.productId&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                            Accumulators.&lt;span style=&#34;color:#a6e22e&#34;&gt;first&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;productName&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;$items.productName&amp;#34;&lt;/span&gt;), &lt;span style=&#34;color:#75715e&#34;&gt;// 快照红利：不用 $lookup&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                            Accumulators.&lt;span style=&#34;color:#a6e22e&#34;&gt;sum&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;totalQty&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;$items.qty&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                            Accumulators.&lt;span style=&#34;color:#a6e22e&#34;&gt;sum&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;totalAmount&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                    &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; Document(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;$multiply&amp;#34;&lt;/span&gt;, Arrays.&lt;span style=&#34;color:#a6e22e&#34;&gt;asList&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;$items.qty&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;$items.price&amp;#34;&lt;/span&gt;)))),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    Aggregates.&lt;span style=&#34;color:#a6e22e&#34;&gt;sort&lt;/span&gt;(Sorts.&lt;span style=&#34;color:#a6e22e&#34;&gt;descending&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;totalQty&amp;#34;&lt;/span&gt;)),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    Aggregates.&lt;span style=&#34;color:#a6e22e&#34;&gt;limit&lt;/span&gt;(10)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            List&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;Document&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; hotList &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; ArrayList&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;gt;&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            orders.&lt;span style=&#34;color:#a6e22e&#34;&gt;aggregate&lt;/span&gt;(pipeline)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    .&lt;span style=&#34;color:#a6e22e&#34;&gt;allowDiskUse&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;)   &lt;span style=&#34;color:#75715e&#34;&gt;// 中间结果超 100MB 允许落盘，报表查询记得开&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    .&lt;span style=&#34;color:#a6e22e&#34;&gt;into&lt;/span&gt;(hotList);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            hotList.&lt;span style=&#34;color:#a6e22e&#34;&gt;forEach&lt;/span&gt;(doc &lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt; System.&lt;span style=&#34;color:#a6e22e&#34;&gt;out&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;println&lt;/span&gt;(doc.&lt;span style=&#34;color:#a6e22e&#34;&gt;toJson&lt;/span&gt;()));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;三个要点：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;$match&lt;/code&gt; 必须放第一&lt;/strong&gt;，放 &lt;code&gt;$unwind&lt;/code&gt; 后面等于先把 500 万订单拆成 3000 万条再筛——有人这么写过，直接把一个节点的内存打爆。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;allowDiskUse(true)&lt;/code&gt;&lt;/strong&gt;：聚合中间结果超 100MB 默认报错，报表类查询记得开。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;能不用 &lt;code&gt;$lookup&lt;/code&gt; 就不用&lt;/strong&gt;。它是应急手段，性能和多表 join 一样是噩梦。需要大量 &lt;code&gt;$lookup&lt;/code&gt; 才能完成的查询，说明建模（第三篇）出了问题——回头改模型，别在查询层硬扛。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&#34;第六篇-副本集高可用与读写语义的分水岭&#34;&gt;第六篇 副本集：高可用与读写语义的分水岭&lt;/h2&gt;
&lt;p&gt;单节点 MongoDB 是玩具，副本集才是起点。这篇是「会用 Mongo」和「敢把核心业务放 Mongo」之间的分水岭。&lt;/p&gt;
&lt;h3 id=&#34;61-架构与选主&#34;&gt;6.1 架构与选主&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TD
    subgraph RS[&amp;#34;副本集 rs0（3 节点是最小生产配置）&amp;#34;]
        P[&amp;#34;Primary&amp;lt;br/&amp;gt;唯一可写&amp;#34;]
        S1[&amp;#34;Secondary&amp;lt;br/&amp;gt;异步复制 oplog&amp;#34;]
        S2[&amp;#34;Secondary&amp;lt;br/&amp;gt;异步复制 oplog&amp;#34;]
        P -- &amp;#34;oplog 同步&amp;#34; --&amp;gt; S1
        P -- &amp;#34;oplog 同步&amp;#34; --&amp;gt; S2
    end
    P -.-&amp;gt;|&amp;#34;心跳超时 10s&amp;lt;br/&amp;gt;自动选主&amp;#34;| S1
    APP[&amp;#34;应用（驱动）&amp;#34;] --&amp;gt;|&amp;#34;自动感知拓扑变化&amp;lt;br/&amp;gt;故障转移对代码透明&amp;#34;| RS&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;必须理解的机制：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;oplog&lt;/strong&gt;：主节点的操作日志，从节点靠它追赶。oplog 是有界集合，从节点宕机太久会追不上，只能全量同步（initial sync）——大库的全量同步以小时计，这就是「从节点宕机要当事故处理」的原因。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;自动选主&lt;/strong&gt;：主挂了，10 秒左右选出新主。&lt;strong&gt;驱动自动重连新主，应用无感&lt;/strong&gt;——前提是连接串写对了（多节点 + &lt;code&gt;replicaSet=rs0&lt;/code&gt;），而不是只写了一个 IP。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;连接池&lt;/strong&gt;：&lt;code&gt;MongoClient&lt;/code&gt; 是线程安全的重对象，&lt;strong&gt;全应用一个单例&lt;/strong&gt;，连接池大小用 &lt;code&gt;maxPoolSize&lt;/code&gt; 调。每请求 new 一个 &lt;code&gt;MongoClient&lt;/code&gt; 等于每请求建一批 TCP 连接，压测时会把数据库连接数打爆——这和「每请求连一次 MySQL」是同一种自杀。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;62-读写语义writeconcern-与-readconcern&#34;&gt;6.2 读写语义：writeConcern 与 readConcern&lt;/h3&gt;
&lt;p&gt;这部分决定你能不能睡着觉——数据到底「写没写进去」？&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// demo02-write-concern：不同档位，不同的安全与延迟权衡&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; com.mongodb.WriteConcern;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; com.mongodb.client.MongoCollection;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; org.bson.Document;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;MongoCollection&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;Document&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; orders &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; client.&lt;span style=&#34;color:#a6e22e&#34;&gt;getDatabase&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;shop&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;getCollection&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;orders&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// 快但可能丢：主节点内存确认就返回（埋点日志可用）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;orders.&lt;span style=&#34;color:#a6e22e&#34;&gt;withWriteConcern&lt;/span&gt;(WriteConcern.&lt;span style=&#34;color:#a6e22e&#34;&gt;W1&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;insertOne&lt;/span&gt;(logDoc);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// 安全：多数派落盘 + journal 落盘才返回（订单必须这档）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;orders.&lt;span style=&#34;color:#a6e22e&#34;&gt;withWriteConcern&lt;/span&gt;(WriteConcern.&lt;span style=&#34;color:#a6e22e&#34;&gt;MAJORITY&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;withJournal&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;)).&lt;span style=&#34;color:#a6e22e&#34;&gt;insertOne&lt;/span&gt;(orderDoc);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&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;writeConcern&lt;/th&gt;
					&lt;th&gt;理由&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;订单创建&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;majority&lt;/code&gt; + journal&lt;/td&gt;
					&lt;td&gt;主从切换时绝不能丢单&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;库存扣减&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;majority&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;丢扣减记录不可忍&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;行为埋点&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;w: 1&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;丢几条日志无所谓，别拖累写入&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;购物车&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;majority&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;用户加购后刷新没了，投诉就来了&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;code&gt;readConcern&lt;/code&gt; 同理：读「已提交到多数派」的数据用 &lt;code&gt;ReadConcern.MAJORITY&lt;/code&gt;，避免读到主从切换后被回滚的数据。&lt;strong&gt;钱相关的读，一律 majority；展示类的读，local 即可。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;踩坑故事 #4（丢单惊魂夜）&lt;/strong&gt;：上线初期订单写入用默认 &lt;code&gt;w: 1&lt;/code&gt;。某天主节点宕机选主，新主上没有最后 8 秒的 23 笔订单——用户付了钱（支付回调已记录），订单却不存在，客服电话被打爆。复盘结论：&lt;strong&gt;支付回调链路和订单链路的 writeConcern 必须对齐 majority&lt;/strong&gt;，宁慢勿丢。这 8 秒人工补单补了一整夜。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;思考与感悟之三&lt;/strong&gt;：分布式系统里「默认配置」几乎都是「性能优先、安全靠边」的，MongoDB 的 &lt;code&gt;w:1&lt;/code&gt;、MySQL 的异步复制、Kafka 的 &lt;code&gt;acks=1&lt;/code&gt; 皆如此。成熟的标志之一：&lt;strong&gt;每接一个中间件，先把默认一致性档位改成业务需要的档位，再开始写业务代码。&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;第七篇-事务与分片规模化之后的一致性难题&#34;&gt;第七篇 事务与分片：规模化之后的一致性难题&lt;/h2&gt;
&lt;h3 id=&#34;71-能不用事务就不用原子操作的威力&#34;&gt;7.1 能不用事务就不用：原子操作的威力&lt;/h3&gt;
&lt;p&gt;MongoDB 单文档操作天然原子。电商防超卖，一条条件更新就够，根本不需要事务：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// demo03-deduct-stock：条件原子扣减，1000 并发抢 1 件也只有 1 个成功&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; com.mongodb.client.MongoCollection;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; com.mongodb.client.model.Filters;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; com.mongodb.client.model.Updates;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; com.mongodb.client.result.UpdateResult;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; org.bson.Document;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; org.bson.types.ObjectId;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;DeductStockDemo&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;deduct&lt;/span&gt;(MongoCollection&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;Document&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; skuStocks, ObjectId skuId, &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; qty) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        UpdateResult r &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; skuStocks.&lt;span style=&#34;color:#a6e22e&#34;&gt;updateOne&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#75715e&#34;&gt;// 库存够不够，放进查询条件 —— 判断和修改合并成一次原子操作&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                Filters.&lt;span style=&#34;color:#a6e22e&#34;&gt;and&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        Filters.&lt;span style=&#34;color:#a6e22e&#34;&gt;eq&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;_id&amp;#34;&lt;/span&gt;, skuId),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        Filters.&lt;span style=&#34;color:#a6e22e&#34;&gt;gte&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;stock&amp;#34;&lt;/span&gt;, qty)),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                Updates.&lt;span style=&#34;color:#a6e22e&#34;&gt;combine&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        Updates.&lt;span style=&#34;color:#a6e22e&#34;&gt;inc&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;stock&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;qty),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        Updates.&lt;span style=&#34;color:#a6e22e&#34;&gt;inc&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sold&amp;#34;&lt;/span&gt;, qty),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        Updates.&lt;span style=&#34;color:#a6e22e&#34;&gt;currentDate&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;updatedAt&amp;#34;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (r.&lt;span style=&#34;color:#a6e22e&#34;&gt;getModifiedCount&lt;/span&gt;() &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; 0) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;throw&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; IllegalStateException(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;库存不足&amp;#34;&lt;/span&gt;); &lt;span style=&#34;color:#75715e&#34;&gt;// 没抢到，上层返回售罄&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;精髓：判断（&lt;code&gt;stock &amp;gt;= qty&lt;/code&gt;）和修改（&lt;code&gt;$inc&lt;/code&gt;）合并成&lt;strong&gt;一次原子操作&lt;/strong&gt;，不存在「查出来够、扣的时候不够」的竞态。这比任何分布式锁都干净。&lt;/p&gt;
&lt;h3 id=&#34;72-真正需要事务的场景&#34;&gt;7.2 真正需要事务的场景&lt;/h3&gt;
&lt;p&gt;跨集合的一致性——下单要同时「扣库存 + 建订单 + 核销券」，三个动作三个集合，这时上多文档事务（4.0+，需副本集）：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// demo04-place-order-tx：多文档事务下单&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; com.mongodb.ClientSessionOptions;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; com.mongodb.ReadConcern;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; com.mongodb.TransactionOptions;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; com.mongodb.WriteConcern;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; com.mongodb.client.ClientSession;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; com.mongodb.client.MongoClient;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; org.bson.Document;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;PlaceOrderTxDemo&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;placeOrder&lt;/span&gt;(MongoClient client, Document orderDoc, ObjectId skuId, &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; qty, ObjectId couponId) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        TransactionOptions txnOptions &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; TransactionOptions.&lt;span style=&#34;color:#a6e22e&#34;&gt;builder&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                .&lt;span style=&#34;color:#a6e22e&#34;&gt;readConcern&lt;/span&gt;(ReadConcern.&lt;span style=&#34;color:#a6e22e&#34;&gt;SNAPSHOT&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                .&lt;span style=&#34;color:#a6e22e&#34;&gt;writeConcern&lt;/span&gt;(WriteConcern.&lt;span style=&#34;color:#a6e22e&#34;&gt;MAJORITY&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                .&lt;span style=&#34;color:#a6e22e&#34;&gt;build&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;try&lt;/span&gt; (ClientSession session &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; client.&lt;span style=&#34;color:#a6e22e&#34;&gt;startSession&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                ClientSessionOptions.&lt;span style=&#34;color:#a6e22e&#34;&gt;builder&lt;/span&gt;().&lt;span style=&#34;color:#a6e22e&#34;&gt;defaultTransactionOptions&lt;/span&gt;(txnOptions).&lt;span style=&#34;color:#a6e22e&#34;&gt;build&lt;/span&gt;())) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            session.&lt;span style=&#34;color:#a6e22e&#34;&gt;withTransaction&lt;/span&gt;(() &lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; db &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; client.&lt;span style=&#34;color:#a6e22e&#34;&gt;getDatabase&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;shop&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#75715e&#34;&gt;// 1. 条件原子扣库存&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; r &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; db.&lt;span style=&#34;color:#a6e22e&#34;&gt;getCollection&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sku_stocks&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;updateOne&lt;/span&gt;(session,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        com.&lt;span style=&#34;color:#a6e22e&#34;&gt;mongodb&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;client&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;model&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Filters&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;and&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                com.&lt;span style=&#34;color:#a6e22e&#34;&gt;mongodb&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;client&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;model&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Filters&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;eq&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;_id&amp;#34;&lt;/span&gt;, skuId),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                com.&lt;span style=&#34;color:#a6e22e&#34;&gt;mongodb&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;client&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;model&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Filters&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;gte&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;stock&amp;#34;&lt;/span&gt;, qty)),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        com.&lt;span style=&#34;color:#a6e22e&#34;&gt;mongodb&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;client&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;model&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Updates&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;inc&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;stock&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;qty));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (r.&lt;span style=&#34;color:#a6e22e&#34;&gt;getModifiedCount&lt;/span&gt;() &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; 0) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#66d9ef&#34;&gt;throw&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; IllegalStateException(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;库存不足，触发回滚&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#75715e&#34;&gt;// 2. 插入订单（含商品快照）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                db.&lt;span style=&#34;color:#a6e22e&#34;&gt;getCollection&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;orders&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;insertOne&lt;/span&gt;(session, orderDoc);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#75715e&#34;&gt;// 3. 核销优惠券&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                db.&lt;span style=&#34;color:#a6e22e&#34;&gt;getCollection&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;coupons&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;updateOne&lt;/span&gt;(session,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        com.&lt;span style=&#34;color:#a6e22e&#34;&gt;mongodb&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;client&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;model&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Filters&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;and&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                com.&lt;span style=&#34;color:#a6e22e&#34;&gt;mongodb&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;client&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;model&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Filters&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;eq&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;_id&amp;#34;&lt;/span&gt;, couponId),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                                com.&lt;span style=&#34;color:#a6e22e&#34;&gt;mongodb&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;client&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;model&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Filters&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;eq&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;used&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;)),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        com.&lt;span style=&#34;color:#a6e22e&#34;&gt;mongodb&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;client&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;model&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Updates&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;set&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;used&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#75715e&#34;&gt;// 任何一步抛异常，前面所有操作自动回滚&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;sequenceDiagram
    participant A as 下单服务
    participant M as MongoDB 副本集
    A-&amp;gt;&amp;gt;M: withTransaction 开启
    A-&amp;gt;&amp;gt;M: 1. 条件原子扣库存
    alt 库存不足
        M--&amp;gt;&amp;gt;A: modifiedCount = 0
        A-&amp;gt;&amp;gt;M: 自动 abortTransaction&amp;lt;br/&amp;gt;三步全部回滚
        A--&amp;gt;&amp;gt;A: 返回售罄
    else 成功
        A-&amp;gt;&amp;gt;M: 2. 插入订单(含快照)
        A-&amp;gt;&amp;gt;M: 3. 核销优惠券
        A-&amp;gt;&amp;gt;M: commit (w:majority)
        M--&amp;gt;&amp;gt;A: 提交成功
    end&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;&lt;strong&gt;踩坑故事 #5（事务的 QPS 税）&lt;/strong&gt;：压测发现全事务下单链路 QPS 比「原子扣减 + 消息队列异步落单」低约 40%，且长事务会压住 WiredTiger 的旧版本清理（第二篇的坑）。大促链路的最终方案：&lt;strong&gt;核心扣减用单文档原子操作（快），订单与券的一致性用 MQ + 定时对账任务兜底（最终一致）&lt;/strong&gt;。事务留给后台管理类低频强一致操作。教训：&lt;strong&gt;事务是保险丝，不是日常用电。&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&#34;73-分片单副本集扛不住之后&#34;&gt;7.3 分片：单副本集扛不住之后&lt;/h3&gt;
&lt;p&gt;数据量单副本集放不下（经验值：单集合过 2TB 或写入 QPS 过万再考虑），才上分片：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TD
    APP[&amp;#34;应用&amp;#34;] --&amp;gt; MONGOS[&amp;#34;mongos 路由层&amp;#34;]
    MONGOS --&amp;gt; S1[&amp;#34;Shard 1&amp;lt;br/&amp;gt;副本集&amp;#34;]
    MONGOS --&amp;gt; S2[&amp;#34;Shard 2&amp;lt;br/&amp;gt;副本集&amp;#34;]
    MONGOS --&amp;gt; S3[&amp;#34;Shard 3&amp;lt;br/&amp;gt;副本集&amp;#34;]
    MONGOS -.-&amp;gt; CFG[&amp;#34;Config Server&amp;lt;br/&amp;gt;元数据副本集&amp;#34;]
    S1 -.-&amp;gt;|&amp;#34;均衡器&amp;lt;br/&amp;gt;chunk 迁移&amp;#34;| S2&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;分片键（shard key）是 Mongo 最「一选定终身」的决策，选错无法修改（5.0 之前）。三条铁律：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;高基数&lt;/strong&gt;：用 &lt;code&gt;status&lt;/code&gt;（只有几个枚举值）做分片键，所有数据挤在少数 chunk 里，分了个寂寞。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;避免单调递增&lt;/strong&gt;：用 &lt;code&gt;createdAt&lt;/code&gt; 或自增 ObjectId 做分片键，所有新写入都打到&lt;strong&gt;最后一个分片&lt;/strong&gt;——热点分片被写爆，其他分片围观。日志类业务要么 hash 分片键，要么接受这个形态。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;匹配查询模式&lt;/strong&gt;：订单按 &lt;code&gt;userId&lt;/code&gt; hash 分片，「查我的订单」路由到单分片，飞快；「全平台订单报表」变成广播查询（scatter-gather），慢——所以报表本来就不该打在分片集群上，该走数仓。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;踩坑故事 #6（jumbo chunk 卡死均衡）&lt;/strong&gt;：早期一张集合按 &lt;code&gt;categoryId&lt;/code&gt; 分片，头部类目一个 chunk 涨到 500MB 还无法拆分（jumbo chunk），均衡器彻底停摆，分片间数据倾斜到 3:1。最后导出数据重建集合换 hash 分片键才解决，停机 4 小时。&lt;strong&gt;分片键评审必须过架构组，这是红线第三条。&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;第八篇-运维底线与-ai-时代把系统做稳把代码写少&#34;&gt;第八篇 运维底线与 AI 时代：把系统做稳，把代码写少&lt;/h2&gt;
&lt;h3 id=&#34;81-生产运维的最低纲领&#34;&gt;8.1 生产运维的最低纲领&lt;/h3&gt;
&lt;p&gt;不展开讲 DBA 手册，只列必须亲自盯的五件事：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TD
    M[&amp;#34;监控&amp;lt;br/&amp;gt;slow query / cache 压力 / oplog 窗口 / 复制延迟&amp;#34;] --&amp;gt; A[&amp;#34;告警&amp;lt;br/&amp;gt;复制延迟 &amp;gt; 30s 即报警&amp;lt;br/&amp;gt;别等追不上才发现&amp;#34;]
    B[&amp;#34;备份&amp;lt;br/&amp;gt; mongodump 逻辑备份&amp;lt;br/&amp;gt;+ 快照物理备份&amp;lt;br/&amp;gt;恢复演练每季度一次&amp;#34;] --&amp;gt; A
    C[&amp;#34;慢查询&amp;lt;br/&amp;gt;profiler 只开采样&amp;lt;br/&amp;gt;每周过 top 慢查询&amp;#34;] --&amp;gt; A
    D[&amp;#34;容量&amp;lt;br/&amp;gt;磁盘水位 70% 预警&amp;lt;br/&amp;gt;oplog 大小按写入峰值调&amp;#34;] --&amp;gt; A
    E[&amp;#34;权限&amp;lt;br/&amp;gt;最小化授权 禁止裸奔&amp;lt;br/&amp;gt;生产禁 dropDatabase&amp;#34;] --&amp;gt; A&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;&lt;strong&gt;踩坑故事 #7（恢复演练救了我们一命）&lt;/strong&gt;：团队坚持每季度做一次「备份恢复演练」，被吐槽形式主义。直到有人误删集合（&lt;code&gt;drop()&lt;/code&gt; 当 &lt;code&gt;deleteMany()&lt;/code&gt; 用了），靠备份 40 分钟恢复，业务无损。&lt;strong&gt;没演练过的备份等于没有备份&lt;/strong&gt;——这句话值一个 P0 事故。&lt;/p&gt;
&lt;h3 id=&#34;82-ai-时代如何少写代码把系统做稳&#34;&gt;8.2 AI 时代：如何少写代码把系统做稳&lt;/h3&gt;
&lt;p&gt;写这篇博客的 2021 年，GitHub Copilot 刚发布。站在工程视角，做几个判断，留给未来验证：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;AI 会吃掉「模式化代码」，吃不掉「模式化决策」。&lt;/strong&gt; CRUD、Schema 定义、聚合管道骨架、连接池配置——这些高度模式化的代码 AI 生成得又快又准（你甚至可以现在就试试让 Copilot 补全上面的 &lt;code&gt;DeductStockDemo&lt;/code&gt;，完成度惊人）。但「嵌入还是引用」「分片键选什么」「writeConcern 定哪档」——这些&lt;strong&gt;依赖业务流量预测的决策&lt;/strong&gt;，AI 给不出答案。核心竞争力从「会写」转向「会判断」。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;「少写代码」的正确姿势是把决策做在数据层。&lt;/strong&gt; 用快照冗余消灭 &lt;code&gt;$lookup&lt;/code&gt;，用 TTL 索引消灭清理脚本，用原子扣减消灭分布式锁，用条件更新消灭事务——本篇通篇的「少写代码」讲的都是这个：&lt;strong&gt;好的建模让代码自然变少&lt;/strong&gt;。AI 是放大器，放大的既可以是好设计，也可以是坏设计。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;一个人撑起一个 App 的门槛在崩塌。&lt;/strong&gt; MongoDB Atlas（托管副本集/分片）+ Serverless 函数 + AI 生成 CRUD 层，一个小电商的服务端几天就能搭起来。2021 年这还只是苗头，我确信它会成常态。但反过来：&lt;strong&gt;门槛越低，线上事故越便宜&lt;/strong&gt;——不懂 writeConcern 的人也能上线了，丢单的事故只会更多。AI 时代的工程教育，重点应该从「怎么实现」转向「哪里会炸」。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AI 生成代码的第一评审项是资源语义。&lt;/strong&gt; 连接谁关、事务多长、批量多大、一致性哪档——AI 生成的代码功能往往是对的，资源语义常常是错的（比如给你 &lt;code&gt;w: 1&lt;/code&gt; 的默认写入）。Code Review 的重心会整体迁移到这四个问题上。&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;思考与感悟之四（收尾）&lt;/strong&gt;：工程的本质，是「替业务把不确定性挡在系统之外」。MongoDB 给了你自由度，也把范式的保护伞收走了——锁粒度、一致性档位、分片拓扑，每一个都要自己选。AI 会帮你把代码写得越来越快，但「选什么」的责任永远在人身上。这既是这份工作的辛苦之处，也是它的护城河。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;附录demo-与检查清单&#34;&gt;附录：Demo 与检查清单&lt;/h2&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Demo&lt;/th&gt;
					&lt;th&gt;类/片段&lt;/th&gt;
					&lt;th&gt;覆盖知识点&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;建索引&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;IndexDemo&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;ESR 复合索引、部分索引、TTL 索引&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;热销榜聚合&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;TopSalesDemo&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;$match&lt;/code&gt; 前置、&lt;code&gt;$unwind&lt;/code&gt;/&lt;code&gt;$group&lt;/code&gt;、&lt;code&gt;allowDiskUse&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;写入档位&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;demo02-write-concern&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;w:1&lt;/code&gt; / &lt;code&gt;majority&lt;/code&gt; / journal 的取舍&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;原子扣库存&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;DeductStockDemo&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;条件原子更新防超卖&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;事务下单&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;PlaceOrderTxDemo&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;多文档事务、回滚、事务读写关注级别&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;MongoDB 生产红线（本文沉淀，可直接抄进团队规范）：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;生产环境永远不开不设限的大批量更新，批量操作必拆批 + 让出窗口。&lt;/li&gt;
&lt;li&gt;凡带 sort 的查询，必须 explain 验证走索引排序。&lt;/li&gt;
&lt;li&gt;分片键评审必须过架构组；能用副本集解决的绝不上分片。&lt;/li&gt;
&lt;li&gt;钱相关的读写一律 &lt;code&gt;majority&lt;/code&gt;；默认配置永远不可信。&lt;/li&gt;
&lt;li&gt;备份必须每季度做恢复演练，没演练过的备份等于没有备份。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;文中代码基于 MongoDB 5.0 + 官方 Java 同步驱动 4.3 实测可编译运行。如果你正站在「要不要接 MongoDB」的门口，建议按八篇顺序在本地副本集上把每个坑亲手复现一遍——看别人踩坑是故事，自己踩一遍才是经验。&lt;/p&gt;
</description>
    </item>
    <item>
      <title>MySQL 实战笔记：安装、事务、索引、锁与高性能优化</title>
      <link>https://ftmao.com/blog/posts/mysql-practical-guide/</link>
      <pubDate>Thu, 28 Nov 2019 14:00:00 +0800</pubDate>
      <guid>https://ftmao.com/blog/posts/mysql-practical-guide/</guid>
      <description>&lt;h1 id=&#34;mysql-实战笔记安装事务索引锁与高性能优化&#34;&gt;MySQL 实战笔记：安装、事务、索引、锁与高性能优化&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;MySQL 是互联网业务中使用最广泛的关系型数据库之一。掌握它不能只停留在“会写 CRUD”，还要理解事务隔离、索引结构、锁机制以及高性能架构设计。本文从实战出发，结合常见坑点与优化建议，帮你建立系统性的 MySQL 知识地图。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;一高层次思想mysql-在解决什么问题&#34;&gt;一、高层次思想：MySQL 在解决什么问题？&lt;/h2&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;strong&gt;一致性&lt;/strong&gt;：通过事务与约束，保证数据始终符合业务规则。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;高效访问&lt;/strong&gt;：通过索引、缓存、分区分表等手段，在海量数据中快速读写。&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph TD
    A[业务应用] --&amp;gt;|SQL| B[MySQL Server]
    B --&amp;gt; C[SQL 解析 / 优化]
    C --&amp;gt; D[执行引擎]
    D --&amp;gt; E[存储引擎 InnoDB/MyISAM]
    E --&amp;gt; F[Buffer Pool 缓存]
    E --&amp;gt; G[Redo / Undo Log]
    E --&amp;gt; H[磁盘数据文件]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;h3 id=&#34;11-mysql-架构分层&#34;&gt;1.1 MySQL 架构分层&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph LR
    A[连接层] --&amp;gt; B[服务层]
    B --&amp;gt; C[存储引擎层]
    B --&amp;gt; D[查询缓存 / 解析器 / 优化器]
    C --&amp;gt; E[InnoDB 支持事务/行锁]
    C --&amp;gt; F[MyISAM 高速查询/表锁]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&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;管理客户端连接、权限验证&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;服务层&lt;/td&gt;
					&lt;td&gt;SQL 解析、优化、执行&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;存储引擎层&lt;/td&gt;
					&lt;td&gt;负责数据的存储与读取&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;12-buffer-poolinnodb-的内存缓存&#34;&gt;1.2 Buffer Pool：InnoDB 的内存缓存&lt;/h3&gt;
&lt;p&gt;Buffer Pool 是 InnoDB 在内存中开辟的一块区域，用来缓存&lt;strong&gt;数据页&lt;/strong&gt;和&lt;strong&gt;索引页&lt;/strong&gt;。它是 MySQL 性能的核心，绝大多数读写操作都先发生在 Buffer Pool 中。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;为什么需要 Buffer Pool？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;磁盘的读写速度比内存慢几个数量级。如果每次查询都读磁盘，性能会极差。Buffer Pool 把热数据放在内存里，大幅减少磁盘 I/O。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Buffer Pool 的工作流程：&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;sequenceDiagram
    participant App as 应用
    participant BP as Buffer Pool
    participant Disk as 磁盘

    App-&amp;gt;&amp;gt;BP: 查询 id=1
    BP--&amp;gt;&amp;gt;App: 命中，直接返回
    Note over BP: 数据页已在内存

    App-&amp;gt;&amp;gt;BP: 查询 id=2
    BP-&amp;gt;&amp;gt;Disk: 未命中，从磁盘加载
    Disk--&amp;gt;&amp;gt;BP: 返回数据页
    BP--&amp;gt;&amp;gt;App: 返回数据&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;&lt;strong&gt;写数据时的流程：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;先把数据页读到 Buffer Pool（如果不在）。&lt;/li&gt;
&lt;li&gt;在 Buffer Pool 中修改数据页，此时这个页变成&lt;strong&gt;脏页（Dirty Page）&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;写 Redo Log（WAL）。&lt;/li&gt;
&lt;li&gt;返回成功给应用。&lt;/li&gt;
&lt;li&gt;后台线程（Page Cleaner）异步把脏页刷回磁盘。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Buffer Pool 的内部结构：&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;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;LRU 链表&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;管理数据页的使用热度，最近最少使用的页被淘汰&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Free 链表&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;管理空闲页&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Flush 链表&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;管理脏页，等待后台刷盘&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Change Buffer&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;缓存二级索引的修改，减少随机 I/O&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Adaptive Hash Index&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;自动为热点数据页建立哈希索引，加速查找&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：脏页是异步刷盘的，所以提交成功不代表数据页已经落盘。InnoDB 通过 Redo Log 保证即使崩溃也能恢复。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;二安装启动与基础配置&#34;&gt;二、安装、启动与基础配置&lt;/h2&gt;
&lt;h3 id=&#34;21-启动-mysql-服务&#34;&gt;2.1 启动 MySQL 服务&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Windows&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;net start MySQL
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Linux/macOS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo systemctl start mysql&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;h3 id=&#34;22-修改-root-密码&#34;&gt;2.2 修改 root 密码&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;ALTER&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;user&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;root&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;@&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;localhost&amp;#39;&lt;/span&gt; IDENTIFIED &lt;span style=&#34;color:#66d9ef&#34;&gt;BY&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;root&amp;#39;&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;h3 id=&#34;23-创建数据库时务必指定编码&#34;&gt;2.3 创建数据库时务必指定编码&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;CREATE&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;DATABASE&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;`&lt;/span&gt;test&lt;span style=&#34;color:#f92672&#34;&gt;`&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;/*!40100 COLLATE &amp;#39;utf8mb4_bin&amp;#39; */&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;避坑指南 #1&lt;/strong&gt;：默认的 &lt;code&gt;utf8&lt;/code&gt; 在 MySQL 中其实是阉割版（最多 3 字节），无法存储 Emoji 等 4 字节字符。生产环境请统一使用 &lt;strong&gt;&lt;code&gt;utf8mb4&lt;/code&gt;&lt;/strong&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;24-jdbc-连接串必须带编码与时区&#34;&gt;2.4 JDBC 连接串必须带编码与时区&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;jdbc:mysql:&lt;span style=&#34;color:#f92672&#34;&gt;//&lt;/span&gt;127.&lt;span style=&#34;color:#a6e22e&#34;&gt;0&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;0&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;1&lt;/span&gt;:3306&lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt;crmeb_java&lt;span style=&#34;color:#f92672&#34;&gt;?&lt;/span&gt;serverTimeZone&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;UTC&lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;useUnicode&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;characterEncoding&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;utf8&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;MySQL 8 推荐写法：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;jdbc:mysql:&lt;span style=&#34;color:#f92672&#34;&gt;//&lt;/span&gt;127.&lt;span style=&#34;color:#a6e22e&#34;&gt;0&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;0&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;1&lt;/span&gt;:3306&lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt;db_name&lt;span style=&#34;color:#f92672&#34;&gt;?&lt;/span&gt;serverTimezone&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;UTC&lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;useSSL&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;useUnicode&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;characterEncoding&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;utf8mb4&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;避坑指南 #2&lt;/strong&gt;：MySQL 8 默认启用 SSL，但旧版驱动连接时可能报错，可显式设置 &lt;code&gt;useSSL=false&lt;/code&gt;（内网环境）。时区必须指定，否则会出现时间差 8 小时的问题。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;25-开启通用查询日志&#34;&gt;2.5 开启通用查询日志&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;SET&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;GLOBAL&lt;/span&gt; general_log &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;ON&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;SET&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;GLOBAL&lt;/span&gt; general_log_file &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;/tmp/mysql.log&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;SHOW&lt;/span&gt; VARIABLES &lt;span style=&#34;color:#66d9ef&#34;&gt;LIKE&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;general_log%&amp;#34;&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;避坑指南 #3&lt;/strong&gt;：通用日志会记录所有 SQL，性能开销极大。仅用于临时排查问题，&lt;strong&gt;生产环境不要长期开启&lt;/strong&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;三存储过程谨慎使用&#34;&gt;三、存储过程：谨慎使用&lt;/h2&gt;
&lt;p&gt;存储过程（Stored Procedure）是把一组 SQL 语句预编译后存储在数据库内部，通过 &lt;code&gt;CALL&lt;/code&gt; 调用的程序单元。它能减少网络往返、封装固定逻辑，但在现代工程实践中已不再被推荐作为首选方案。&lt;/p&gt;
&lt;h3 id=&#34;为什么不推荐重度使用存储过程&#34;&gt;为什么不推荐重度使用存储过程？&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;strong&gt;调试困难&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;缺乏现代 IDE 的断点、单步调试和日志追踪能力，定位问题成本高。&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;版本控制弱&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;存储过程游离在应用代码仓库之外，难以做 Code Review 和版本回滚。&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;迁移成本高&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;不同数据库（MySQL、Oracle、SQL Server、PostgreSQL）的存储过程语法差异大，换库时重写代价大。&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;测试困难&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;不易做单元测试、集成测试，CI/CD 流程中难以保证质量。&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;与微服务理念冲突&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;业务逻辑下沉到数据库，导致数据库成为“另一个服务边界”，增加耦合。&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;适合使用存储过程的场景&#34;&gt;适合使用存储过程的场景&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;数据迁移与 ETL&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;/ul&gt;
&lt;h3 id=&#34;更推荐的替代方案&#34;&gt;更推荐的替代方案&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph LR
    A[复杂业务逻辑] --&amp;gt; B[应用层 / Service 层实现]
    A --&amp;gt; C[ORM / MyBatis 等持久层框架]
    B --&amp;gt; D[便于测试、调试、版本控制]
    C --&amp;gt; D&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;应用层处理&lt;/strong&gt;：把复杂逻辑放在 Java/Go/Python 等服务层，配合事务注解保证一致性。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;批量 SQL&lt;/strong&gt;：使用 &lt;code&gt;INSERT ... SELECT&lt;/code&gt; 或 &lt;code&gt;LOAD DATA&lt;/code&gt; 完成批量导入。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;定时任务&lt;/strong&gt;：使用调度框架（如 Quartz、XXL-JOB）替代存储过程做周期化数据处理。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;避坑指南 #4&lt;/strong&gt;：存储过程调试困难、不利于迁移，现代微服务架构中建议&lt;strong&gt;慎用&lt;/strong&gt;。批量数据处理优先考虑在应用层分批插入，或使用 &lt;code&gt;INSERT ... SELECT&lt;/code&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;四事务acid-与隔离级别&#34;&gt;四、事务：ACID 与隔离级别&lt;/h2&gt;
&lt;h3 id=&#34;41-事务的-acid-特性&#34;&gt;4.1 事务的 ACID 特性&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph TD
    A[事务] --&amp;gt; B[Atomicity 原子性]
    A --&amp;gt; C[Consistency 一致性]
    A --&amp;gt; D[Isolation 隔离性]
    A --&amp;gt; E[Durability 持久性]
    B --&amp;gt; F[Undo Log]
    E --&amp;gt; G[Redo Log]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&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;MySQL 实现&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;原子性&lt;/td&gt;
					&lt;td&gt;要么全成功，要么全回滚&lt;/td&gt;
					&lt;td&gt;Undo Log&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;一致性&lt;/td&gt;
					&lt;td&gt;事务前后数据满足约束&lt;/td&gt;
					&lt;td&gt;约束 + 触发器 + 业务校验&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;隔离性&lt;/td&gt;
					&lt;td&gt;并发事务互不干扰&lt;/td&gt;
					&lt;td&gt;锁 + MVCC&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;持久性&lt;/td&gt;
					&lt;td&gt;提交后数据不丢失&lt;/td&gt;
					&lt;td&gt;Redo Log&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;42-undo-log-与-redo-log&#34;&gt;4.2 Undo Log 与 Redo Log&lt;/h3&gt;
&lt;h4 id=&#34;421-redo-log崩溃恢复与何时返回成功&#34;&gt;4.2.1 Redo Log：崩溃恢复与“何时返回成功”&lt;/h4&gt;
&lt;p&gt;InnoDB 使用 &lt;strong&gt;WAL（Write-Ahead Logging）&lt;/strong&gt; 保证持久性：事务提交前先把修改写到 Redo Log，真正的脏页可以延迟异步刷回磁盘。核心流程如下：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;sequenceDiagram
    participant App as 应用/客户端
    participant MySQL as MySQL Server
    participant BP as Buffer Pool
    participant Redo as Redo Log Buffer / 文件
    participant Disk as 磁盘数据文件

    App-&amp;gt;&amp;gt;MySQL: BEGIN
    App-&amp;gt;&amp;gt;MySQL: UPDATE user SET age=20 WHERE id=1
    MySQL-&amp;gt;&amp;gt;BP: 读取数据页并修改 age=20
    MySQL-&amp;gt;&amp;gt;Redo: 写入 Redo Log：&amp;lt;br/&amp;gt;&amp;#34;将 id=1 的 age 改为 20&amp;#34;
    Note over Redo: WAL：先写日志，再写磁盘
    App-&amp;gt;&amp;gt;MySQL: COMMIT
    MySQL-&amp;gt;&amp;gt;Redo: 按策略刷新 Redo Log
    MySQL--&amp;gt;&amp;gt;App: 返回 Commit 成功
    Note over BP,Disk: 脏页由后台线程异步刷盘&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;&lt;strong&gt;Redo 写完后，MySQL 才告诉应用写完成了吗？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;答案是：&lt;strong&gt;默认配置（&lt;code&gt;innodb_flush_log_at_trx_commit = 1&lt;/code&gt;）下，是的。&lt;/strong&gt; MySQL 把 Redo Log &lt;code&gt;fsync&lt;/code&gt; 到磁盘后，才会返回 &lt;code&gt;COMMIT&lt;/code&gt; 成功。&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;参数值&lt;/th&gt;
					&lt;th&gt;提交时的行为&lt;/th&gt;
					&lt;th&gt;持久性&lt;/th&gt;
					&lt;th&gt;典型场景&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;1（默认）&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;每次提交都 &lt;code&gt;fsync&lt;/code&gt; 刷盘&lt;/td&gt;
					&lt;td&gt;最高，提交即持久&lt;/td&gt;
					&lt;td&gt;生产默认，强一致业务&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;2&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;写到 OS Page Cache，每秒刷盘&lt;/td&gt;
					&lt;td&gt;可能丢失 1 秒内数据&lt;/td&gt;
					&lt;td&gt;追求性能，可容忍少量丢失&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;0&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;由后台线程每秒刷一次&lt;/td&gt;
					&lt;td&gt;最低，进程崩溃可能丢 1 秒&lt;/td&gt;
					&lt;td&gt;极少使用&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;关键结论&lt;/strong&gt;：默认情况下，应用收到 &lt;code&gt;COMMIT&lt;/code&gt; 成功，就意味着对应事务的 Redo Log 已经持久化到磁盘。此后即使 MySQL 立刻崩溃，重启时也能根据 Redo Log 恢复这条已提交事务；如果崩溃发生在 &lt;code&gt;fsync&lt;/code&gt; 之前，则该事务会被回滚或视为未提交。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;崩溃恢复流程：&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TD
    A[MySQL 崩溃后重启] --&amp;gt; B[加载磁盘 Redo Log]
    B --&amp;gt; C[找到上一次 Checkpoint LSN]
    C --&amp;gt; D[扫描 Checkpoint 之后的 Redo 记录]
    D --&amp;gt; E{Redo 记录对应页&amp;lt;br/&amp;gt;是否需要重放?}
    E --&amp;gt;|Redo LSN &amp;gt; 页 LSN| F[重放该条 Redo]
    F --&amp;gt; G[恢复已提交事务的修改]
    E --&amp;gt;|已落盘，无需重放| H[跳过]
    G --&amp;gt; I[恢复完成，数据库可用]
    H --&amp;gt; I
    J[崩溃时 Redo 尚未 fsync] --&amp;gt; K[事务视为未提交&amp;lt;br/&amp;gt;不会恢复]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：Redo Log 是物理逻辑日志，恢复时按 LSN 顺序重放。InnoDB 会对比 Redo 记录与数据页上的 LSN，避免对已经落盘的页重复应用，保证幂等性。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id=&#34;422-undo-log回滚是如何实现的&#34;&gt;4.2.2 Undo Log：回滚是如何实现的？&lt;/h4&gt;
&lt;p&gt;Redo 记录“新值”用于崩溃后重做；Undo 记录“旧值”用于事务回滚和 MVCC。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Undo Log 回滚流程：&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TD
    A[事务开始 BEGIN] --&amp;gt; B[执行 UPDATE / DELETE]
    B --&amp;gt; C[生成 Undo Log&amp;lt;br/&amp;gt;记录修改前的旧值]
    C --&amp;gt; D[修改 Buffer Pool 数据页]
    D --&amp;gt; E{事务结果}
    E --&amp;gt;|ROLLBACK| F[读取本事务的 Undo Log]
    F --&amp;gt; G[用旧值覆盖 Buffer Pool 中的修改]
    G --&amp;gt; H[释放事务持有的锁]
    H --&amp;gt; I[事务结束，修改被撤销]
    E --&amp;gt;|COMMIT| J[Redo Log 持久化后提交]
    J --&amp;gt; K[Undo Log 标记为可清理]
    K --&amp;gt; L[Purge 线程异步回收]
    L --&amp;gt; M[事务结束]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;&lt;strong&gt;Undo Log 的两个核心作用：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;事务回滚&lt;/strong&gt;：&lt;code&gt;ROLLBACK&lt;/code&gt; 或语句失败时，InnoDB 沿着 Undo Log 把修改逐项撤销，恢复数据到事务开始前的状态，保证原子性。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MVCC 一致性读&lt;/strong&gt;：RR/RC 级别下，普通 &lt;code&gt;SELECT&lt;/code&gt; 不加锁，而是通过 Undo Log 链构造事务开始时可见的数据版本（ReadView），实现无锁并发读。&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：Undo Log 的修改本身也受到 Redo Log 保护。崩溃恢复时，Undo Log 页也会通过 Redo Log 重建，否则回滚能力会丢失。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：事务提交后，Undo Log 不会立即删除。如果其他事务还需要用它构造快照，Undo Log 会继续保留，直到没有事务依赖该旧版本后，Purge 线程才会回收。长事务会导致 Undo 堆积、表空间膨胀。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;43-事务的并发问题&#34;&gt;4.3 事务的并发问题&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;strong&gt;脏读&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;事务 A 读到事务 B 未提交的数据，B 回滚后 A 读到的是脏数据&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;不可重复读&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;事务 A 多次读取同一行，事务 B 修改并提交，导致 A 两次结果不同&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;幻读&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;事务 A 按条件查询一批数据，事务 B 新增/删除满足条件的数据，导致 A 再次查询时结果集不同&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;避坑指南 #5&lt;/strong&gt;：不可重复读侧重“修改”，幻读侧重“新增或删除”。InnoDB 默认的可重复读通过 MVCC + 间隙锁（Gap Lock）在很大程度上解决了幻读。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;44-四种隔离级别&#34;&gt;4.4 四种隔离级别&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph LR
    A[读未提交&amp;lt;br&amp;gt;Read Uncommitted] --&amp;gt; B[读已提交&amp;lt;br&amp;gt;Read Committed]
    B --&amp;gt; C[可重复读&amp;lt;br&amp;gt;Repeatable Read]
    C --&amp;gt; D[串行化&amp;lt;br&amp;gt;Serializable]
    style C fill:#e1f5e1&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;隔离级别&lt;/th&gt;
					&lt;th&gt;脏读&lt;/th&gt;
					&lt;th&gt;不可重复读&lt;/th&gt;
					&lt;th&gt;幻读&lt;/th&gt;
					&lt;th&gt;说明&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;读未提交（RU）&lt;/td&gt;
					&lt;td&gt;是&lt;/td&gt;
					&lt;td&gt;是&lt;/td&gt;
					&lt;td&gt;是&lt;/td&gt;
					&lt;td&gt;基本不使用&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;读已提交（RC）&lt;/td&gt;
					&lt;td&gt;否&lt;/td&gt;
					&lt;td&gt;是&lt;/td&gt;
					&lt;td&gt;是&lt;/td&gt;
					&lt;td&gt;Oracle / SQL Server 默认&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;可重复读（RR）&lt;/td&gt;
					&lt;td&gt;否&lt;/td&gt;
					&lt;td&gt;否&lt;/td&gt;
					&lt;td&gt;否*&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;MySQL 默认&lt;/strong&gt;，InnoDB 通过 MVCC + Gap Lock 解决幻读&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;串行化（S）&lt;/td&gt;
					&lt;td&gt;否&lt;/td&gt;
					&lt;td&gt;否&lt;/td&gt;
					&lt;td&gt;否&lt;/td&gt;
					&lt;td&gt;性能最差，完全串行&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;避坑指南 #6&lt;/strong&gt;：MySQL 默认是可重复读（RR），但很多互联网项目会改为读已提交（RC），因为 RC 锁粒度更小、并发更高。但改为 RC 后，需要业务层自行处理不可重复读问题。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;五索引类型&#34;&gt;五、索引类型&lt;/h2&gt;
&lt;p&gt;索引是 MySQL 高性能的基石。理解索引类型与适用场景，比单纯记住语法更重要。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph TD
    A[MySQL 索引] --&amp;gt; B[普通索引]
    A --&amp;gt; C[唯一索引]
    A --&amp;gt; D[主键索引]
    A --&amp;gt; E[组合索引]
    A --&amp;gt; F[全文索引]
    D --&amp;gt; G[聚簇索引：数据与索引在一起]
    B --&amp;gt; H[二级索引：叶子节点存主键]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;h3 id=&#34;51-普通索引&#34;&gt;5.1 普通索引&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;CREATE&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;INDEX&lt;/span&gt; index_name &lt;span style=&#34;color:#66d9ef&#34;&gt;ON&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;table&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;column&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;length&lt;/span&gt;));&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;h3 id=&#34;52-唯一索引&#34;&gt;5.2 唯一索引&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;CREATE&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;UNIQUE&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;INDEX&lt;/span&gt; indexName &lt;span style=&#34;color:#66d9ef&#34;&gt;ON&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;table&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;column&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;length&lt;/span&gt;));&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;h3 id=&#34;53-主键索引&#34;&gt;5.3 主键索引&lt;/h3&gt;
&lt;p&gt;主键索引是一种特殊的唯一索引，一张表只能有一个主键，且不允许为空。&lt;/p&gt;
&lt;h3 id=&#34;54-组合索引&#34;&gt;5.4 组合索引&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;CREATE&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;INDEX&lt;/span&gt; idx_name_age &lt;span style=&#34;color:#66d9ef&#34;&gt;ON&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;user&lt;/span&gt;(name, age);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;避坑指南 #7&lt;/strong&gt;：组合索引遵循&lt;strong&gt;最左前缀原则&lt;/strong&gt;。&lt;code&gt;idx_name_age&lt;/code&gt; 对 &lt;code&gt;name&lt;/code&gt;、&lt;code&gt;name+age&lt;/code&gt; 有效，但对单独 &lt;code&gt;age&lt;/code&gt; 无效。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;55-全文索引&#34;&gt;5.5 全文索引&lt;/h3&gt;
&lt;p&gt;适用于 &lt;code&gt;LIKE &#39;%关键词%&#39;&lt;/code&gt; 场景，MyISAM 和 InnoDB 5.6+ 都支持。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;六锁机制与-mvcc&#34;&gt;六、锁机制与 MVCC&lt;/h2&gt;
&lt;h3 id=&#34;61-共享锁与排他锁&#34;&gt;6.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;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;S Lock / 读锁&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;SELECT ... LOCK IN SHARE MODE&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;读读兼容，读写互斥&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;排他锁&lt;/td&gt;
					&lt;td&gt;X Lock / 写锁&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;SELECT ... FOR UPDATE&lt;/code&gt; / 增删改&lt;/td&gt;
					&lt;td&gt;与任何锁都互斥&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph LR
    A[共享锁 S] -- 兼容 --&amp;gt; B[共享锁 S]
    A -- 互斥 --&amp;gt; C[排他锁 X]
    C -- 互斥 --&amp;gt; D[排他锁 X]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;避坑指南 #8&lt;/strong&gt;：只有 &lt;code&gt;INSERT / UPDATE / DELETE&lt;/code&gt; 会自动加排他锁；普通 &lt;code&gt;SELECT&lt;/code&gt; 不加锁。如果需要保证读取的是最新已提交数据并防止被修改，应显式使用 &lt;code&gt;SELECT ... FOR UPDATE&lt;/code&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;62-mvcc-实现可重复读&#34;&gt;6.2 MVCC 实现可重复读&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;sequenceDiagram
    participant T1 as 事务 A
    participant DB as InnoDB
    participant T2 as 事务 B

    T1-&amp;gt;&amp;gt;DB: START TRANSACTION&amp;lt;br&amp;gt;创建 ReadView
    T2-&amp;gt;&amp;gt;DB: UPDATE row SET x=2&amp;lt;br&amp;gt;并提交
    T1-&amp;gt;&amp;gt;DB: SELECT x&amp;lt;br&amp;gt;仍读取快照中的 x=1
    Note over T1,DB: 事务 A 始终使用开始时的 ReadView&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;MVCC 不是复制一份数据，而是通过 &lt;strong&gt;ReadView + 数据行隐藏字段 + Undo Log 版本链&lt;/strong&gt;，来判断当前事务应该看到哪个版本的数据。&lt;/p&gt;
&lt;h4 id=&#34;621-数据行的两个隐藏字段&#34;&gt;6.2.1 数据行的两个隐藏字段&lt;/h4&gt;
&lt;p&gt;InnoDB 每行数据都有两个隐藏字段：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;DB_TRX_ID&lt;/code&gt;：最后修改这行的事务 ID。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DB_ROLL_PTR&lt;/code&gt;：回滚指针，指向这行数据在 Undo Log 中的上一个版本。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;622-readview-里面有什么&#34;&gt;6.2.2 ReadView 里面有什么？&lt;/h4&gt;
&lt;p&gt;ReadView 是一个轻量的结构，不保存任何实际数据，只保存生成时刻的事务状态：&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;m_ids&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;生成 ReadView 时，所有未提交事务的 ID 列表&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;min_trx_id&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;m_ids&lt;/code&gt; 中的最小事务 ID&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;max_trx_id&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;生成 ReadView 时，系统下一个要分配的事务 ID&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;creator_trx_id&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;创建该 ReadView 的事务自身 ID&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 id=&#34;623-可见性判断算法&#34;&gt;6.2.3 可见性判断算法&lt;/h4&gt;
&lt;p&gt;对于查询到的一行数据，取出它的 &lt;code&gt;DB_TRX_ID&lt;/code&gt;，按以下规则判断：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;DB_TRX_ID == creator_trx_id&lt;/code&gt;&lt;/strong&gt;：当前事务自己修改的，可见。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;DB_TRX_ID &amp;lt; min_trx_id&lt;/code&gt;&lt;/strong&gt;：在 ReadView 创建前已提交，可见。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;DB_TRX_ID &amp;gt;= max_trx_id&lt;/code&gt;&lt;/strong&gt;：在 ReadView 创建后才启动的事务修改的，不可见。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;min_trx_id &amp;lt;= DB_TRX_ID &amp;lt; max_trx_id&lt;/code&gt;&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;DB_TRX_ID&lt;/code&gt; 在 &lt;code&gt;m_ids&lt;/code&gt; 列表中 → 事务未提交，不可见。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DB_TRX_ID&lt;/code&gt; 不在 &lt;code&gt;m_ids&lt;/code&gt; 列表中 → 事务已提交，可见。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;如果当前版本不可见，就沿着 &lt;code&gt;DB_ROLL_PTR&lt;/code&gt; 到 Undo Log 链中找上一个版本，重复判断，直到找到可见版本。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TD
    A[SELECT 读取一行] --&amp;gt; B[获取 DB_TRX_ID]
    B --&amp;gt; C{可见性判断}
    C --&amp;gt;|可见| D[返回当前版本]
    C --&amp;gt;|不可见| E[沿 DB_ROLL_PTR 读 Undo Log 上一个版本]
    E --&amp;gt; C&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;h4 id=&#34;624-一个具体例子&#34;&gt;6.2.4 一个具体例子&lt;/h4&gt;
&lt;p&gt;假设 &lt;code&gt;id=1&lt;/code&gt; 的行初始值为 &lt;code&gt;x=1&lt;/code&gt;，事务时间线如下：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;时间&lt;/th&gt;
					&lt;th&gt;事务 10&lt;/th&gt;
					&lt;th&gt;事务 20&lt;/th&gt;
					&lt;th&gt;事务 30（查询）&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;T1&lt;/td&gt;
					&lt;td&gt;开始&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;T2&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
					&lt;td&gt;开始&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;T3&lt;/td&gt;
					&lt;td&gt;更新 x=2，未提交&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;T4&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
					&lt;td&gt;更新 x=3，未提交&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;T5&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
					&lt;td&gt;开始，SELECT x&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;事务 30 生成的 ReadView：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;m_ids = [10, 20]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;min_trx_id = 10&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;max_trx_id = 31&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;creator_trx_id = 30&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;查询时，当前行最新版本的 &lt;code&gt;DB_TRX_ID = 20&lt;/code&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;20&lt;/code&gt; 在 &lt;code&gt;m_ids&lt;/code&gt; 中 → 未提交 → 不可见。&lt;/li&gt;
&lt;li&gt;沿 Undo Log 找到上一个版本，&lt;code&gt;DB_TRX_ID = 10&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;10&lt;/code&gt; 在 &lt;code&gt;m_ids&lt;/code&gt; 中 → 未提交 → 不可见。&lt;/li&gt;
&lt;li&gt;再沿 Undo Log 找到初始版本，&lt;code&gt;DB_TRX_ID = 5&lt;/code&gt;（假设为初始插入）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;5 &amp;lt; min_trx_id 10&lt;/code&gt; → 已提交 → 可见。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所以事务 30 读到的是 &lt;code&gt;x=1&lt;/code&gt;。&lt;/p&gt;
&lt;h4 id=&#34;625-rr-与-rc-的区别&#34;&gt;6.2.5 RR 与 RC 的区别&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;RR（可重复读）&lt;/strong&gt;：ReadView 在事务的第一次 SELECT 时创建，之后整个事务复用该 ReadView。因此同一个事务内多次查询结果一致。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;RC（读已提交）&lt;/strong&gt;：每次 SELECT 都会创建新的 ReadView，所以能看到其他事务已经提交的最新数据。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;避坑指南 #9&lt;/strong&gt;：RR 级别下，事务开始后创建一致性视图，后续查询都基于该快照。&lt;code&gt;FOR UPDATE&lt;/code&gt; 可以读到最新数据，同时解决第二类更新丢失问题。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;七mysql-优化四大方向&#34;&gt;七、MySQL 优化四大方向&lt;/h2&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph LR
    A[MySQL 优化] --&amp;gt; B[设计层面]
    A --&amp;gt; C[功能层面]
    A --&amp;gt; D[架构层面]
    A --&amp;gt; E[SQL 层面]
    B --&amp;gt; B1[存储引擎 / 字段类型 / 范式]
    C --&amp;gt; C1[索引 / 缓存 / 分区分表]
    D --&amp;gt; D1[主从复制 / 读写分离 / 负载均衡]
    E --&amp;gt; E1[慢 SQL 分析 / 执行计划]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;h3 id=&#34;71-存储引擎选择&#34;&gt;7.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;th&gt;适用场景&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;InnoDB&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;支持事务、行锁、外键、崩溃恢复&lt;/td&gt;
					&lt;td&gt;需要数据完整性、高并发的 OLTP 业务&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;MyISAM&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;表锁、无事务、查询和插入速度快&lt;/td&gt;
					&lt;td&gt;读多写少、日志型、临时统计表&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;避坑指南 #10&lt;/strong&gt;：MySQL 5.5+ 默认引擎是 InnoDB，新项目&lt;strong&gt;无脑选 InnoDB&lt;/strong&gt;。除非有非常明确的只读/归档场景，否则不要用 MyISAM。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;72-字段类型选择原则&#34;&gt;7.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;尽可能小&lt;/td&gt;
					&lt;td&gt;能用 &lt;code&gt;INT&lt;/code&gt; 不用 &lt;code&gt;BIGINT&lt;/code&gt;，能用 &lt;code&gt;TINYINT&lt;/code&gt; 不用 &lt;code&gt;INT&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;尽可能定长&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;CHAR&lt;/code&gt; 比 &lt;code&gt;VARCHAR&lt;/code&gt; 更适合短定长文本&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;尽可能用整数&lt;/td&gt;
					&lt;td&gt;整数比较和索引效率高于字符串&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;短文本定长&lt;/td&gt;
					&lt;td&gt;用 &lt;code&gt;CHAR&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;短文本变长&lt;/td&gt;
					&lt;td&gt;用 &lt;code&gt;VARCHAR&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;长文本&lt;/td&gt;
					&lt;td&gt;用 &lt;code&gt;TEXT&lt;/code&gt; / &lt;code&gt;BLOB&lt;/code&gt;，避免直接索引大字段&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;73-数据库三大范式&#34;&gt;7.3 数据库三大范式&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph LR
    A[1NF 原子性] --&amp;gt; B[2NF 消除部分依赖]
    B --&amp;gt; C[3NF 消除传递依赖]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&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;第一范式 1NF&lt;/td&gt;
					&lt;td&gt;字段不可再分，每个字段只存单一值&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;第二范式 2NF&lt;/td&gt;
					&lt;td&gt;非主键字段必须完全依赖主键，不能依赖部分主键&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;第三范式 3NF&lt;/td&gt;
					&lt;td&gt;消除传递依赖，非主键字段只依赖主键&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;避坑指南 #11&lt;/strong&gt;：不要过度范式化。互联网业务为了查询性能，常采用&lt;strong&gt;逆范式&lt;/strong&gt;设计，允许适当冗余，用空间换时间。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;74-功能层面优化索引缓存分区分表&#34;&gt;7.4 功能层面优化：索引、缓存、分区分表&lt;/h3&gt;
&lt;p&gt;功能层面优化是在不改变核心业务表结构的前提下，通过索引、缓存、分区分表等手段提升性能。&lt;/p&gt;
&lt;h4 id=&#34;索引优化&#34;&gt;索引优化&lt;/h4&gt;
&lt;p&gt;索引是最常用的功能优化手段。核心原则：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;WHERE&lt;/code&gt;、&lt;code&gt;JOIN&lt;/code&gt;、&lt;code&gt;ORDER BY&lt;/code&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;li&gt;&lt;strong&gt;避免索引失效&lt;/strong&gt;：如函数操作索引列、隐式类型转换、前模糊 &lt;code&gt;LIKE&lt;/code&gt;、OR 半边无索引等。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;缓存策略&#34;&gt;缓存策略&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;查询缓存&lt;/strong&gt;：MySQL 8.0 已移除，不建议依赖。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;应用层缓存&lt;/strong&gt;：Redis / Memcached 缓存热点数据，减少数据库压力。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;注意缓存一致性&lt;/strong&gt;：写数据库后要及时失效或更新缓存，避免脏读。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;分区分表&#34;&gt;分区分表&lt;/h4&gt;
&lt;p&gt;当单表数据量过大（通常超过 500 万 - 1000 万行，或单表超过 10GB）时，需要考虑拆分。主要分为两种：&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;strong&gt;分区（Partition）&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;单张表在物理上拆成多个分区文件，逻辑上仍是一张表&lt;/td&gt;
					&lt;td&gt;基本透明，应用按普通表读写&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;分表（Sharding）&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;把数据拆到多张物理表中，如 &lt;code&gt;user_0&lt;/code&gt; - &lt;code&gt;user_15&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;不透明，通常需要分库分表中间件或应用层路由&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h5 id=&#34;分区partition&#34;&gt;分区（Partition）&lt;/h5&gt;
&lt;p&gt;分区是 MySQL 提供的原生功能，一张逻辑表由多个物理分区组成，每个分区独立存储。&lt;/p&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;&lt;strong&gt;RANGE&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;按连续范围分区，如按时间、ID 范围&lt;/td&gt;
					&lt;td&gt;日志表、订单表按月份归档&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;LIST&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;按离散值列表分区&lt;/td&gt;
					&lt;td&gt;按地区、状态等枚举值&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;HASH&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;按分区键哈希取模&lt;/td&gt;
					&lt;td&gt;数据分布均匀，无业务热点&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;KEY&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;类似 HASH，但由 MySQL 内部决定哈希函数&lt;/td&gt;
					&lt;td&gt;简化使用&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;创建分区表示例（RANGE）：&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;CREATE&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;TABLE&lt;/span&gt; user_log (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    id BIGINT &lt;span style=&#34;color:#66d9ef&#34;&gt;NOT&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;NULL&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    user_id BIGINT &lt;span style=&#34;color:#66d9ef&#34;&gt;NOT&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;NULL&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    action VARCHAR(&lt;span style=&#34;color:#ae81ff&#34;&gt;50&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    created_at DATETIME &lt;span style=&#34;color:#66d9ef&#34;&gt;NOT&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;NULL&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;) PARTITION &lt;span style=&#34;color:#66d9ef&#34;&gt;BY&lt;/span&gt; RANGE (&lt;span style=&#34;color:#66d9ef&#34;&gt;YEAR&lt;/span&gt;(created_at)) (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    PARTITION p2022 &lt;span style=&#34;color:#66d9ef&#34;&gt;VALUES&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;LESS&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;THAN&lt;/span&gt; (&lt;span style=&#34;color:#ae81ff&#34;&gt;2023&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    PARTITION p2023 &lt;span style=&#34;color:#66d9ef&#34;&gt;VALUES&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;LESS&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;THAN&lt;/span&gt; (&lt;span style=&#34;color:#ae81ff&#34;&gt;2024&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    PARTITION p2024 &lt;span style=&#34;color:#66d9ef&#34;&gt;VALUES&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;LESS&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;THAN&lt;/span&gt; (&lt;span style=&#34;color:#ae81ff&#34;&gt;2025&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    PARTITION pfuture &lt;span style=&#34;color:#66d9ef&#34;&gt;VALUES&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;LESS&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;THAN&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;MAXVALUE&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;&lt;strong&gt;分区表的增删改查：&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;-- 查询整个表（和普通表一样）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;SELECT&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;FROM&lt;/span&gt; user_log &lt;span style=&#34;color:#66d9ef&#34;&gt;WHERE&lt;/span&gt; created_at &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;2024-01-01&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;-- 指定分区查询（强制扫描某个分区）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;SELECT&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;FROM&lt;/span&gt; user_log PARTITION (p2024) &lt;span style=&#34;color:#66d9ef&#34;&gt;WHERE&lt;/span&gt; action &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;login&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;-- 插入数据（MySQL 自动根据分区键路由）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;INSERT&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;INTO&lt;/span&gt; user_log &lt;span style=&#34;color:#66d9ef&#34;&gt;VALUES&lt;/span&gt; (&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;10086&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;login&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;2024-06-01 10:00:00&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;-- 更新数据（如果修改分区键，可能需要跨分区移动行）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;UPDATE&lt;/span&gt; user_log &lt;span style=&#34;color:#66d9ef&#34;&gt;SET&lt;/span&gt; created_at &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;2025-01-01&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;WHERE&lt;/span&gt; id &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;-- 删除整个分区的数据（比分批 DELETE 快得多）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;ALTER&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;TABLE&lt;/span&gt; user_log &lt;span style=&#34;color:#66d9ef&#34;&gt;DROP&lt;/span&gt; PARTITION p2022;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;&lt;strong&gt;分区表的关键点：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;分区键必须包含在唯一索引/主键中&lt;/strong&gt;。例如主键是 &lt;code&gt;(id)&lt;/code&gt;，那么不能按 &lt;code&gt;created_at&lt;/code&gt; 分区，除非主键改成 &lt;code&gt;(id, created_at)&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;查询要命中分区键才能裁剪&lt;/strong&gt;。如果 &lt;code&gt;WHERE&lt;/code&gt; 里没有分区键，MySQL 会扫描所有分区，反而更慢。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;分区数量不宜过多&lt;/strong&gt;。MySQL 官方建议单表分区数控制在 1000 以内，过多会消耗文件描述符和内存。&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id=&#34;分表sharding&#34;&gt;分表（Sharding）&lt;/h5&gt;
&lt;p&gt;分表是把一张逻辑表拆成多张物理表，通常需要配合分库分表中间件（如 ShardingSphere、MyCat）或应用层路由。&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;strong&gt;垂直拆分&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;按业务/字段拆分，如 &lt;code&gt;user_base&lt;/code&gt; 和 &lt;code&gt;user_profile&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;单表字段过多、冷热数据分离&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;水平拆分&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;按行拆分，如 &lt;code&gt;user_id % 16&lt;/code&gt; 分 16 张表&lt;/td&gt;
					&lt;td&gt;单表数据量过大、并发过高&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&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;&lt;strong&gt;范围分片&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;按时间/ID 范围，扩容简单&lt;/td&gt;
					&lt;td&gt;容易热点，如最新时间片集中写入&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;哈希分片&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;数据分布均匀&lt;/td&gt;
					&lt;td&gt;扩容需要迁移数据&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;一致性哈希&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;扩容时迁移数据量少&lt;/td&gt;
					&lt;td&gt;实现稍复杂&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;列表分片&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;按地区/类型等枚举值&lt;/td&gt;
					&lt;td&gt;适合有明显业务边界的数据&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;分库分表带来的问题：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;分布式事务&lt;/strong&gt;：跨库事务无法靠单机 ACID 保证。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;跨库 JOIN&lt;/strong&gt;：需要在应用层聚合，或反范式冗余字段。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;全局唯一 ID&lt;/strong&gt;：不能用自增主键，需要雪花算法、号段等方案。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;分页排序&lt;/strong&gt;：&lt;code&gt;ORDER BY&lt;/code&gt; + &lt;code&gt;LIMIT&lt;/code&gt; 跨多表后合并复杂。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数据迁移&lt;/strong&gt;：扩容时需要平滑迁移历史数据。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;避坑指南 #14&lt;/strong&gt;：不要为了拆分而拆分。大多数业务先做好索引、SQL 优化、加缓存，单表撑到千万级没问题。拆分成本：分区 &amp;lt; 分表 &amp;lt; 分库。优先考虑分区；分区也撑不住时，再考虑分库分表。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;75-sql-层面优化慢-sql-分析与执行计划&#34;&gt;7.5 SQL 层面优化：慢 SQL 分析与执行计划&lt;/h3&gt;
&lt;p&gt;SQL 层面的优化收益往往最高，因为一条烂 SQL 可以把整个数据库拖垮。&lt;/p&gt;
&lt;h4 id=&#34;如何发现慢-sql&#34;&gt;如何发现慢 SQL&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;慢查询日志&lt;/strong&gt;：&lt;code&gt;slow_query_log = ON&lt;/code&gt;，&lt;code&gt;long_query_time&lt;/code&gt; 设置阈值。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;performance_schema&lt;/strong&gt;：MySQL 内置性能监控。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;pt-query-digest&lt;/strong&gt;：Percona Toolkit 分析慢日志。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;应用层 APM&lt;/strong&gt;：SkyWalking、Pinpoint 等追踪 SQL 耗时。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;explain-看执行计划&#34;&gt;EXPLAIN 看执行计划&lt;/h4&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;EXPLAIN&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;SELECT&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;FROM&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;`&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;order&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;`&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;WHERE&lt;/span&gt; age &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;18&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;ORDER&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;BY&lt;/span&gt; created_at &lt;span style=&#34;color:#66d9ef&#34;&gt;LIMIT&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&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;type&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;访问类型，越靠前越好：&lt;code&gt;system &amp;gt; const &amp;gt; eq_ref &amp;gt; ref &amp;gt; range &amp;gt; index &amp;gt; ALL&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;key&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;实际使用的索引&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;rows&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;预估扫描行数&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;Extra&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;额外信息&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;type&lt;/code&gt; 详解：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;type&lt;/code&gt; 表示 MySQL 访问表的方式，可以理解为“查资料时翻书的方式”。&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;type&lt;/th&gt;
					&lt;th&gt;通俗解释&lt;/th&gt;
					&lt;th&gt;效率&lt;/th&gt;
					&lt;th&gt;典型 SQL&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;system&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;表里最多 1 条数据，直接当常量&lt;/td&gt;
					&lt;td&gt;⭐⭐⭐⭐⭐&lt;/td&gt;
					&lt;td&gt;查只有一条配置的配置表&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;const&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;通过主键/唯一索引等值查，最多 1 条&lt;/td&gt;
					&lt;td&gt;⭐⭐⭐⭐⭐&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;WHERE id = 1&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;eq_ref&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;JOIN 时，被驱动表用主键/唯一索引匹配，最多 1 条&lt;/td&gt;
					&lt;td&gt;⭐⭐⭐⭐⭐&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;A JOIN B ON A.b_id = B.id&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;ref&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;通过普通索引等值查，可能有多条&lt;/td&gt;
					&lt;td&gt;⭐⭐⭐⭐&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;WHERE phone = &#39;xxx&#39;&lt;/code&gt;（phone 是普通索引）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;range&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;索引范围扫描&lt;/td&gt;
					&lt;td&gt;⭐⭐⭐&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;WHERE age &amp;gt; 18&lt;/code&gt;、&lt;code&gt;BETWEEN&lt;/code&gt;、&lt;code&gt;IN&lt;/code&gt;、&lt;code&gt;LIKE &#39;prefix%&#39;&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;index&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;全索引扫描&lt;/td&gt;
					&lt;td&gt;⭐⭐&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;SELECT 索引列 FROM 表&lt;/code&gt;、按索引排序&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;ALL&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;全表扫描&lt;/td&gt;
					&lt;td&gt;⭐&lt;/td&gt;
					&lt;td&gt;没走索引，逐行读&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;各 type 详细说明与示例：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. system：表本身就是常量&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;表里只有 0 条或 1 条数据，MySQL 把它当成常量处理。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;SELECT&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;FROM&lt;/span&gt; system_config;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;如果 &lt;code&gt;system_config&lt;/code&gt; 只有一条记录，EXPLAIN 可能看到 &lt;code&gt;type = system&lt;/code&gt;。实际业务表中很少见。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. const：精确命中唯一值&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;通过主键或唯一索引做等值查询，最多返回 1 行。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;-- id 是主键
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;SELECT&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;FROM&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;user&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;WHERE&lt;/span&gt; id &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;10086&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;-- phone 是唯一索引
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;SELECT&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;FROM&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;user&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;WHERE&lt;/span&gt; phone &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;13800138000&amp;#39;&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;这是单表查询里最好的 type。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. eq_ref：JOIN 中的 const&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;多表 JOIN 时，被驱动表通过主键或唯一索引与驱动表匹配，每次匹配最多返回 1 行。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;SELECT&lt;/span&gt; o.&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;, u.name
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;FROM&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;`&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;order&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;`&lt;/span&gt; o
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;JOIN&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;user&lt;/span&gt; u &lt;span style=&#34;color:#66d9ef&#34;&gt;ON&lt;/span&gt; o.user_id &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; u.id;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;假设 &lt;code&gt;user.id&lt;/code&gt; 是主键，&lt;code&gt;order&lt;/code&gt; 表的每一行去 &lt;code&gt;user&lt;/code&gt; 表查都是精确命中 1 行，此时 &lt;code&gt;user&lt;/code&gt; 表的 &lt;code&gt;type = eq_ref&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4. ref：普通索引等值查询&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;通过普通索引（非唯一索引）做等值查询，可能返回多条。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;-- status 是普通索引
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;SELECT&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;FROM&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;`&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;order&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;`&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;WHERE&lt;/span&gt; status &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;PAID&amp;#39;&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;&lt;code&gt;status = &#39;PAID&#39;&lt;/code&gt; 可能对应很多订单。这是生产环境最常见的良好 type。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5. range：索引范围扫描&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;使用索引做范围查询。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;-- age 有索引
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;SELECT&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;FROM&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;user&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;WHERE&lt;/span&gt; age &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;18&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;-- create_time 有索引
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;SELECT&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;FROM&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;`&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;order&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;WHERE&lt;/span&gt; create_time &lt;span style=&#34;color:#66d9ef&#34;&gt;BETWEEN&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;2024-01-01&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;AND&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;2024-01-31&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;-- id 是主键
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;SELECT&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;FROM&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;user&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;WHERE&lt;/span&gt; id &lt;span style=&#34;color:#66d9ef&#34;&gt;IN&lt;/span&gt; (&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;-- phone 有索引
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;SELECT&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;FROM&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;user&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;WHERE&lt;/span&gt; phone &lt;span style=&#34;color:#66d9ef&#34;&gt;LIKE&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1380%&amp;#39;&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;注意：&lt;code&gt;LIKE &#39;1380%&#39;&lt;/code&gt; 走 range；&lt;code&gt;LIKE &#39;%1380&#39;&lt;/code&gt; 或 &lt;code&gt;LIKE &#39;%1380%&#39;&lt;/code&gt; 会退化成 ALL。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6. index：全索引扫描&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;遍历整个索引树，而不是全表扫描数据。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;-- name 是索引列
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;SELECT&lt;/span&gt; name &lt;span style=&#34;color:#66d9ef&#34;&gt;FROM&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;user&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;-- 按索引列排序
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;SELECT&lt;/span&gt; id &lt;span style=&#34;color:#66d9ef&#34;&gt;FROM&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;user&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;ORDER&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;BY&lt;/span&gt; id;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;因为索引通常比实际数据小，读索引比读整行快。但如果查询列不在索引里或需要大量回表，index 也会很慢。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7. ALL：全表扫描&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;没有用到索引，逐行读取整张表。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;-- phone 没有索引
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;SELECT&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;FROM&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;user&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;WHERE&lt;/span&gt; phone &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;13800138000&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;-- 对索引列使用函数
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;SELECT&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;FROM&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;`&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;order&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;`&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;WHERE&lt;/span&gt; DATE(create_time) &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;2024-01-01&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;-- 前模糊 LIKE
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;SELECT&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;FROM&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;user&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;WHERE&lt;/span&gt; phone &lt;span style=&#34;color:#66d9ef&#34;&gt;LIKE&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;%1380%&amp;#39;&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;生产环境出现 &lt;code&gt;type = ALL&lt;/code&gt; 通常需要优化。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;简单记忆口诀：&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;system/const 是精确，一行数据最省事；
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;eq_ref 是 JOIN 版 const，主键关联一对一；
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ref 普通索引等值查，可能返回好几行；
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;range 范围扫描用得广，BETWEEN IN 都能上；
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;index 全扫索引比表强；
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ALL 全表扫描要提防。&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&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;type&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;system / const / eq_ref / ref&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;优秀，基本不用管&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;range&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;正常，注意范围不要太大&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;index&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;需要警惕，确认是否真的需要全索引扫描&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;ALL&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;必须优化，除非表很小（几万行以内）&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;一般原则：单表查询尽量做到 &lt;code&gt;ref&lt;/code&gt; 或更好，范围查询 &lt;code&gt;range&lt;/code&gt; 可接受，尽量避免 &lt;code&gt;ALL&lt;/code&gt; 和大量数据的 &lt;code&gt;index&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;危险的 Extra：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Using filesort&lt;/code&gt;：需要额外排序，常见于无索引的 &lt;code&gt;ORDER BY&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Using temporary&lt;/code&gt;：需要创建临时表，常见于 &lt;code&gt;GROUP BY&lt;/code&gt;、&lt;code&gt;DISTINCT&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Using index&lt;/code&gt;：好现象，使用了覆盖索引。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Using where&lt;/code&gt;：在存储引擎返回数据后再过滤。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;典型慢-sql-写法与严重踩坑案例&#34;&gt;典型慢 SQL 写法与严重踩坑案例&lt;/h4&gt;
&lt;h5 id=&#34;案例-1select-&#34;&gt;案例 1：&lt;code&gt;SELECT *&lt;/code&gt;&lt;/h5&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;SELECT&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;FROM&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;`&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;order&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;`&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;WHERE&lt;/span&gt; user_id &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;10086&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;&lt;strong&gt;问题&lt;/strong&gt;：读取不需要的列，增加网络传输和 Buffer Pool 占用，容易触发回表。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;优化&lt;/strong&gt;：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;SELECT&lt;/span&gt; order_id, status, amount &lt;span style=&#34;color:#66d9ef&#34;&gt;FROM&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;`&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;order&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;`&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;WHERE&lt;/span&gt; user_id &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;10086&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;h5 id=&#34;案例-2前模糊-like-导致索引失效&#34;&gt;案例 2：前模糊 LIKE 导致索引失效&lt;/h5&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;SELECT&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;FROM&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;user&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;WHERE&lt;/span&gt; phone &lt;span style=&#34;color:#66d9ef&#34;&gt;LIKE&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;%1380%&amp;#39;&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;&lt;strong&gt;问题&lt;/strong&gt;：&lt;code&gt;%&lt;/code&gt; 在开头无法使用 B+ 树索引，会全表扫描。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;优化&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;改为后缀匹配：&lt;code&gt;LIKE &#39;1380%&#39;&lt;/code&gt;（如果有索引仍可用）。&lt;/li&gt;
&lt;li&gt;使用全文索引或 Elasticsearch。&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id=&#34;案例-3or-条件半边无索引&#34;&gt;案例 3：&lt;code&gt;OR&lt;/code&gt; 条件半边无索引&lt;/h5&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;SELECT&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;FROM&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;user&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;WHERE&lt;/span&gt; phone &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;13800138000&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;OR&lt;/span&gt; email &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;a@b.com&amp;#39;&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;&lt;strong&gt;问题&lt;/strong&gt;：如果 &lt;code&gt;email&lt;/code&gt; 没有索引，MySQL 可能放弃索引，直接全表扫描。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;优化&lt;/strong&gt;：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;SELECT&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;FROM&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;user&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;WHERE&lt;/span&gt; phone &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;13800138000&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;UNION&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;ALL&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;SELECT&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;FROM&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;user&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;WHERE&lt;/span&gt; email &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;a@b.com&amp;#39;&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;或给 &lt;code&gt;email&lt;/code&gt; 也建索引。&lt;/p&gt;
&lt;h5 id=&#34;案例-4隐式类型转换&#34;&gt;案例 4：隐式类型转换&lt;/h5&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;SELECT&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;FROM&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;user&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;WHERE&lt;/span&gt; phone &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;13800138000&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;&lt;strong&gt;问题&lt;/strong&gt;：&lt;code&gt;phone&lt;/code&gt; 是 &lt;code&gt;varchar&lt;/code&gt;，MySQL 会先把所有行的 &lt;code&gt;phone&lt;/code&gt; 转成整数再比较，索引失效。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;优化&lt;/strong&gt;：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;SELECT&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;FROM&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;user&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;WHERE&lt;/span&gt; phone &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;13800138000&amp;#39;&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;h5 id=&#34;案例-5函数操作索引列&#34;&gt;案例 5：函数操作索引列&lt;/h5&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;SELECT&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;FROM&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;`&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;order&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;`&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;WHERE&lt;/span&gt; DATE(create_time) &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;2024-01-01&amp;#39;&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;&lt;strong&gt;问题&lt;/strong&gt;：对索引列使用函数，&lt;code&gt;DATE(create_time)&lt;/code&gt; 不是索引本身，索引失效。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;优化&lt;/strong&gt;：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;SELECT&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;FROM&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;`&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;order&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;WHERE&lt;/span&gt; create_time &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;2024-01-01&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;AND&lt;/span&gt; create_time &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;2024-01-02&amp;#39;&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;h5 id=&#34;案例-6大-offset-深分页&#34;&gt;案例 6：大 OFFSET 深分页&lt;/h5&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;SELECT&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;FROM&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;`&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;order&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;`&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;ORDER&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;BY&lt;/span&gt; id &lt;span style=&#34;color:#66d9ef&#34;&gt;LIMIT&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1000000&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;&lt;strong&gt;问题&lt;/strong&gt;：MySQL 需要先扫描 1000010 行，再扔掉前面的 1000000 行，越往后越慢。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;优化&lt;/strong&gt;（延迟关联）：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;SELECT&lt;/span&gt; o.&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;FROM&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;`&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;order&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;`&lt;/span&gt; o
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;JOIN&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;SELECT&lt;/span&gt; id &lt;span style=&#34;color:#66d9ef&#34;&gt;FROM&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;`&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;order&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;`&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;ORDER&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;BY&lt;/span&gt; id &lt;span style=&#34;color:#66d9ef&#34;&gt;LIMIT&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1000000&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;) tmp &lt;span style=&#34;color:#66d9ef&#34;&gt;ON&lt;/span&gt; o.id &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; tmp.id;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;或记录上次位置：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;SELECT&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;FROM&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;`&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;order&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;`&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;WHERE&lt;/span&gt; id &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;?&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;ORDER&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;BY&lt;/span&gt; id &lt;span style=&#34;color:#66d9ef&#34;&gt;LIMIT&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;h5 id=&#34;案例-7in-子句元素过多&#34;&gt;案例 7：&lt;code&gt;IN&lt;/code&gt; 子句元素过多&lt;/h5&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;SELECT&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;FROM&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;user&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;WHERE&lt;/span&gt; id &lt;span style=&#34;color:#66d9ef&#34;&gt;IN&lt;/span&gt; (&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;, ..., &lt;span style=&#34;color:#ae81ff&#34;&gt;50000&lt;/span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;&lt;strong&gt;问题&lt;/strong&gt;：IN 元素过多会导致解析和执行效率下降，甚至超出限制。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;优化&lt;/strong&gt;：分批查询，或把 ID 写入临时表再 JOIN。&lt;/p&gt;
&lt;h5 id=&#34;案例-8大事务长时间持有锁&#34;&gt;案例 8：大事务长时间持有锁&lt;/h5&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;START&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;TRANSACTION&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;-- 大量更新操作
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;UPDATE&lt;/span&gt; huge_table &lt;span style=&#34;color:#66d9ef&#34;&gt;SET&lt;/span&gt; ... &lt;span style=&#34;color:#66d9ef&#34;&gt;WHERE&lt;/span&gt; ...;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;-- 业务逻辑处理
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;-- 几分钟后才 COMMIT&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;&lt;strong&gt;问题&lt;/strong&gt;：长时间持有行锁/间隙锁，阻塞其他事务，Undo Log 堆积，主从延迟放大。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;优化&lt;/strong&gt;：拆分为小批量事务，减少单次锁定范围。&lt;/p&gt;
&lt;h4 id=&#34;线上最严重的几个踩坑&#34;&gt;线上最严重的几个踩坑&lt;/h4&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;UPDATE / DELETE&lt;/code&gt; 忘记写 &lt;code&gt;WHERE&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;全表数据被改/删，可能导致业务瘫痪&lt;/td&gt;
					&lt;td&gt;先 &lt;code&gt;SELECT&lt;/code&gt; 确认影响行数，使用 &lt;code&gt;LIMIT&lt;/code&gt;，小批量执行&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;高峰期执行 &lt;code&gt;ALTER TABLE&lt;/code&gt; / &lt;code&gt;OPTIMIZE&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;锁表，查询堆积，服务雪崩&lt;/td&gt;
					&lt;td&gt;使用 Online DDL（如 &lt;code&gt;pt-online-schema-change&lt;/code&gt;），低峰期执行&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;直接 &lt;code&gt;SELECT * FROM huge_table&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;打满 Buffer Pool，拖垮数据库&lt;/td&gt;
					&lt;td&gt;加分页、加条件、只查必要字段&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;慢 SQL 打满连接池&lt;/td&gt;
					&lt;td&gt;应用无法获取连接，全面超时&lt;/td&gt;
					&lt;td&gt;设置 SQL 超时，连接池隔离，熔断降级&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;避坑指南 #15&lt;/strong&gt;：上线任何 SQL 前，先用 &lt;code&gt;EXPLAIN&lt;/code&gt; 检查执行计划。看到 &lt;code&gt;type = ALL&lt;/code&gt;、&lt;code&gt;Extra = Using filesort&lt;/code&gt; 或 &lt;code&gt;Using temporary&lt;/code&gt; 时，必须优化后再上线。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;八高性能架构设计&#34;&gt;八、高性能架构设计&lt;/h2&gt;
&lt;h3 id=&#34;81-主从复制--读写分离&#34;&gt;8.1 主从复制 + 读写分离&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph LR
    A[写请求] --&amp;gt; B[主库 Master]
    B --&amp;gt;|binlog 同步| C[从库 Slave 1]
    B --&amp;gt;|binlog 同步| D[从库 Slave 2]
    E[读请求] --&amp;gt; C
    E --&amp;gt; D&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;避坑指南 #12&lt;/strong&gt;：主从复制存在延迟。如果对实时性要求极高（如支付后立刻查询余额），读请求应强制走主库，或在业务层做延迟容忍设计。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;82-分区分表&#34;&gt;8.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;垂直拆分&lt;/td&gt;
					&lt;td&gt;按业务/字段拆分，如用户表拆成 &lt;code&gt;user_base&lt;/code&gt; 和 &lt;code&gt;user_profile&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;水平拆分&lt;/td&gt;
					&lt;td&gt;按行拆分，如按 &lt;code&gt;user_id % 16&lt;/code&gt; 分 16 张表&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id=&#34;九用户权限与数据导出&#34;&gt;九、用户权限与数据导出&lt;/h2&gt;
&lt;h3 id=&#34;91-创建新用户并授权&#34;&gt;9.1 创建新用户并授权&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;-- 创建用户
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;CREATE&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;USER&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;goose_vod&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;@&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;%&amp;#39;&lt;/span&gt; IDENTIFIED &lt;span style=&#34;color:#66d9ef&#34;&gt;BY&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;xxxx&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;-- 授权
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;GRANT&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;ALL&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;PRIVILEGES&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;ON&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;`&lt;/span&gt;ad_db&lt;span style=&#34;color:#f92672&#34;&gt;`&lt;/span&gt;.&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;TO&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;`&lt;/span&gt;goose_vod&lt;span style=&#34;color:#f92672&#34;&gt;`@`%`&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;-- 刷新权限
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;FLUSH &lt;span style=&#34;color:#66d9ef&#34;&gt;PRIVILEGES&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;避坑指南 #13&lt;/strong&gt;：生产环境请遵循最小权限原则，不要给业务账号 &lt;code&gt;ALL PRIVILEGES&lt;/code&gt;。例如只读账号只授予 &lt;code&gt;SELECT&lt;/code&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;92-导出数据库&#34;&gt;9.2 导出数据库&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mysqldump -h xxxx-rds.xxxx.us-east-1.rds.amazonaws.com &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -u goose_vod -p appstore_db &amp;gt; ~/database_dump.txt&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;十避坑清单速查表&#34;&gt;十、避坑清单速查表&lt;/h2&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;编号&lt;/th&gt;
					&lt;th&gt;避坑点&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;1&lt;/td&gt;
					&lt;td&gt;数据库字符集用 &lt;code&gt;utf8mb4&lt;/code&gt;，不要用默认 &lt;code&gt;utf8&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;2&lt;/td&gt;
					&lt;td&gt;JDBC 连接串指定 &lt;code&gt;serverTimezone&lt;/code&gt; 和 &lt;code&gt;characterEncoding&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;3&lt;/td&gt;
					&lt;td&gt;通用日志只用于临时排查，不要长期开启&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;4&lt;/td&gt;
					&lt;td&gt;存储过程谨慎使用，优先应用层处理业务逻辑&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;5&lt;/td&gt;
					&lt;td&gt;不可重复读 ≠ 幻读，RR 级别下 InnoDB 基本解决幻读&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;6&lt;/td&gt;
					&lt;td&gt;根据业务决定是否将隔离级别从 RR 改为 RC&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;7&lt;/td&gt;
					&lt;td&gt;组合索引遵循最左前缀原则&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;8&lt;/td&gt;
					&lt;td&gt;普通 &lt;code&gt;SELECT&lt;/code&gt; 不加锁，需要锁时显式 &lt;code&gt;FOR UPDATE&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;9&lt;/td&gt;
					&lt;td&gt;RR 下快照读基于 ReadView，&lt;code&gt;FOR UPDATE&lt;/code&gt; 读最新数据&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;10&lt;/td&gt;
					&lt;td&gt;新项目默认使用 InnoDB，不要选 MyISAM&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;11&lt;/td&gt;
					&lt;td&gt;适度逆范式，用空间换查询时间&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;12&lt;/td&gt;
					&lt;td&gt;主从延迟存在，实时读考虑强制走主库&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;13&lt;/td&gt;
					&lt;td&gt;生产环境遵循最小权限原则&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;14&lt;/td&gt;
					&lt;td&gt;不要为了拆分而拆分；拆分成本：分区 &amp;lt; 分表 &amp;lt; 分库，优先考虑分区&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;15&lt;/td&gt;
					&lt;td&gt;上线前用 EXPLAIN 检查执行计划，警惕 ALL / filesort / temporary&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id=&#34;十一总结&#34;&gt;十一、总结&lt;/h2&gt;
&lt;p&gt;MySQL 的学习路径可以概括为：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph LR
    A[熟练 SQL 与安装配置] --&amp;gt; B[理解事务与隔离级别]
    B --&amp;gt; C[掌握索引设计与优化]
    C --&amp;gt; D[理解锁与 MVCC]
    D --&amp;gt; E[掌握主从复制与读写分离]
    E --&amp;gt; F[持续分析慢 SQL 与执行计划]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;高性能 MySQL 没有银弹，核心在于：&lt;strong&gt;合理的表结构设计、精准的索引、恰到好处的事务隔离、以及能够水平扩展的架构&lt;/strong&gt;。希望这份笔记能成为你日常开发与面试复习的速查手册。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;参考&#34;&gt;参考&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.cnblogs.com/zhangkanghui/p/9613844.html&#34;&gt;MySQL 5.7 安装教程&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.cnblogs.com/sharpest/p/10390035.html&#34;&gt;MySQL 优化四大方面&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://blog.csdn.net/weixin_43895671/article/details/107402521&#34;&gt;MySQL 锁与事务详解&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
  </channel>
</rss>
