<?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/%E4%BA%8B%E5%8A%A1/</link>
    <description>AI 视频、AI Agent 与工程实践笔记</description>
    <generator>Hugo</generator>
    <language>zh-CN</language>
    <lastBuildDate>Tue, 08 Aug 2023 10:40:00 +0800</lastBuildDate><atom:link href="https://ftmao.com/blog/tags/%E4%BA%8B%E5%8A%A1/index.xml" rel="self" type="application/rss+xml" /><item>
      <title>《设计数据密集型应用》学习总结：存量、流量、回路——每个设计决策都是三者的权衡</title>
      <link>https://ftmao.com/blog/bookstore/ddia-book-notes/</link>
      <pubDate>Tue, 08 Aug 2023 10:40:00 +0800</pubDate>
      <guid>https://ftmao.com/blog/bookstore/ddia-book-notes/</guid>
      <description>&lt;h1 id=&#34;设计数据密集型应用学习总结存量流量回路每个设计决策都是三者的权衡&#34;&gt;《设计数据密集型应用》学习总结：存量、流量、回路——每个设计决策都是三者的权衡&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;目标读者：写过 CRUD、用过 MySQL/Redis/Kafka，但说不清它们&amp;quot;为什么这么设计&amp;quot;的工程师。&lt;br&gt;
