<?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 Zampo Blog</title><link>https://blog.cpdd.fyi/tags/innodb/</link><description>Recent content in InnoDB on Zampo Blog</description><generator>Hugo</generator><language>zh-cn</language><lastBuildDate>Wed, 03 Jun 2026 16:34:00 +0800</lastBuildDate><atom:link href="https://blog.cpdd.fyi/tags/innodb/index.xml" rel="self" type="application/rss+xml"/><item><title>MySQL 是蠢是坏，还是又蠢又坏</title><link>https://blog.cpdd.fyi/posts/mysql-stupid-or-evil/</link><pubDate>Wed, 03 Jun 2026 16:34:00 +0800</pubDate><guid>https://blog.cpdd.fyi/posts/mysql-stupid-or-evil/</guid><description>&lt;p&gt;你只是改了一下订单状态，结果买家表被锁住了。&lt;/p&gt;
&lt;p&gt;订单状态在订单表里。买家信息在买家表里。按正常直觉，这两个动作不该互相打扰。外键要保护的是买家 ID 这种关联字段，不是订单状态这种业务字段。&lt;/p&gt;
&lt;p&gt;但 MySQL 里事情偏偏就这么发生了：因为订单状态和买家 ID 出现在同一个 composite index 里，更新订单状态时，父表也被卷进了锁行为。&lt;/p&gt;
&lt;p&gt;这不是“开发者不会用数据库”的问题。&lt;/p&gt;
&lt;p&gt;真正别扭的地方在于：你很难从数据库基本原理里推导出这种结果。它不是那种“虽然复杂，但想通后合理”的设计，而更像一个长期使用者不得不记住的禁忌：这个功能别这么用，不然会出怪事。&lt;/p&gt;
&lt;p&gt;于是，MySQL 的争议就不该只停在“它有没有 bug”。&lt;/p&gt;
&lt;p&gt;更值得问的是：当一个工具足够普及，又把一些本来该可靠的能力做得反直觉，它会不会反过来改变一代程序员对数据库的想象？&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;烂工具最可怕的地方，不是它难用，而是它会重写行业常识。&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="懂行的人都不用这句话本身就很可疑"&gt;“懂行的人都不用”这句话，本身就很可疑&lt;/h2&gt;
&lt;p&gt;很多 MySQL 使用者都听过一套经验：&lt;/p&gt;
&lt;p&gt;Foreign Key 少用，Trigger 别用，Enum 少碰，Composite Index 别乱搞。&lt;/p&gt;
&lt;p&gt;如果这句话只是提醒大家别滥用复杂功能，那没问题。工程里确实有很多功能不是“能用就该用”。外键会影响写入路径，触发器会增加隐式逻辑，枚举会带来迁移成本，复合索引也可能让优化器选择变复杂。&lt;/p&gt;
&lt;p&gt;但问题是，这套经验在很多团队里已经变味了。&lt;/p&gt;
&lt;p&gt;它不再是“理解代价后谨慎使用”，而是“懂行的人都不用”。&lt;/p&gt;
&lt;p&gt;这就很奇怪。&lt;/p&gt;
&lt;p&gt;Foreign Key 本来是关系数据库维持 referential integrity 的基础能力。Trigger 本来是数据库表达约束和副作用的一种方式。事务本来是数据库区别于电子表格的核心能力之一。你当然可以因为项目规模、团队习惯、迁移策略而不用它们，但如果一个生态里大量老手都把这些能力当雷区，那就说明问题不只是“用户没学好”。&lt;/p&gt;
&lt;p&gt;有时所谓最佳实践，只是长期踩坑后的创伤反应。&lt;/p&gt;
&lt;p&gt;这也是 MySQL 最有杀伤力的地方。它不只是让你踩坑，它还会让踩坑的人长出一套非常自洽的解释：数据库本来就不该管太多，业务逻辑都应该放应用层，表只要能 CRUD 就够了。&lt;/p&gt;
&lt;p&gt;听起来很务实。&lt;/p&gt;
&lt;p&gt;但这套务实背后，可能藏着一种被训练出来的贫瘠。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.cpdd.fyi/images/mysql-stupid-or-evil/inline-01.png" alt="工具缺陷如何训练成行业常识"&gt;&lt;/p&gt;
&lt;h2 id="第一个坑索引和外键不该这么纠缠"&gt;第一个坑：索引和外键不该这么纠缠&lt;/h2&gt;
&lt;p&gt;我们把视频里的例子简化一下。&lt;/p&gt;
&lt;p&gt;一个订单表有买家 ID，也有订单状态：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&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;buyers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;BIGINT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="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;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;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;orders&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;BIGINT&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;buyer_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;BIGINT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NOT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NOT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FOREIGN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buyer_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;REFERENCES&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;buyers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;UPDATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;CASCADE&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;如果你更新的是 &lt;code&gt;buyer_id&lt;/code&gt;，数据库需要检查父表、维护关系完整性，这很正常。&lt;/p&gt;</description></item></channel></rss>