<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>InnoDB on 吾爱主机</title><link>https://blog.waihost.com/tags/innodb/</link><description>Recent content in InnoDB on 吾爱主机</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Tue, 14 Jul 2026 00:00:00 +0800</lastBuildDate><atom:link href="https://blog.waihost.com/tags/innodb/index.xml" rel="self" type="application/rss+xml"/><item><title>MySQL InnoDB 事务隔离级别与 MVCC：原理、可见性与工程实践</title><link>https://blog.waihost.com/posts/mysql-innodb-mvcc-isolation-levels/</link><pubDate>Tue, 14 Jul 2026 00:00:00 +0800</pubDate><guid>https://blog.waihost.com/posts/mysql-innodb-mvcc-isolation-levels/</guid><description>&lt;img src="https://blog.waihost.com/images/covers/mysql-innodb-mvcc-isolation-levels.svg" alt="Featured image of post MySQL InnoDB 事务隔离级别与 MVCC：原理、可见性与工程实践" /&gt;&lt;p&gt;在业务代码里，很多人把「开事务」当成理所当然：&lt;code&gt;BEGIN&lt;/code&gt; → 几条 SQL → &lt;code&gt;COMMIT&lt;/code&gt;。线上一旦出现脏数据、重复扣减、偶发死锁，第一反应却是「加锁」或「把隔离级别调高」。其实 InnoDB 的默认行为已经是一套完整的 &lt;strong&gt;多版本并发控制（MVCC）+ 行级锁&lt;/strong&gt; 组合；真正关键的是：读走快照还是走加锁读、当前隔离级别如何决定可见性与间隙锁。&lt;/p&gt;
&lt;p&gt;本文基于 MySQL 官方手册中的隔离级别、多版本、一致性非锁定读、间隙锁/next-key 与死锁说明，梳理可复用的原理与工程实践，而不是版本变更清单。&lt;/p&gt;
&lt;h2 id="问题背景并发事务到底在打架什么"&gt;&lt;a href="#%e9%97%ae%e9%a2%98%e8%83%8c%e6%99%af%e5%b9%b6%e5%8f%91%e4%ba%8b%e5%8a%a1%e5%88%b0%e5%ba%95%e5%9c%a8%e6%89%93%e6%9e%b6%e4%bb%80%e4%b9%88" class="header-anchor"&gt;&lt;/a&gt;问题背景：并发事务到底在“打架”什么
&lt;/h2&gt;&lt;p&gt;SQL 标准用隔离级别描述并发下的异常现象（脏读、不可重复读、幻读）。InnoDB 实现了标准里的四级隔离：&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;InnoDB 要点（官方行为）&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;READ UNCOMMITTED&lt;/td&gt;
					&lt;td&gt;脏读&lt;/td&gt;
					&lt;td&gt;普通 &lt;code&gt;SELECT&lt;/code&gt; 可能读到「更早版本」的行，读本身不一致&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;READ COMMITTED&lt;/td&gt;
					&lt;td&gt;不可重复读、幻读&lt;/td&gt;
					&lt;td&gt;每次一致性读使用&lt;strong&gt;新快照&lt;/strong&gt;；锁定读通常只锁记录、不锁间隙&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;REPEATABLE READ（默认）&lt;/td&gt;
					&lt;td&gt;幻读在加锁读路径上被抑制&lt;/td&gt;
					&lt;td&gt;事务内普通一致性读复用&lt;strong&gt;首次读建立的快照&lt;/strong&gt;；范围锁定读使用 gap/next-key&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;SERIALIZABLE&lt;/td&gt;
					&lt;td&gt;最强串行语义&lt;/td&gt;
					&lt;td&gt;在 &lt;code&gt;autocommit&lt;/code&gt; 关闭时，普通 &lt;code&gt;SELECT&lt;/code&gt; 隐式变为 &lt;code&gt;SELECT ... FOR SHARE&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;默认是 &lt;strong&gt;REPEATABLE READ&lt;/strong&gt;。很多团队在迁移自 Oracle 等库时会改成 READ COMMITTED，这会显著改变锁与幻读行为——必须按业务场景评估，而不是「听说更像 Oracle 就改」。&lt;/p&gt;