参考书籍：Martin Kleppmann《Designing Data-Intensive Applications》（DDIA，那只野猪书）&lt;br&gt;
阅读目标：不背目录，而是拿到一个能套在任何存储/中间件上的分析框架——存量、流量、回路。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;0-写在前面别背组件背权衡&#34;&gt;0. 写在前面：别背组件，背权衡&lt;/h2&gt;
&lt;p&gt;市面上的&amp;quot;中间件教程&amp;quot;有个通病：教你 MySQL 怎么用、Redis 怎么用、Kafka 怎么用，但从不告诉你&lt;strong&gt;为什么它们长得这么不一样&lt;/strong&gt;。同样是存数据，Redis 敢把数据放内存，MySQL 不敢；同样是日志，Kafka 能当数据库用，而大多数人只拿它当管道。&lt;/p&gt;
&lt;p&gt;DDIA 这本书最值钱的地方，就是把这些&amp;quot;为什么&amp;quot;串成了一条线。读完我把它提炼成三个词：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;存量（State）&lt;/strong&gt;：数据怎么&lt;strong&gt;存&lt;/strong&gt;——存储引擎（LSM/B-Tree）、编码格式、行存列存。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;流量（Flow）&lt;/strong&gt;：数据怎么&lt;strong&gt;动&lt;/strong&gt;——复制、分区、派生数据、流处理。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;回路（Loop）&lt;/strong&gt;：系统怎么&lt;strong&gt;自我纠错&lt;/strong&gt;——事务、隔离级别、共识、线性一致性。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;一句话理解：&lt;strong&gt;任何一个数据系统的设计决策，都是在存量成本、流量吞吐、回路强度三者之间做取舍。&lt;/strong&gt; 你要强一致（回路强），就得牺牲可用性或延迟（流量贵）；你要写入快（流量大），就得接受读时合并或后台压缩的代价（存量结构变复杂）。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;避坑指南 0&lt;/strong&gt;：读完这本书最大的误区是&amp;quot;上来就选最强的一致性&amp;quot;。Kleppmann 自己说过大意如此的话：一致性模型的选择应该由&lt;strong&gt;业务能不能容忍异常&lt;/strong&gt;决定，而不是由工程师的洁癖决定。能容忍就用最终一致，性能白捡。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;和博客已有的 Redis/MySQL/MQ 笔记是&amp;quot;原理 vs 用法&amp;quot;的关系：那些笔记讲&amp;quot;这个命令怎么用、这个参数怎么调&amp;quot;，这篇讲&amp;quot;这个组件为什么被设计成这样&amp;quot;。建议搭配阅读——看完这篇再回头看 Redis 的 RDB/AOF、MySQL 的 InnoDB redo log、Kafka 的副本协议，会有&amp;quot;原来如此&amp;quot;的感觉。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;第一部分-存量数据怎么存&#34;&gt;第一部分 存量：数据怎么存&lt;/h2&gt;
&lt;h3 id=&#34;1-lsm-tree-vs-b-tree写的快和读的快不可兼得&#34;&gt;1. LSM-Tree vs B-Tree：写的快和读的快，不可兼得&lt;/h3&gt;
&lt;p&gt;数据库索引结构就两大流派，区别一句话：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;B-Tree&lt;/strong&gt;：读的时候顺藤摸瓜，树的高度就是磁盘寻道次数。写入是&amp;quot;就地更新&amp;quot;——找到那一页，改掉。MySQL InnoDB 就是这个。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;LSM-Tree&lt;/strong&gt;：写入只往内存里的有序结构（Memtable）追加，内存写满了就整体刷到磁盘变成一个 &lt;strong&gt;SSTable&lt;/strong&gt;（Sorted String Table，一段有序的文件）。读取时从最新的 SSTable 往最旧的依次找。LevelDB、RocksDB、Cassandra、HBase 都是这个。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;SSTable 合并（compaction）是 LSM 的灵魂&lt;/strong&gt;。因为同一个 key 的新旧版本散落在多个 SSTable 里，后台必须定期把它们归并排序、去重、丢掉被删除的墓碑标记，否则文件越积越多，读放大失控。&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
    W[&amp;#34;写入请求&amp;#34;] --&amp;gt; L[&amp;#34;WAL 追加日志&amp;lt;br/&amp;gt;（崩溃恢复用）&amp;#34;]
    L --&amp;gt; M[&amp;#34;Memtable&amp;lt;br/&amp;gt;（内存有序表）&amp;#34;]
    M --&amp;gt;|&amp;#34;写满阈值&amp;#34;| S1[&amp;#34;SSTable 1&amp;lt;br/&amp;gt;（不可变文件）&amp;#34;]
    M --&amp;gt;|&amp;#34;再写满&amp;#34;| S2[&amp;#34;SSTable 2&amp;#34;]
    M --&amp;gt;|&amp;#34;又写满&amp;#34;| S3[&amp;#34;SSTable 3&amp;#34;]
    S1 --&amp;gt; C[&amp;#34;后台 Compaction&amp;lt;br/&amp;gt;归并排序 + 去重 + 清墓碑&amp;#34;]
    S2 --&amp;gt; C
    S3 --&amp;gt; C
    C --&amp;gt; S4[&amp;#34;合并后的大 SSTable&amp;lt;br/&amp;gt;（旧文件丢弃）&amp;#34;]
    R[&amp;#34;读取请求&amp;#34;] --&amp;gt; M
    R --&amp;gt; S3
    R --&amp;gt; S2
    R --&amp;gt; S1
    R --&amp;gt;|&amp;#34;配合布隆过滤器&amp;lt;br/&amp;gt;跳过不含该 key 的文件&amp;#34;| MISS[&amp;#34;快速判不存在&amp;#34;]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;小数字例子感受写放大：假设每写 1 GB 数据，经过 3 层 compaction，每层都要重写一遍，实际磁盘写入可能是 5~10 GB。这就是&amp;quot;写放大&amp;quot;，也是 RocksDB 调优的核心战场。&lt;/p&gt;
&lt;p&gt;LSM 读取慢怎么补救？靠&lt;strong&gt;布隆过滤器（Bloom filter）&lt;/strong&gt;：一个很小的内存位图，能告诉你&amp;quot;这个 key 肯定不在这个 SSTable 里&amp;quot;（可能有误报，但绝不漏报）。查一个不存在的 key 时，布隆过滤器直接拦掉，不用真的去翻磁盘文件——这对&amp;quot;查缓存未命中再查库&amp;quot;的场景是救命优化。Cassandra、HBase、RocksDB 全都内置。&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 style=&#34;text-align: left&#34;&gt;维度&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;B-Tree&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;LSM-Tree&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;写入&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;就地更新，写放大相对低但随机 IO&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;顺序追加，吞吐高但有 compaction 写放大&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;读取&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;稳定，一次查找几层树&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;要查多层文件，靠布隆过滤器和缓存补救&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;磁盘碎片&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;页分裂会产生碎片&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;文件整体重写，天然紧凑&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;典型代表&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;InnoDB、PostgreSQL&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;RocksDB、Cassandra、HBase&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;一句话理解&lt;/strong&gt;：B-Tree 是&amp;quot;图书馆书架&amp;quot;，书按位置摆好，找书快、插书要挪位置；LSM 是&amp;quot;先记在便签上，攒一批再上架&amp;quot;，记便签飞快，但定期要整理书架，找书得翻几摞便签。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;2-wal所有存储引擎的保命符&#34;&gt;2. WAL：所有存储引擎的&amp;quot;保命符&amp;quot;&lt;/h3&gt;
&lt;p&gt;LSM 写 Memtable、B-Tree 改数据页，都是先动内存。断电怎么办？答案是 &lt;strong&gt;WAL（Write-Ahead Log，预写日志）&lt;/strong&gt;：&lt;strong&gt;任何修改先追加一条日志到磁盘，再改内存结构&lt;/strong&gt;。崩溃后重放日志恢复。&lt;/p&gt;
&lt;p&gt;这个思想无处不在：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;InnoDB 的 redo log 是 WAL；&lt;/li&gt;
&lt;li&gt;PostgreSQL 的 WAL 直接就叫 WAL；&lt;/li&gt;
&lt;li&gt;Redis 的 AOF 本质也是 WAL；&lt;/li&gt;
&lt;li&gt;Kafka 的消息日志、ZooKeeper 的事务日志，全是同一个套路。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;一句话理解&lt;/strong&gt;：WAL 就是把&amp;quot;随机写&amp;quot;变成&amp;quot;顺序写&amp;quot;——磁盘顺序追加比随机写快几个数量级，顺便白捡了崩溃恢复能力。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;3-行存-vs-列存oltp-与-olap-的分水岭&#34;&gt;3. 行存 vs 列存：OLTP 与 OLAP 的分水岭&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;行存&lt;/strong&gt;：一行的所有列连续存放。适合&amp;quot;查一个人的全部信息&amp;quot;（点查、事务），即 OLTP。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;列存&lt;/strong&gt;：一列的所有值连续存放。适合&amp;quot;统计一百万人的平均年龄&amp;quot;（聚合、分析），即 OLAP。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;为什么列存分析快？小数字例子：一张 100 列的表，分析查询只用 5 列。行存要把 100 列全部从磁盘读出来再丢弃 95 列；列存只读那 5 列，IO 直接省 95%。而且单列数据类型相同，压缩率极高（比如&amp;quot;性别&amp;quot;列去重后就两个值，位图编码一下几乎不占空间）。&lt;/p&gt;
&lt;p&gt;代表：Parquet、ORC、ClickHouse、DuckDB 是列存；MySQL、PostgreSQL 是行存。Snowflake、BigQuery 这类数仓全部站在列存一边。&lt;/p&gt;
&lt;h3 id=&#34;4-编码与演进-schema-变了老数据怎么办&#34;&gt;4. 编码与演进： schema 变了，老数据怎么办&lt;/h3&gt;
&lt;p&gt;存量还有个隐藏维度：&lt;strong&gt;数据格式会随时间演化&lt;/strong&gt;。JSON 灵活但没约束；Protocol Buffers / Thrift / Avro 这类二进制编码通过&lt;strong&gt;字段编号&lt;/strong&gt;实现前后兼容——新增字段给新编号，删掉字段不动旧编号，新老代码就能和平共处。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;避坑指南 1&lt;/strong&gt;：滚动升级时&amp;quot;新代码读老数据&amp;quot;和&amp;quot;老代码读新数据&amp;quot;会同时发生。所以兼容性必须是&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;单台机器存不下、扛不住、会宕机——这是&amp;quot;流量篇&amp;quot;要解决的三个问题。对应三板斧：&lt;strong&gt;复制&lt;/strong&gt;（同一份数据放多台机器）、&lt;strong&gt;分区&lt;/strong&gt;（不同数据放不同机器）、&lt;strong&gt;派生数据&lt;/strong&gt;（数据从一个系统流向另一个系统）。&lt;/p&gt;
&lt;h3 id=&#34;5-复制主从多主无主三种政治体制&#34;&gt;5. 复制：主从、多主、无主，三种政治体制&lt;/h3&gt;
&lt;p&gt;复制的目标是可用性和读扩展，但引入了全书最核心的矛盾：&lt;strong&gt;副本之间怎么保持一致&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;主从复制（单主）&lt;/strong&gt;：所有写请求走 Leader，Leader 把变更日志同步给 Follower。读可以分摊到 Follower。简单、直觉，MySQL、PostgreSQL、MongoDB（副本集）、Kafka 分区副本都是这个思路。&lt;/p&gt;
&lt;p&gt;但&amp;quot;Follower 的数据是旧版本&amp;quot;这件事，会带来一连串用户可感知的诡异现象：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;你刚发的朋友圈自己刷新后消失了&lt;/strong&gt; → 需要&lt;strong&gt;读己之写（read-your-writes）&lt;/strong&gt;：用户写完之后，读请求必须走 Leader 或带版本号校验。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;你的余额从 100 变成 80 又变回 100&lt;/strong&gt;（读到了更旧的副本）→ 需要&lt;strong&gt;单调读（monotonic reads）&lt;/strong&gt;：一个用户的连续读请求固定打到同一个副本。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;聊天里先看到回复再看到原帖&lt;/strong&gt;（分区的数据流向不同副本速度不同）→ 需要&lt;strong&gt;一致前缀读（consistent prefix reads）&lt;/strong&gt;：有因果关系的写入，任何读者都应该按相同顺序看到。&lt;/li&gt;
&lt;/ul&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 C as 客户端
    participant L as Leader（主库）
    participant F1 as Follower 1（同步副本）
    participant F2 as Follower 2（异步副本）

    C-&amp;gt;&amp;gt;L: 写 x = 1
    L-&amp;gt;&amp;gt;L: 写本地 WAL 并提交
    L-&amp;gt;&amp;gt;F1: 同步复制：等 F1 确认
    F1--&amp;gt;&amp;gt;L: ACK
    L--&amp;gt;&amp;gt;C: 写入成功
    L-&amp;gt;&amp;gt;F2: 异步复制：后台慢慢发
    Note over F2: 此时 F2 还是旧值 x = 0&amp;lt;br/&amp;gt;复制延迟窗口
    C-&amp;gt;&amp;gt;F2: 读 x
    F2--&amp;gt;&amp;gt;C: x = 0（读到旧数据！）
    Note over C,F2: 这就是&amp;#34;读己之写&amp;#34;被破坏的瞬间&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;&lt;strong&gt;同步 vs 异步是个硬权衡&lt;/strong&gt;：全同步，任何一个副本宕机写入就卡死；全异步，Leader 宕机时没同步完的写入直接丢失。实践中的折中是&amp;quot;半同步&amp;quot;——一个副本同步、其余异步（MySQL 的 semi-sync 就是这个）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;多主复制&lt;/strong&gt;：多个节点都能接受写，彼此同步。适合多数据中心就近写入，但&lt;strong&gt;写冲突&lt;/strong&gt;是噩梦——两个机房同时改同一条数据，谁说了算？常见解法：LWW（最后写入者胜，靠时间戳，会丢数据）、CRDT（无冲突数据结构）、应用层解决。CouchDB 走这条路。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;无主复制（Dynamo 风格）&lt;/strong&gt;：没有 Leader，写入时客户端把数据发给 N 个副本中的任意 W 个，读的时候从 R 个里读，只要 &lt;strong&gt;W + R &amp;gt; N&lt;/strong&gt; 就能保证读到最新值（法定人数，quorum）。经典参数 N=3, W=2, R=2。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;经典案例：Amazon Dynamo 论文与购物车&lt;/strong&gt;。2007 年 Amazon 发表 Dynamo 论文，动机极其务实：&lt;strong&gt;购物车绝不能拒绝写入&lt;/strong&gt;——用户加购物车被拒，就是真金白银的损失。所以 Dynamo 选择永远可写（AP），哪怕网络分区时也接受写入，事后再合并冲突版本（著名的&amp;quot;向量时钟&amp;quot;就出自这篇论文）。这篇论文直接催生了 Riak、Cassandra、Voldemort 一整代 NoSQL。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;一句话理解&lt;/strong&gt;：单主是&amp;quot;独裁&amp;quot;，多主是&amp;quot;联邦&amp;quot;，无主是&amp;quot;全民投票&amp;quot;。越民主写入越自由，但事后摆平分歧的成本越高。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;6-分区数据切蛋糕的两种刀法&#34;&gt;6. 分区：数据切蛋糕的两种刀法&lt;/h3&gt;
&lt;p&gt;单机存不下就分区（分片）。两种切法：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;按 key 范围分区&lt;/strong&gt;：字典式切分，A&lt;del&gt;M 一个分区、N&lt;/del&gt;Z 一个分区。范围查询（&amp;ldquo;查 7 月的所有订单&amp;rdquo;）天然高效，但&lt;strong&gt;热点&lt;/strong&gt;问题明显——如果 key 是时间戳，所有新写入全砸在最后一个分区上。Bigtable、HBase 用这套。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;按键的哈希分区&lt;/strong&gt;：key 过一遍哈希函数打散。负载均匀，但范围查询要 scatter-gather 到所有分区，效率骤降。Cassandra、DynamoDB 用这套（Cassandra 实际上是&amp;quot;哈希定分区 + 分区内按 key 排序&amp;quot;的混合体）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;再平衡（rebalancing）&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 扩容前[&amp;#34;扩容前：3 节点，每节点 4 个虚拟分区&amp;#34;]
        N1[&amp;#34;节点 A&amp;lt;br/&amp;gt;v0 v1 v2 v3&amp;#34;]
        N2[&amp;#34;节点 B&amp;lt;br/&amp;gt;v4 v5 v6 v7&amp;#34;]
        N3[&amp;#34;节点 C&amp;lt;br/&amp;gt;v8 v9 v10 v11&amp;#34;]
    end
    subgraph 扩容后[&amp;#34;扩容后：新节点 D 加入&amp;lt;br/&amp;gt;只搬走 3 个虚拟分区，其余不动&amp;#34;]
        N1b[&amp;#34;节点 A&amp;lt;br/&amp;gt;v0 v1 v2&amp;#34;]
        N2b[&amp;#34;节点 B&amp;lt;br/&amp;gt;v4 v5 v6&amp;#34;]
        N3b[&amp;#34;节点 C&amp;lt;br/&amp;gt;v8 v9 v10&amp;#34;]
        N4[&amp;#34;节点 D&amp;lt;br/&amp;gt;v3 v7 v11&amp;lt;br/&amp;gt;（从 A/B/C 各搬一个）&amp;#34;]
    end
    扩容前 --&amp;gt;|&amp;#34;固定数量虚拟分区策略&amp;lt;br/&amp;gt;只迁移少量 vNode&amp;#34;| 扩容后&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;主流策略就是图里这个：&lt;strong&gt;固定数量的虚拟分区&lt;/strong&gt;（vNode）——分区数远多于节点数，加节点时从每个老节点&amp;quot;匀&amp;quot;几个虚拟分区过来。Riak、Cassandra 都这么干。反例是取模哈希（hash mod N）：节点数一变，几乎全部 key 都要搬家，这就是一致性哈希要解决的原始问题。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;避坑指南 2&lt;/strong&gt;：分区键一旦选定，几乎不可更改。用 user_id 做分区键的系统，后来想做&amp;quot;按时间范围全局扫描&amp;quot;，会发现每个查询都要打满全部分区。选分区键时想清楚：&lt;strong&gt;你的主要查询模式是什么，热点在哪&lt;/strong&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;7-派生数据一个数据系统的输出是另一个系统的输入&#34;&gt;7. 派生数据：一个数据系统的输出，是另一个系统的输入&lt;/h3&gt;
&lt;p&gt;Kleppmann 有个非常锋利的视角：&lt;strong&gt;缓存、搜索索引、数据仓库，本质上都是派生数据&lt;/strong&gt;——它们都是从某个&amp;quot;真相源&amp;quot;（source of truth）加工出来的冗余副本。&lt;/p&gt;
&lt;p&gt;一旦接受这个设定，很多架构问题豁然开朗：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;缓存和数据库不一致？因为派生数据没有和源数据同步更新。&lt;/li&gt;
&lt;li&gt;ES 里的搜索结果和 MySQL 对不上？同理。&lt;/li&gt;
&lt;li&gt;数仓报表延迟一天？因为派生管道是 T+1 的批处理。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;于是问题从&amp;quot;怎么让缓存不失效&amp;quot;变成&amp;quot;&lt;strong&gt;派生管道怎么设计&lt;/strong&gt;&amp;quot;，这正是流处理要回答的，下文第 10 节展开。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;第三部分-回路系统怎么自我纠错&#34;&gt;第三部分 回路：系统怎么自我纠错&lt;/h2&gt;
&lt;p&gt;存量和流量解决了&amp;quot;存得下、流得动&amp;quot;，剩下的问题是：&lt;strong&gt;当多个客户端并发操作、机器会宕机、网络会分区时，系统凭什么保证数据不出错？&lt;/strong&gt; 这就是回路——一致性保障与反馈机制。&lt;/p&gt;
&lt;h3 id=&#34;8-事务与隔离级别用弱隔离换性能代价是异常&#34;&gt;8. 事务与隔离级别：用&amp;quot;弱隔离&amp;quot;换性能，代价是异常&lt;/h3&gt;
&lt;p&gt;事务（ACID）是数据库给程序员的最大善意：&lt;strong&gt;把一堆操作打包成原子单元，要么全成要么全不成，并发的麻烦我来扛&lt;/strong&gt;。但&amp;quot;我来扛&amp;quot;是有价格的——严格串行执行性能太差，于是数据库发明了隔离级别这个&amp;quot;滑杆&amp;quot;，让你自己选扛多少：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;隔离级别&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;防住的异常&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;防不住的异常&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;谁在用&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;读未提交&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;几乎啥都不防&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;脏读、一切&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;基本没人用&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;读提交（RC）&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;脏读、脏写&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;不可重复读、写倾斜&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;Oracle、PostgreSQL 的默认&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;快照隔离（SI）&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;上面所有 + 不可重复读&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;写倾斜&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;PostgreSQL 的 RR、MySQL InnoDB 的 RR&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;串行化&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;一切&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;无&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;性能最好时的梦想&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;快照隔离（Snapshot Isolation）&lt;/strong&gt; 值得单独说，因为它是现代数据库的主力。原理是 MVCC（多版本并发控制）：每次写入不覆盖旧值，而是生成新版本，每个事务看到&amp;quot;它开始那一刻&amp;quot;的数据快照。读不阻塞写、写不阻塞读，性能极佳。InnoDB 的 undo log、PostgreSQL 的多版本堆表，都是 MVCC 实现。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;写倾斜（write skew）是快照隔离最大的盲区&lt;/strong&gt;，也是面试和实战最容易踩的坑。小例子：医院排班系统要求&amp;quot;至少有一名医生值班&amp;quot;，当前有 Alice 和 Bob 两人值班。两人同时点&amp;quot;请假&amp;quot;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Alice 的事务读到&amp;quot;2 人值班&amp;quot;，判断&amp;quot;我请假后还剩 1 人，OK&amp;quot;，提交；&lt;/li&gt;
&lt;li&gt;Bob 的事务同样读到&amp;quot;2 人值班&amp;quot;（快照！他看不到 Alice 未提交的修改），同样判断 OK，提交；&lt;/li&gt;
&lt;li&gt;结果：两人都不值班了，约束被破坏。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;注意这不是脏读也不是不可重复读——每个事务内部看到的数据完全一致，但&lt;strong&gt;两个事务基于同一份过期前提做出了互相冲突的决定&lt;/strong&gt;。解法只有：升级到串行化，或者用 &lt;code&gt;SELECT ... FOR UPDATE&lt;/code&gt; 物化冲突（把隐含的读前提变成显式行锁）。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;避坑指南 3&lt;/strong&gt;：凡是有&amp;quot;读取一个约束条件，然后据此决定写入&amp;quot;模式的代码（库存扣减、名额抢占、余额校验），在 RR/快照隔离下都可能写倾斜。别指望 ORM 救你，老老实实 &lt;code&gt;FOR UPDATE&lt;/code&gt; 或上串行化。&lt;/p&gt;
&lt;/blockquote&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;：单线程跑事务（Redis 单线程模型、VoltDB），简单暴力，内存够快时性能惊人；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;两阶段锁（2PL）&lt;/strong&gt;：读写都加锁，性能差但正确，MySQL 的串行化级别就是这么实现的，也是死锁的主要来源；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可串行化快照隔离（SSI）&lt;/strong&gt;：快照隔离之上检测危险读写依赖，有冲突就中止重试。PostgreSQL 9.1+ 的串行化级别用的就是它，是近几十年事务领域最重要的学术成果落地。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;9-共识与线性一致性分布式系统的终极回路&#34;&gt;9. 共识与线性一致性：分布式系统的&amp;quot;终极回路&amp;quot;&lt;/h3&gt;
&lt;p&gt;事务解决的是&amp;quot;单机/单系统内的并发正确性&amp;quot;，共识（consensus）解决的是更难的问题：&lt;strong&gt;一群会宕机、消息会延迟的机器，怎么对&amp;quot;某件事&amp;quot;达成一致&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;先纠正一个常见混淆：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;线性一致性（Linearizability）&lt;/strong&gt;：让系统表现得&amp;quot;像只有一份数据&amp;quot;——任何人读到 x=1 之后，不可能再读到 x=0。它是对&lt;strong&gt;单个对象&lt;/strong&gt;的读写一致性承诺。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;共识（Consensus）&lt;/strong&gt;：多个节点对&lt;strong&gt;某个值/某条决策&lt;/strong&gt;达成不可逆的一致，典型算法 Paxos、Raft、Zab。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;两者的关系：要实现线性一致的存储，背后几乎必然要靠共识算法选主或定序。ZooKeeper（Zab）、etcd（Raft）就是共识系统的工业代表，整个 Kubernetes 的正确性底座就是 etcd 里的那套 Raft。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;经典案例：Google Spanner 与 TrueTime&lt;/strong&gt;。Spanner 要在全球范围提供&lt;strong&gt;外部一致性&lt;/strong&gt;（比线性一致更强，跨事务的全局顺序），最大的拦路虎是：分布式系统里各机器时钟不可靠，你没法靠时间戳判断两个事务谁先谁后。Google 的解法粗暴而硬核——&lt;strong&gt;给数据中心装 GPS 接收机和原子钟&lt;/strong&gt;，把时钟误差压缩到一个已知的小区间（TrueTime API 返回的不是一个时间点，而是&amp;quot;真实时间必然落在的这个区间&amp;quot;）。提交事务时，如果发现误差区间还没过去，就&lt;strong&gt;故意等待&lt;/strong&gt;（commit wait）直到确定。用硬件换确定性，用等待换正确性——这是&amp;quot;回路强度可以花钱买&amp;quot;的最著名示范。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;FLP 不可能定理&lt;/strong&gt;泼一盆冷水：在纯异步网络里，哪怕只有一台机器可能崩溃，也不存在&lt;strong&gt;确定性&lt;/strong&gt;的、保证能终止的共识算法。工程上的出路是引入&amp;quot;部分同步&amp;quot;假设（网络总会恢复）和超时机制——所以 Raft/Paxos 的正确性不依赖时钟，但&lt;strong&gt;活性&lt;/strong&gt;（liveness，能不能往前走）依赖。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;极端案例：比特币与拜占庭容错&lt;/strong&gt;。上面说的共识算法都假设节点&amp;quot;可能宕机但不会撒谎&amp;quot;（崩溃容错 CFT）。比特币面对的是更恶劣的环境：&lt;strong&gt;节点可能作恶&lt;/strong&gt;（拜占庭将军问题）。中本聪的解法是放弃确定性共识，改用工作量证明（PoW）+ 最长链规则做&lt;strong&gt;概率性共识&lt;/strong&gt;——一笔交易等 6 个区块确认，被推翻的概率指数级下降但永不为零。性能极差（比特币全网约 7 TPS，对比 Visa 峰值数万 TPS），换来的是&amp;quot;无许可环境下没人能篡改历史&amp;quot;。这是把回路强度推到极限、把流量吞吐牺牲殆尽的极端案例，也完美印证了我们的框架：&lt;strong&gt;一切都是权衡&lt;/strong&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;共识落地前，还有个更现实的问题：故障检测&lt;/strong&gt;。分布式系统判断&amp;quot;某节点死了&amp;quot;的唯一办法是超时——但超时无法区分&amp;quot;节点真死了&amp;quot;和&amp;quot;节点只是 GC 停顿了 30 秒&amp;quot;（Java 应用的 Full GC、虚拟机的 steal time、网络拥塞都会造成这种&amp;quot;假死&amp;quot;）。于是就有了&lt;strong&gt;脑裂（split-brain）&lt;/strong&gt;：老 Leader 以为自己还是 Leader，新 Leader 已经被选出来，两个&amp;quot;主&amp;quot;同时接受写入，数据互相覆盖。&lt;/p&gt;
&lt;p&gt;工程解法是 &lt;strong&gt;fencing token（防护令牌）&lt;/strong&gt;：每次选主，共识系统发一个单调递增的编号，存储层只接受&amp;quot;编号比上次大&amp;quot;的写入。老 Leader 拿着旧编号来写，直接被拒。这个小机制在 ZooKeeper（zxid）、etcd（revision）里都有对应物。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;业界掌故&lt;/strong&gt;：Kleppmann 本人就是分布式锁话题的&amp;quot;圈内名人&amp;quot;——2016 年他发文《How to do distributed locking》批判 Redis 的 Redlock 算法在时钟跳变和进程暂停下不安全，引来 Redis 作者 antirez 的公开反驳，两人来回论战了好几轮，成为分布式领域最著名的公开辩论之一。争论的核心正是上面这些：时钟不可信、暂停不可测，所以任何分布式锁要么上 fencing token，要么干脆用共识系统。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;10-批处理-vs-流处理同一枚硬币的两面&#34;&gt;10. 批处理 vs 流处理：同一枚硬币的两面&lt;/h3&gt;
&lt;p&gt;最后回到流量，看数据处理的两种世界观：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;批处理&lt;/strong&gt;：数据是&lt;strong&gt;有界的&lt;/strong&gt;——一堆文件摆在那，MapReduce/Spark 跑一遍，输出结果。优点是吞吐高、可重跑、容错简单（任务挂了重跑）；缺点是延迟以小时计。Hadoop/MapReduce 的黄金时代（Google 2004 年论文、Yahoo 孵化 Hadoop）就是这套世界观。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;流处理&lt;/strong&gt;：数据是&lt;strong&gt;无界的&lt;/strong&gt;——事件源源不断地来，Kafka + Flink/Storm 逐条处理，延迟毫秒级。难点：乱序、恰好一次语义（exactly-once）、容错状态恢复。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Lambda 架构&lt;/strong&gt;：流处理快但不准（可能丢、可能重复），批处理准但慢，那就两条都跑——流处理给实时近似结果，批处理定期用精确结果覆盖。维护两套系统、两份代码，运维成本翻倍，是出名的&amp;quot;说起来美、做起来痛&amp;quot;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Kappa 架构&lt;/strong&gt;：LinkedIn 的 Jay Kreps（Kafka 作者）提出的简化——&lt;strong&gt;只要流处理一条腿&lt;/strong&gt;。历史数据重算？把 Kafka 的日志保留期拉长，让流处理程序从头重放一遍就行。这个架构能成立的前提，是 Kafka 把&amp;quot;消息队列&amp;quot;做成了&amp;quot;可重放的日志存储&amp;quot;。&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 数据源
        DB[(&amp;#34;业务数据库&amp;lt;br/&amp;gt;MySQL&amp;#34;)]
        APP[&amp;#34;应用事件&amp;#34;]
    end
    DB --&amp;gt;|&amp;#34;CDC 变更数据捕获&amp;lt;br/&amp;gt;（Debezium 监听 binlog）&amp;#34;| K[&amp;#34;Kafka&amp;lt;br/&amp;gt;（可重放的日志存储）&amp;#34;]
    APP --&amp;gt; K
    K --&amp;gt; S[&amp;#34;流处理引擎&amp;lt;br/&amp;gt;Flink / Kafka Streams&amp;#34;]
    S --&amp;gt; V1[&amp;#34;派生视图：缓存 Redis&amp;#34;]
    S --&amp;gt; V2[&amp;#34;派生视图：搜索索引 ES&amp;#34;]
    S --&amp;gt; V3[&amp;#34;派生视图：数仓 ClickHouse&amp;#34;]
    K --&amp;gt;|&amp;#34;日志长期保留&amp;lt;br/&amp;gt;需要重算时从头重放&amp;#34;| S
    subgraph KP[&amp;#34;Kappa / 批流一体&amp;#34;]
        S
    end&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;这张图里藏着 DDIA 全书最重要的思想之一：&lt;strong&gt;数据库和消息队列正在融合&lt;/strong&gt;。Kafka 是可以当数据库用的日志（有保留期、可重放、有流处理）；反过来，数据库的 binlog/WAL 通过 CDC（Change Data Capture）技术流出来，就变成了消息流。Debezium 监听 MySQL binlog 把每一次行变更变成 Kafka 消息，是当下&amp;quot;派生数据管道&amp;quot;的标准做法。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;经典案例：Kafka 从 LinkedIn 内部管道到行业标准&lt;/strong&gt;。Kafka 诞生时（2010 年前后）只是 LinkedIn 内部的一个活动流（activity stream）和日志管道，目标是扛住&amp;quot;每个用户的每个页面浏览都要记录&amp;quot;的巨大流量。它的关键设计——把消息组织成&lt;strong&gt;分区有序日志&lt;/strong&gt;、消费者自己维护 offset、依赖磁盘顺序写+page cache 而非内存——让它意外地发现：这个东西不只是消息队列，而是&amp;quot;以日志为中心的存储系统&amp;quot;。Confluent 成立后，Kafka 成了流处理事实标准，Kreps 那篇《The Log: What every software engineer should know about real-time data&amp;rsquo;s unifying abstraction》（2013）把&amp;quot;日志是数据系统统一抽象&amp;quot;的思想讲透了——和 DDIA 第 3 章&amp;quot;日志结构存储&amp;quot;一脉相承。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;11-全书串讲用三个词复盘四个案例&#34;&gt;11. 全书串讲：用三个词复盘四个案例&lt;/h2&gt;
&lt;p&gt;现在用&amp;quot;存量、流量、回路&amp;quot;的框架，把本文案例快速过一遍，你会发现它们只是同一个框架的不同参数组合：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Kafka&lt;/strong&gt;：存量用顺序日志（LSM 的近亲），流量靠分区+副本，回路选择&amp;quot;至少一次/恰好一次可配&amp;quot;的务实路线。→ 流量优先，回路可调。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dynamo&lt;/strong&gt;：无主复制+永远可写，回路是最终一致+应用层冲突解决。→ 流量（可用性）压倒一切，回路最弱。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Spanner&lt;/strong&gt;：全球分区+同步复制，回路用 TrueTime 堆到最强（外部一致性）。→ 回路优先，花钱买。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;比特币&lt;/strong&gt;：回路要求拜占庭容错（最强敌对环境），代价是吞吐 7 TPS。→ 回路极限，流量牺牲殆尽。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;没有银弹，只有取舍。&lt;/strong&gt; 这就是 Kleppmann 反复想说、而无数&amp;quot;中间件选型指南&amp;quot;不会告诉你的事。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;12-带走的清单&#34;&gt;12. 带走的清单&lt;/h2&gt;
&lt;p&gt;读完 DDIA，做系统设计时问自己这几句话：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;存量三问&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;写入多还是读取多？写多读少看 LSM（RocksDB/Cassandra），读多写少看 B-Tree（MySQL），分析查询上列存（ClickHouse/Parquet）。&lt;/li&gt;
&lt;li&gt;崩溃恢复靠什么？找到那个 WAL——它一定存在，可能只是叫别的名字（redo log / AOF / binlog / journal）。&lt;/li&gt;
&lt;li&gt;schema 演进支持双向兼容吗？向前向后都要，滚动升级时缺一不可。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;流量三问&lt;/strong&gt;&lt;/p&gt;
&lt;ol start=&#34;4&#34;&gt;
&lt;li&gt;复制延迟用户能感知吗？能，就按需要补读己之写、单调读、一致前缀读，别等用户来报 bug。&lt;/li&gt;
&lt;li&gt;分区键选对了吗？对照主要查询模式和热点，选错分区键≈架构重来。&lt;/li&gt;
&lt;li&gt;缓存/索引/数仓是不是派生数据？是，就把&amp;quot;同步管道&amp;quot;当成一等公民来设计（CDC + 流处理），而不是事后打补丁。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;回路三问&lt;/strong&gt;&lt;/p&gt;
&lt;ol start=&#34;7&#34;&gt;
&lt;li&gt;业务能容忍什么级别的一致性？能用最终一致就别上线性一致，省下的都是真金白银。&lt;/li&gt;
&lt;li&gt;有没有&amp;quot;读约束再写入&amp;quot;的代码？有，检查写倾斜——&lt;code&gt;FOR UPDATE&lt;/code&gt; 或串行化，二选一。&lt;/li&gt;
&lt;li&gt;选主/分布式锁靠什么？别自己造轮子，ZooKeeper/etcd 这类共识系统才是正解（Redis 分布式锁的争议——Martin Kleppmann 本人就是那篇著名批判文章《How to do distributed locking》的作者，还设计了&amp;quot;马丁小花&amp;quot;fencing token 方案）。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;最后的避坑清单&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;别把默认隔离级别当成&amp;quot;安全级别&amp;quot;——PostgreSQL/MySQL 的默认都不是串行化，写倾斜在看着你。&lt;/li&gt;
&lt;li&gt;别迷信&amp;quot;分布式 = 高可用&amp;quot;——复制和分区引入的故障模式（脑裂、时钟漂移、复制滞后）比单机复杂一个数量级，Jepsen 测试报告（Kyle Kingsbury 对各大数据库的&amp;quot;虐测&amp;quot;）里全是血泪。&lt;/li&gt;
&lt;li&gt;别在不了解权衡时引用 CAP——CAP 只说&amp;quot;网络分区时一致性和可用性二选一&amp;quot;，而网络不分区时你还有延迟与一致性的权衡（PACELC 才是完整版）。&lt;/li&gt;
&lt;li&gt;时间不可信：进程会 GC 暂停、线程会被调度、时钟会跳变。所有依赖&amp;quot;本地时钟先后关系&amp;quot;的分布式逻辑都值得怀疑——这正是 Spanner 要装原子钟的原因。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;13-结语野猪书为什么是神书&#34;&gt;13. 结语：野猪书为什么是神书&lt;/h2&gt;
&lt;p&gt;市面上讲分布式的书，要么讲理论（一堆证明，读完不会写代码），要么讲产品（一堆配置，换个组件就过时）。DDIA 恰好卡在中间：&lt;strong&gt;它讲的是&amp;quot;不会过时的权衡&amp;quot;&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;LSM 和 B-Tree 的取舍，二十年前成立，二十年后还会成立；复制滞后引发的那些&amp;quot;诡异现象&amp;quot;，今天在 MySQL 从库上出现，明天就会在你的新数据库上换个马甲出现。工具会死，权衡永存。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;顺带给个阅读路径建议&lt;/strong&gt;：这本书 12 章不必按顺序啃——&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;日常写业务代码的：先读第 3 章（存储引擎）、第 7 章（事务），性价比最高；&lt;/li&gt;
&lt;li&gt;做中间件/基础架构的：第 5、6 章（复制、分区）和第 9 章（共识）是主菜；&lt;/li&gt;
&lt;li&gt;做数据平台/实时数仓的：第 10、11 章（批处理、流处理）直接对口；&lt;/li&gt;
&lt;li&gt;第 8 章（分布式系统的麻烦）建议所有人都读，它是全书&amp;quot;劝退幻想&amp;quot;的一章，读完你会对时钟和网络产生健康的怀疑。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;用一句话收尾，也是这本书给我最大的思维升级：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;不要问&amp;quot;这个系统支持什么特性&amp;quot;，要问&amp;quot;这个系统在存量、流量、回路上各做了什么取舍，这些取舍和我的业务匹配吗&amp;quot;。&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;能问出这个问题，这本书就没白读。&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>