&lt;h2 id="核心原理mvcc-如何让读不挡写"&gt;&lt;a href="#%e6%a0%b8%e5%bf%83%e5%8e%9f%e7%90%86mvcc-%e5%a6%82%e4%bd%95%e8%ae%a9%e8%af%bb%e4%b8%8d%e6%8c%a1%e5%86%99" class="header-anchor"&gt;&lt;/a&gt;核心原理：MVCC 如何让读不挡写
&lt;/h2&gt;&lt;h3 id="行上的隐藏字段与-undo"&gt;&lt;a href="#%e8%a1%8c%e4%b8%8a%e7%9a%84%e9%9a%90%e8%97%8f%e5%ad%97%e6%ae%b5%e4%b8%8e-undo" class="header-anchor"&gt;&lt;/a&gt;行上的隐藏字段与 undo
&lt;/h3&gt;&lt;p&gt;InnoDB 是多版本存储引擎：被修改行的旧版本信息保存在 &lt;strong&gt;undo 表空间 / 回滚段&lt;/strong&gt; 中，用于：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;事务回滚时做 undo&lt;/li&gt;
&lt;li&gt;一致性读时构造「更早版本的行」&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;官方文档指出，InnoDB 会在每行内部维护（概念上）这些字段：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;DB_TRX_ID&lt;/strong&gt;（6 字节）：最近一次插入或更新该行的事务 ID；删除在内部也视作更新，并打上删除标记&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DB_ROLL_PTR&lt;/strong&gt;（7 字节）：回滚指针，指向 undo log，从而能重建更新前的行内容&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DB_ROW_ID&lt;/strong&gt;（6 字节）：单调递增的行 ID；仅在没有合适主键、需要自动生成聚簇索引时进入索引结构&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;undo 又分为 insert undo 与 update undo：insert undo 在事务提交后即可丢弃；update undo 还要服务一致性读，只有当不再存在「可能需要该历史版本」的读视图事务时，才能清理。这也是「长事务拖垮 undo / 历史版本膨胀」的根因。&lt;/p&gt;
&lt;h3 id="一致性非锁定读consistent-nonlocking-read"&gt;&lt;a href="#%e4%b8%80%e8%87%b4%e6%80%a7%e9%9d%9e%e9%94%81%e5%ae%9a%e8%af%bbconsistent-nonlocking-read" class="header-anchor"&gt;&lt;/a&gt;一致性非锁定读（Consistent Nonlocking Read）
&lt;/h3&gt;&lt;p&gt;在 READ COMMITTED 与 REPEATABLE READ 下，普通 &lt;code&gt;SELECT&lt;/code&gt; 默认走 &lt;strong&gt;一致性读&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;查询看到某个时间点之前已提交事务所做的修改&lt;/li&gt;
&lt;li&gt;看不到之后提交或未提交事务的修改&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;例外&lt;/strong&gt;：同一事务内更早语句自己写过的变更，当前事务能看见&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;一致性读 &lt;strong&gt;不在表上加读锁&lt;/strong&gt;，因此其他会话可以同时改这些表——这就是 MVCC 带来的读写并发。&lt;/p&gt;
&lt;p&gt;快照时机是理解 RR / RC 差异的关键：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;REPEATABLE READ&lt;/strong&gt;：事务内第一次一致性读建立快照，后续普通 &lt;code&gt;SELECT&lt;/code&gt; 复用同一快照，彼此一致&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;READ COMMITTED&lt;/strong&gt;：事务内每一次一致性读都建立并读取&lt;strong&gt;新的&lt;/strong&gt;快照&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;因此在 RR 下，「同一事务里两次 &lt;code&gt;SELECT&lt;/code&gt; 结果一样」是默认语义；在 RC 下，「读到别人已提交的新数据」是默认语义。&lt;/p&gt;
&lt;p&gt;官方还特别提醒：快照主要作用于事务内的 &lt;code&gt;SELECT&lt;/code&gt;，&lt;strong&gt;不一定&lt;/strong&gt;约束 &lt;code&gt;UPDATE&lt;/code&gt;/&lt;code&gt;DELETE&lt;/code&gt; 的目标行集合。在 RR 中可能出现：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 会话 A：REPEATABLE READ
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;xyz&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;-- 0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;DELETE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;xyz&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;-- 却可能删掉其他事务刚提交的多行
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;xyz&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;-- 仍可能是 0（普通 SELECT 仍看旧快照）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;也就是说：&lt;strong&gt;写路径看到的是“当前最新可提交状态 + 锁”&lt;/strong&gt;，读路径在 RR 下仍可能停在旧快照。混用「普通 SELECT 决策 + DML」时，要意识到二者语义不同；需要当前读时用 &lt;code&gt;SELECT ... FOR UPDATE&lt;/code&gt; / &lt;code&gt;FOR SHARE&lt;/code&gt;。&lt;/p&gt;
&lt;h3 id="加锁读与间隙幻读如何被挡住"&gt;&lt;a href="#%e5%8a%a0%e9%94%81%e8%af%bb%e4%b8%8e%e9%97%b4%e9%9a%99%e5%b9%bb%e8%af%bb%e5%a6%82%e4%bd%95%e8%a2%ab%e6%8c%a1%e4%bd%8f" class="header-anchor"&gt;&lt;/a&gt;加锁读与间隙：幻读如何被挡住
&lt;/h3&gt;&lt;p&gt;普通 &lt;code&gt;SELECT&lt;/code&gt; 是快照读；下列语句是 &lt;strong&gt;locking read / 当前读&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;SELECT ... FOR UPDATE&lt;/code&gt; / &lt;code&gt;SELECT ... FOR SHARE&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UPDATE&lt;/code&gt; / &lt;code&gt;DELETE&lt;/code&gt;（以及会读取并锁定目标行的 DML）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在 &lt;strong&gt;REPEATABLE READ&lt;/strong&gt; 下（官方）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;唯一索引 + &lt;strong&gt;唯一等值&lt;/strong&gt;条件：通常只锁住找到的索引记录，&lt;strong&gt;不锁前面的 gap&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;其他搜索条件（范围、非唯一等）：扫描范围内使用 &lt;strong&gt;gap lock&lt;/strong&gt; 或 &lt;strong&gt;next-key lock&lt;/strong&gt;，阻止其他会话向间隙中插入&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Next-key lock&lt;/strong&gt; = 索引记录锁 + 该记录之前间隙上的 gap lock。官方用幻读例子说明：若只锁已有记录、不锁间隙，其他事务可在范围中间插入新行，导致同一事务再次查询出现「幻影行」。InnoDB 通过 next-key 算法抑制这类插入。&lt;/p&gt;
&lt;p&gt;在 &lt;strong&gt;READ COMMITTED&lt;/strong&gt; 下（官方）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;锁定读一般只锁 &lt;strong&gt;index record&lt;/strong&gt;，不锁 gap，因此允许在已锁记录旁插入新行&lt;/li&gt;
&lt;li&gt;gap 锁主要用于外键检查与重复键检查&lt;/li&gt;
&lt;li&gt;因为 gap 锁关闭，&lt;strong&gt;可能出现幻读&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;另有工程意义：&lt;code&gt;UPDATE&lt;/code&gt;/&lt;code&gt;DELETE&lt;/code&gt; 对 &lt;strong&gt;不匹配 WHERE 的行&lt;/strong&gt; 会在评估后释放锁，死锁概率通常更低；但死锁仍可能发生&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="serializable-不是神秘黑盒"&gt;&lt;a href="#serializable-%e4%b8%8d%e6%98%af%e7%a5%9e%e7%a7%98%e9%bb%91%e7%9b%92" class="header-anchor"&gt;&lt;/a&gt;SERIALIZABLE 不是「神秘黑盒」
&lt;/h3&gt;&lt;p&gt;官方定义很明确：SERIALIZABLE 类似 REPEATABLE READ，但当 &lt;strong&gt;autocommit 关闭&lt;/strong&gt; 时，InnoDB 会把普通 &lt;code&gt;SELECT&lt;/code&gt; &lt;strong&gt;隐式转换成&lt;/strong&gt; &lt;code&gt;SELECT ... FOR SHARE&lt;/code&gt;。若 autocommit 开启，单条 &lt;code&gt;SELECT&lt;/code&gt; 本身就是只读事务，可走一致性非锁定读而不必阻塞。&lt;/p&gt;
&lt;p&gt;因此：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;想用 SERIALIZABLE 做「读也要挡住别人改」：确保在显式事务中（关闭 autocommit）&lt;/li&gt;
&lt;li&gt;它会显著增加锁竞争，只适合 XA、强一致性专项场景，不适合默认全库开启&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="实践如何查看设置与正确选用"&gt;&lt;a href="#%e5%ae%9e%e8%b7%b5%e5%a6%82%e4%bd%95%e6%9f%a5%e7%9c%8b%e8%ae%be%e7%bd%ae%e4%b8%8e%e6%ad%a3%e7%a1%ae%e9%80%89%e7%94%a8" class="header-anchor"&gt;&lt;/a&gt;实践：如何查看、设置与正确选用
&lt;/h2&gt;&lt;h3 id="查看与设置隔离级别"&gt;&lt;a href="#%e6%9f%a5%e7%9c%8b%e4%b8%8e%e8%ae%be%e7%bd%ae%e9%9a%94%e7%a6%bb%e7%ba%a7%e5%88%ab" class="header-anchor"&gt;&lt;/a&gt;查看与设置隔离级别
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 查看（MySQL 8 推荐系统变量）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;@@&lt;/span&gt;&lt;span class="k"&gt;GLOBAL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transaction_isolation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;@@&lt;/span&gt;&lt;span class="k"&gt;SESSION&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transaction_isolation&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 仅影响下一笔未开始的事务
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TRANSACTION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ISOLATION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;LEVEL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;READ&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;COMMITTED&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 影响当前会话后续事务
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;SESSION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TRANSACTION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ISOLATION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;LEVEL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;REPEATABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;READ&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 影响新连接的默认（需权限）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;GLOBAL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TRANSACTION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ISOLATION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;LEVEL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;READ&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;COMMITTED&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;也可在配置文件中设置服务端默认：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[mysqld]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;transaction-isolation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;READ-COMMITTED&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;（选项值使用连字符形式，如 &lt;code&gt;REPEATABLE-READ&lt;/code&gt;。）&lt;/p&gt;
&lt;p&gt;访问模式还可设为 &lt;code&gt;READ ONLY&lt;/code&gt; / &lt;code&gt;READ WRITE&lt;/code&gt;，只读事务有助于引擎做优化，适合报表会话。&lt;/p&gt;
&lt;h3 id="业务选型建议工程向"&gt;&lt;a href="#%e4%b8%9a%e5%8a%a1%e9%80%89%e5%9e%8b%e5%bb%ba%e8%ae%ae%e5%b7%a5%e7%a8%8b%e5%90%91" class="header-anchor"&gt;&lt;/a&gt;业务选型建议（工程向）
&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;默认保持 REPEATABLE READ&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;账户余额校验 + 扣减、库存预占、需要「事务内多次普通读一致」的流程更自然&lt;/li&gt;
&lt;li&gt;范围更新/删除配合索引时，要注意 gap/next-key 带来的插入阻塞&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;高并发、写多读多、能接受「读到最新已提交」时考虑 READ COMMITTED&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;常见于互联网业务减少间隙锁、降低死锁&lt;/li&gt;
&lt;li&gt;必须接受：同一事务内两次普通读可能不同；幻读可能发生&lt;/li&gt;
&lt;li&gt;官方要求 RC 下二进制日志实质走 &lt;strong&gt;row-based&lt;/strong&gt;（&lt;code&gt;MIXED&lt;/code&gt; 时会自动切 row）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;需要「读到的行在事务期间不被别人改」&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;不要只靠普通 &lt;code&gt;SELECT&lt;/code&gt; 的 RR 快照（别人仍可改，你只是看不见）&lt;/li&gt;
&lt;li&gt;使用 &lt;code&gt;SELECT ... FOR UPDATE&lt;/code&gt;（要改）或 &lt;code&gt;FOR SHARE&lt;/code&gt;（共享读）明确当前读&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;避免长事务&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;官方建议即使是只做一致性读的事务也要及时提交，否则 update undo 无法回收，回滚段膨胀&lt;/li&gt;
&lt;li&gt;长事务还会拉长锁持有时间，放大死锁与锁等待&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="可复现的小实验建议在测试库"&gt;&lt;a href="#%e5%8f%af%e5%a4%8d%e7%8e%b0%e7%9a%84%e5%b0%8f%e5%ae%9e%e9%aa%8c%e5%bb%ba%e8%ae%ae%e5%9c%a8%e6%b5%8b%e8%af%95%e5%ba%93" class="header-anchor"&gt;&lt;/a&gt;可复现的小实验（建议在测试库）
&lt;/h3&gt;&lt;p&gt;准备：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;INT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;PRIMARY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;INT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NOT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ENGINE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;InnoDB&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;INTO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;VALUES&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;实验 A：RR 下快照读&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 会话 1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;SESSION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TRANSACTION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ISOLATION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;LEVEL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;REPEATABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;READ&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;START&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TRANSACTION&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;-- 100
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 会话 2
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;UPDATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;SET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;COMMIT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 会话 1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;-- 仍为 100（复用首次快照）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;COMMIT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;-- 提交后新事务可见 90
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;实验 B：需要当前读时加锁&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;START&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TRANSACTION&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FOR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;UPDATE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;-- 当前读 + X 锁
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 业务校验 balance &amp;gt;= 10 后再扣减
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;UPDATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;SET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;COMMIT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;实验 C：范围条件与插入阻塞（RR）&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 会话 1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;START&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TRANSACTION&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FOR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;UPDATE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;-- 范围 + next-key/gap
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 会话 2
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;INTO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;VALUES&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;-- 往往会等待会话 1 提交
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;把 &lt;code&gt;FOR UPDATE&lt;/code&gt; 换成普通 &lt;code&gt;SELECT&lt;/code&gt; 时，会话 2 通常可以插入（快照读不加锁），这能直观区分「看不见」和「锁住」。&lt;/p&gt;
&lt;h2 id="常见坑与排查"&gt;&lt;a href="#%e5%b8%b8%e8%a7%81%e5%9d%91%e4%b8%8e%e6%8e%92%e6%9f%a5" class="header-anchor"&gt;&lt;/a&gt;常见坑与排查
&lt;/h2&gt;&lt;h3 id="1-用普通-select-做决策却期望互斥"&gt;&lt;a href="#1-%e7%94%a8%e6%99%ae%e9%80%9a-select-%e5%81%9a%e5%86%b3%e7%ad%96%e5%8d%b4%e6%9c%9f%e6%9c%9b%e4%ba%92%e6%96%a5" class="header-anchor"&gt;&lt;/a&gt;1. 用普通 SELECT 做决策，却期望互斥
&lt;/h3&gt;&lt;p&gt;快照读不互斥。库存/余额扣减必须：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;事务内 &lt;code&gt;SELECT ... FOR UPDATE&lt;/code&gt; 再更新，或&lt;/li&gt;
&lt;li&gt;直接条件更新并检查影响行数：&lt;code&gt;UPDATE ... WHERE id=? AND balance&amp;gt;=?&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;仅靠 RR 的「两次 SELECT 一样」&lt;strong&gt;不能&lt;/strong&gt;防止超卖。&lt;/p&gt;
&lt;h3 id="2-唯一等值-vs-范围锁范围差很多"&gt;&lt;a href="#2-%e5%94%af%e4%b8%80%e7%ad%89%e5%80%bc-vs-%e8%8c%83%e5%9b%b4%e9%94%81%e8%8c%83%e5%9b%b4%e5%b7%ae%e5%be%88%e5%a4%9a" class="header-anchor"&gt;&lt;/a&gt;2. 唯一等值 vs 范围：锁范围差很多
&lt;/h3&gt;&lt;p&gt;官方写明：RR 下唯一索引等值命中通常只锁记录；范围或非唯一条件会锁间隙。排查「插入被堵住」时，先看 SQL 是否走了范围扫描、索引是否合适。&lt;/p&gt;
&lt;h3 id="3-rr-下select-看不到delete-却删到了"&gt;&lt;a href="#3-rr-%e4%b8%8bselect-%e7%9c%8b%e4%b8%8d%e5%88%b0delete-%e5%8d%b4%e5%88%a0%e5%88%b0%e4%ba%86" class="header-anchor"&gt;&lt;/a&gt;3. RR 下「SELECT 看不到，DELETE 却删到了」
&lt;/h3&gt;&lt;p&gt;这是官方明确描述过的快照读与 DML 当前读差异，不是玄学 bug。审计日志或对账逻辑要按「语句类型」理解可见性。&lt;/p&gt;
&lt;h3 id="4-死锁隔离级别救不了写写冲突"&gt;&lt;a href="#4-%e6%ad%bb%e9%94%81%e9%9a%94%e7%a6%bb%e7%ba%a7%e5%88%ab%e6%95%91%e4%b8%8d%e4%ba%86%e5%86%99%e5%86%99%e5%86%b2%e7%aa%81" class="header-anchor"&gt;&lt;/a&gt;4. 死锁：隔离级别救不了写写冲突
&lt;/h3&gt;&lt;p&gt;官方指出：死锁可能性与隔离级别无直接关系——隔离级别主要改读行为，而死锁来自写锁互相等待。处理要点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;保持默认死锁检测开启（&lt;code&gt;innodb_deadlock_detect&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;死锁发生时 InnoDB 会回滚其中一个事务（victim），&lt;strong&gt;应用必须可重试&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;关闭检测时依赖 &lt;code&gt;innodb_lock_wait_timeout&lt;/code&gt;，代价是更长的等待&lt;/li&gt;
&lt;li&gt;用 &lt;code&gt;SHOW ENGINE INNODB STATUS&lt;/code&gt; 查看最近一次死锁&lt;/li&gt;
&lt;li&gt;工程上：固定多表/多行加锁顺序、缩短事务、给 &lt;code&gt;WHERE&lt;/code&gt; 列建合适索引&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="5-历史版本与undo-涨爆"&gt;&lt;a href="#5-%e5%8e%86%e5%8f%b2%e7%89%88%e6%9c%ac%e4%b8%8eundo-%e6%b6%a8%e7%88%86" class="header-anchor"&gt;&lt;/a&gt;5. 历史版本与「undo 涨爆」
&lt;/h3&gt;&lt;p&gt;长查询、忘记提交的会话、超大事务都会让 update undo 滞留。监控 undo 表空间、历史列表长度，并在应用层强制事务超时/连接回收。&lt;/p&gt;
&lt;h3 id="6-混用锁定读与非锁定读"&gt;&lt;a href="#6-%e6%b7%b7%e7%94%a8%e9%94%81%e5%ae%9a%e8%af%bb%e4%b8%8e%e9%9d%9e%e9%94%81%e5%ae%9a%e8%af%bb" class="header-anchor"&gt;&lt;/a&gt;6. 混用锁定读与非锁定读
&lt;/h3&gt;&lt;p&gt;官方不建议在同一 RR 事务里混用大量「普通 SELECT」与 &lt;code&gt;UPDATE&lt;/code&gt;/&lt;code&gt;SELECT ... FOR UPDATE&lt;/code&gt;，因为二者看到的状态基准不同，逻辑难推理；若目标接近串行，应显式使用加锁读或评估 SERIALIZABLE。&lt;/p&gt;
&lt;h2 id="总结"&gt;&lt;a href="#%e6%80%bb%e7%bb%93" class="header-anchor"&gt;&lt;/a&gt;总结
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;InnoDB 默认 &lt;strong&gt;REPEATABLE READ&lt;/strong&gt;，普通读靠 &lt;strong&gt;MVCC 快照&lt;/strong&gt;，写与加锁读靠 &lt;strong&gt;行锁 +（RR 下）gap/next-key&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;RC&lt;/strong&gt; 每次一致性读换新快照、锁定读基本不锁 gap，并发更好但幻读与不可重复读更易出现。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SERIALIZABLE&lt;/strong&gt; 在关闭 autocommit 时把普通 &lt;code&gt;SELECT&lt;/code&gt; 变成 &lt;code&gt;FOR SHARE&lt;/code&gt;，代价是锁竞争。&lt;/li&gt;
&lt;li&gt;工程上优先：短事务、正确索引、该当前读就 &lt;code&gt;FOR UPDATE&lt;/code&gt;、死锁可重试；不要把「提高隔离级别」当成唯一药方。&lt;/li&gt;
&lt;li&gt;理解「快照读 vs 当前读」比背诵四个级别名称更能减少线上数据竞争事故。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="参考资料"&gt;&lt;a href="#%e5%8f%82%e8%80%83%e8%b5%84%e6%96%99" class="header-anchor"&gt;&lt;/a&gt;参考资料
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;MySQL 8.4 Reference Manual — &lt;a class="link" href="https://dev.mysql.com/doc/refman/8.4/en/innodb-transaction-isolation-levels.html" target="_blank" rel="noopener"
 &gt;Transaction Isolation Levels&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;MySQL 8.0 Reference Manual（Oracle Docs）— &lt;a class="link" href="https://docs.oracle.com/cd/E17952_01/mysql-8.0-en/innodb-multi-versioning.html" target="_blank" rel="noopener"
 &gt;InnoDB Multi-Versioning&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;MySQL 8.0 Reference Manual — &lt;a class="link" href="https://docs.oracle.com/cd/E17952_01/mysql-8.0-en/innodb-consistent-read.html" target="_blank" rel="noopener"
 &gt;Consistent Nonlocking Reads&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;MySQL 8.0 Reference Manual — &lt;a class="link" href="https://docs.oracle.com/cd/E17952_01/mysql-8.0-en/innodb-locking.html" target="_blank" rel="noopener"
 &gt;InnoDB Locking&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;MySQL 8.0 Reference Manual — &lt;a class="link" href="https://docs.oracle.com/cd/E17952_01/mysql-8.0-en/innodb-next-key-locking.html" target="_blank" rel="noopener"
 &gt;Phantom Rows&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;MySQL 8.0 Reference Manual — &lt;a class="link" href="https://docs.oracle.com/cd/E17952_01/mysql-8.0-en/innodb-deadlocks.html" target="_blank" rel="noopener"
 &gt;Deadlocks in InnoDB&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;MySQL 8.0 Reference Manual — &lt;a class="link" href="https://docs.oracle.com/cd/E17952_01/mysql-8.0-en/set-transaction.html" target="_blank" rel="noopener"
 &gt;SET TRANSACTION Statement&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;MariaDB Knowledge Base — &lt;a class="link" href="https://mariadb.com/kb/en/set-transaction/" target="_blank" rel="noopener"
 &gt;SET TRANSACTION&lt;/a&gt;（辅助对照 InnoDB 兼容实现）&lt;/li&gt;
&lt;/ol&gt;</description></item></channel></rss>