<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Zongzhi Chen</title>
    <description>The latest articles on DEV Community by Zongzhi Chen (@baotiao).</description>
    <link>https://dev.to/baotiao</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F832714%2F2b38e8ae-343b-4033-9208-639aa4d4512c.jpeg</url>
      <title>DEV Community: Zongzhi Chen</title>
      <link>https://dev.to/baotiao</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/baotiao"/>
    <language>en</language>
    <item>
      <title>In 2026, Can AI Modify Database Kernel Code? Rewriting PostgreSQL with Claude Code: Full Page Write vs Doublewrite Buffer</title>
      <dc:creator>Zongzhi Chen</dc:creator>
      <pubDate>Fri, 13 Feb 2026 21:41:07 +0000</pubDate>
      <link>https://dev.to/baotiao/in-2026-can-ai-modify-database-kernel-code-rewriting-postgresql-with-claude-code-full-page-write-360g</link>
      <guid>https://dev.to/baotiao/in-2026-can-ai-modify-database-kernel-code-rewriting-postgresql-with-claude-code-full-page-write-360g</guid>
      <description>&lt;p&gt;It's 2026. Can AI actually modify database kernel code? I used Claude Code to replace PostgreSQL's Full Page Write with MySQL's Doublewrite Buffer approach. Turns out DWB is about 3x faster.&lt;/p&gt;

&lt;h4&gt;
  
  
  Why I Did This
&lt;/h4&gt;

&lt;p&gt;I've been bugged by this question for years: which torn page protection is better, PostgreSQL's Full Page Write (FPW) or MySQL's Doublewrite Buffer (DWB)? I brought it up on the &lt;a href="https://www.postgresql.org/message-id/CAGbZs7hbJeUe7xY4QD25QW6VSnNFk1e3cwbCa8_R%2B2%2BYnoYRKw%40mail.gmail.com" rel="noopener noreferrer"&gt;pgsql-hackers mailing list&lt;/a&gt; a while back, but the discussion didn't really go anywhere.&lt;/p&gt;

&lt;p&gt;Now in 2026, I figured I'd kill two birds with one stone: see if Claude Code can handle kernel-level modifications, and settle the FPW vs DWB debate with actual code. Show me the code -- so here we are.&lt;/p&gt;

&lt;h4&gt;
  
  
  The Torn Page Problem
&lt;/h4&gt;

&lt;p&gt;Databases manage data in pages -- 8KB in PostgreSQL, 16KB in MySQL. But the OS and disk atomic write unit is usually 4KB.&lt;/p&gt;

&lt;p&gt;So writing one database page takes multiple physical I/Os. If you lose power or crash halfway through, you get a partially written page -- a &lt;strong&gt;torn page&lt;/strong&gt; with mixed old and new data. Corrupted.&lt;/p&gt;

&lt;p&gt;PostgreSQL and MySQL handle this very differently.&lt;/p&gt;

&lt;h4&gt;
  
  
  PostgreSQL: Full Page Write
&lt;/h4&gt;

&lt;p&gt;After each checkpoint, the first time a page gets modified, PostgreSQL dumps the entire page into WAL. If a crash corrupts that page, recovery grabs the full page image from WAL, overwrites the bad page, then replays the remaining WAL records.&lt;/p&gt;

&lt;p&gt;The problem? &lt;strong&gt;FPW makes checkpoint frequency a lose-lose decision.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You want fewer checkpoints because every checkpoint triggers a flood of full-page WAL writes for all the pages getting dirtied for the first time. WAL bloats, write performance tanks. That's why &lt;code&gt;checkpoint_timeout&lt;/code&gt; has a floor of 30 seconds. And of course checkpoints can also be triggered by exceeding &lt;code&gt;max_wal_size&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;But you also want more checkpoints because crash recovery replays less WAL, so the database comes back faster.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;FPW wants fewer checkpoints. Fast recovery wants more. Pick one.&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  MySQL: Doublewrite Buffer
&lt;/h4&gt;

&lt;p&gt;InnoDB does it differently. Before flushing dirty pages to their actual data file locations, it sequentially writes them into a dedicated Doublewrite Buffer area on disk. Once the buffer fills, one &lt;code&gt;fsync()&lt;/code&gt;, then scatter-write the pages to where they actually belong.&lt;/p&gt;

&lt;p&gt;Crash? On restart, check the Doublewrite Buffer for intact copies of any torn pages, restore them, done. No torn pages.&lt;/p&gt;

&lt;h4&gt;
  
  
  Why I Think Doublewrite Buffer Wins
&lt;/h4&gt;

&lt;h5&gt;
  
  
  Foreground vs background
&lt;/h5&gt;

&lt;p&gt;Without data merging:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;FPW = 1 WAL write + 1 page write&lt;/li&gt;
&lt;li&gt;DWB = 2 page writes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both 2 I/Os. But WAL writes are on the foreground path -- they directly hit your SQL latency. DWB writes are on the background flush path -- users barely notice.&lt;/p&gt;

&lt;h5&gt;
  
  
  Batching potential
&lt;/h5&gt;

&lt;p&gt;DWB doesn't &lt;code&gt;fsync()&lt;/code&gt; every page. It fills a buffer, then syncs once. WAL writes can batch too, sure, but it's the foreground path -- you can't make users wait forever, so the batching window is small.&lt;/p&gt;

&lt;h5&gt;
  
  
  No checkpoint frequency trade-off
&lt;/h5&gt;

&lt;p&gt;DWB doesn't depend on checkpoints for torn page protection. So you can crank up checkpoint frequency for faster crash recovery without the write amplification penalty.&lt;/p&gt;

&lt;h4&gt;
  
  
  Benchmarks
&lt;/h4&gt;

&lt;p&gt;Config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;shared_buffers=4GB
wal_buffers=64MB
synchronous_commit=on
maintenance_work_mem=2GB
checkpoint_timeout=30s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi8jsxyviaw6sgaeg1usb.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi8jsxyviaw6sgaeg1usb.webp" alt=" " width="800" height="241"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fresh database per scenario, &lt;code&gt;VACUUM FULL&lt;/code&gt; + 60s warmup before each 300s run.&lt;/p&gt;

&lt;p&gt;Scenario: io-bound, --tables=10 --table_size=10000000&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Workload&lt;/th&gt;
&lt;th&gt;Threads&lt;/th&gt;
&lt;th&gt;FPW OFF (QPS)&lt;/th&gt;
&lt;th&gt;FPW ON (QPS)&lt;/th&gt;
&lt;th&gt;DWB ON (QPS)&lt;/th&gt;
&lt;th&gt;FPW OFF (TPS)&lt;/th&gt;
&lt;th&gt;FPW ON (TPS)&lt;/th&gt;
&lt;th&gt;DWB ON (TPS)&lt;/th&gt;
&lt;th&gt;FPW OFF (ms)&lt;/th&gt;
&lt;th&gt;FPW ON (ms)&lt;/th&gt;
&lt;th&gt;DWB ON (ms)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;read_write&lt;/td&gt;
&lt;td&gt;32&lt;/td&gt;
&lt;td&gt;360,764&lt;/td&gt;
&lt;td&gt;158,865&lt;/td&gt;
&lt;td&gt;260,171&lt;/td&gt;
&lt;td&gt;18,038&lt;/td&gt;
&lt;td&gt;7,943&lt;/td&gt;
&lt;td&gt;13,009&lt;/td&gt;
&lt;td&gt;1.77&lt;/td&gt;
&lt;td&gt;4.03&lt;/td&gt;
&lt;td&gt;2.46&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;read_write&lt;/td&gt;
&lt;td&gt;64&lt;/td&gt;
&lt;td&gt;484,988&lt;/td&gt;
&lt;td&gt;190,654&lt;/td&gt;
&lt;td&gt;307,735&lt;/td&gt;
&lt;td&gt;24,249&lt;/td&gt;
&lt;td&gt;9,533&lt;/td&gt;
&lt;td&gt;15,387&lt;/td&gt;
&lt;td&gt;2.64&lt;/td&gt;
&lt;td&gt;6.71&lt;/td&gt;
&lt;td&gt;4.16&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;read_write&lt;/td&gt;
&lt;td&gt;128&lt;/td&gt;
&lt;td&gt;556,021&lt;/td&gt;
&lt;td&gt;194,301&lt;/td&gt;
&lt;td&gt;301,791&lt;/td&gt;
&lt;td&gt;27,801&lt;/td&gt;
&lt;td&gt;9,715&lt;/td&gt;
&lt;td&gt;15,387&lt;/td&gt;
&lt;td&gt;4.60&lt;/td&gt;
&lt;td&gt;13.17&lt;/td&gt;
&lt;td&gt;9.81&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;write_only&lt;/td&gt;
&lt;td&gt;32&lt;/td&gt;
&lt;td&gt;318,879&lt;/td&gt;
&lt;td&gt;108,696&lt;/td&gt;
&lt;td&gt;188,760&lt;/td&gt;
&lt;td&gt;53,146&lt;/td&gt;
&lt;td&gt;18,116&lt;/td&gt;
&lt;td&gt;31,460&lt;/td&gt;
&lt;td&gt;0.60&lt;/td&gt;
&lt;td&gt;1.77&lt;/td&gt;
&lt;td&gt;1.02&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;write_only&lt;/td&gt;
&lt;td&gt;64&lt;/td&gt;
&lt;td&gt;345,766&lt;/td&gt;
&lt;td&gt;117,533&lt;/td&gt;
&lt;td&gt;197,251&lt;/td&gt;
&lt;td&gt;57,628&lt;/td&gt;
&lt;td&gt;19,589&lt;/td&gt;
&lt;td&gt;32,875&lt;/td&gt;
&lt;td&gt;1.11&lt;/td&gt;
&lt;td&gt;3.27&lt;/td&gt;
&lt;td&gt;1.95&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;write_only&lt;/td&gt;
&lt;td&gt;128&lt;/td&gt;
&lt;td&gt;356,725&lt;/td&gt;
&lt;td&gt;89,144&lt;/td&gt;
&lt;td&gt;202,884&lt;/td&gt;
&lt;td&gt;59,454&lt;/td&gt;
&lt;td&gt;14,857&lt;/td&gt;
&lt;td&gt;33,814&lt;/td&gt;
&lt;td&gt;2.15&lt;/td&gt;
&lt;td&gt;8.61&lt;/td&gt;
&lt;td&gt;3.78&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The numbers speak for themselves: FPW OFF is the baseline, FPW ON drops to ~25% of baseline, DWB ON holds at ~57%. At write_only 128 threads, DWB delivers 2.3x the throughput of FPW with much better latency across the board.&lt;/p&gt;

&lt;h4&gt;
  
  
  Code
&lt;/h4&gt;

&lt;p&gt;The modified PostgreSQL with Doublewrite Buffer support: &lt;a href="https://github.com/baotiao/postgres" rel="noopener noreferrer"&gt;https://github.com/baotiao/postgres&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The whole kernel modification was done with Claude Code. So yeah, AI can hack on database internals now.&lt;/p&gt;

&lt;p&gt;BTW: Check out &lt;a href="https://github.com/alibaba/AliSQL" rel="noopener noreferrer"&gt;AliSQL&lt;/a&gt;&lt;/p&gt;

</description>
      <category>postgressql</category>
      <category>claudecode</category>
      <category>database</category>
    </item>
    <item>
      <title>MySQL 常见死锁场景 -- 并发Replace into导致死锁</title>
      <dc:creator>Zongzhi Chen</dc:creator>
      <pubDate>Mon, 04 Dec 2023 20:00:45 +0000</pubDate>
      <link>https://dev.to/baotiao/mysql-chang-jian-si-suo-chang-jing-bing-fa-replace-intodao-zhi-si-suo-104h</link>
      <guid>https://dev.to/baotiao/mysql-chang-jian-si-suo-chang-jing-bing-fa-replace-intodao-zhi-si-suo-104h</guid>
      <description>&lt;p&gt;在之前的文章 &lt;a href="https://zhuanlan.zhihu.com/p/503880736"&gt;#issue 68021 MySQL unique check 问题&lt;/a&gt;中, 我们已经介绍了在 MySQL 里面, 由于唯一键的检查(unique check), 导致 MySQL 在 Read Commit 隔离级别也需要添加 GAP lock, 导致有些比较奇怪情况下有一些锁等待.&lt;/p&gt;

&lt;p&gt;另外一类问题是由于唯一键检查导致的死锁问题, 这类问题也非常多, 也是我们处理线上经常收到用户反馈的问题, 这里我们就分析几个这样死锁的 Case.&lt;/p&gt;

&lt;p&gt;Replace into 操作是非常常用的操作, 很多时候在插入数据的时候, 不确定表中是否已经存在数据, 有没有唯一性的冲突, 所以会使用 replace into 或者 insert .. on duplicate update 这样的操作, 如果冲突就把对应的行给自动更新.&lt;/p&gt;

&lt;p&gt;但是这样的操作在并发场景, 当存在唯一键的时候容易有死锁问题场景, 那么为什么会这样, 我们来看一个简单的 case:&lt;/p&gt;

&lt;p&gt;通过GDB 和脚本可以复现以下死锁场景.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;create table t(a int AUTO_INCREMENT, b int, PRIMARY KEY (a), UNIQUE KEY (b));

insert into t(a, b) values (100, 8);

session1:
replace into t(a, b) values (10, 8);

session2:
replace into t(a, b) values (11, 8);
(40001): Deadlock found when trying to get lock; try restarting transaction
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;当然也可以通过这个脚本, 不需要 GDB 就可以随机复现:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#! /bin/bash&lt;/span&gt;

&lt;span class="nv"&gt;MYSQL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"mysql -h127.0.0.1 -P2255 -uroot test"&lt;/span&gt;

&lt;span class="nv"&gt;$MYSQL&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"create table t(a int AUTO_INCREMENT, b int, PRIMARY KEY (a), UNIQUE KEY (b))"&lt;/span&gt;

&lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;true
&lt;/span&gt;&lt;span class="k"&gt;do&lt;/span&gt;

  &lt;span class="nv"&gt;$MYSQL&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"replace into t(b) values (8)"&lt;/span&gt; &amp;amp;
  &lt;span class="nv"&gt;$MYSQL&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"replace into t(b) values (8)"&lt;/span&gt; &amp;amp;
  &lt;span class="nv"&gt;$MYSQL&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"replace into t(b) values (8)"&lt;/span&gt; &amp;amp;

  &lt;span class="nb"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;这里在并发session1 和 session2 插入的时候, 就容易出现 Deadlock Lock 的问题, 类似用户并发插入数据的场景.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OjI0GBgB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://raw.githubusercontent.com/baotiao/bb/main/uPic/image-20230608205808520.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OjI0GBgB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://raw.githubusercontent.com/baotiao/bb/main/uPic/image-20230608205808520.png" alt="image-20230608205808520" width="800" height="490"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;上面的死锁信息 Trx HOLDS THE LOCK 和 WAITING FOR THIS LOCK TO BE GRANTED 是一个错误的误导信息, 官方版本在新的版本中已经修复, 这里 HOLDS THE LOCK 是不对的, 其实还未持有 X lock.&lt;/p&gt;

&lt;p&gt;这里看到 Trx 1 waiting 在 8, 100 next-key X lock 上.&lt;/p&gt;

&lt;p&gt;然后 Trx2 持有 8, 100 next-key X lock, 但是 WAITING FOR 8, 100 insert_intention lock.&lt;/p&gt;

&lt;p&gt;那么为什么会有死锁呢?&lt;/p&gt;

&lt;p&gt;我们先看一下单个 replace into 的流程&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;整体而言, 如果replace into 第1遍insert 操作的时候, 遇到unique index 冲牧, 那么需要重新执行update 操作或者delete + 重新insert 操作, 但是第1遍insert 操作失败添加的事务锁并不会释放, 而是等到整个事务提交才会释放, 原因当然是现在MySQL 2Phase Lock 机制要做的保证&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;replace into 大概代码如下:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="err"&gt;所有&lt;/span&gt;&lt;span class="n"&gt;replace&lt;/span&gt; &lt;span class="n"&gt;into&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="n"&gt;duplicate&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="err"&gt;这里&lt;/span&gt;&lt;span class="n"&gt;execute_inner&lt;/span&gt; &lt;span class="err"&gt;执行的是&lt;/span&gt;&lt;span class="n"&gt;Sql_cmd_insert_values&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;execute_inner&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="err"&gt;方法&lt;/span&gt;

&lt;span class="err"&gt;这里&lt;/span&gt;&lt;span class="n"&gt;replace&lt;/span&gt; &lt;span class="n"&gt;into&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="n"&gt;duplicate&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="err"&gt;执行在这个循环里面&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;duplicate_handling&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;DUP_REPLACE&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;duplicate_handling&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;DUP_UPDATE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;DBUG_ASSERT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;duplicate_handling&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;DUP_UPDATE&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;ha_write_row&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// ...&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;duplicate_handling&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;DUP_UPDATE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="err"&gt;这里&lt;/span&gt; &lt;span class="n"&gt;branch&lt;/span&gt; &lt;span class="err"&gt;就是处理&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="n"&gt;duplicate&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="err"&gt;的&lt;/span&gt;&lt;span class="n"&gt;duplicate&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="err"&gt;场景&lt;/span&gt;
    &lt;span class="err"&gt;判断如果是&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="n"&gt;duplicate&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="err"&gt;逻辑&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;那么遇到&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="err"&gt;以后&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;就是用&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;ha_update_row&lt;/span&gt; &lt;span class="err"&gt;通过&lt;/span&gt; &lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="err"&gt;进行更新&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="cm"&gt;/* DUP_REPLACE */&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;duplicate_handling&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;DUP_REPLACE&lt;/span&gt; &lt;span class="err"&gt;就是处理&lt;/span&gt; &lt;span class="n"&gt;replace&lt;/span&gt; &lt;span class="n"&gt;into&lt;/span&gt; &lt;span class="err"&gt;错误场景&lt;/span&gt;
    &lt;span class="err"&gt;在&lt;/span&gt;&lt;span class="n"&gt;replace&lt;/span&gt; &lt;span class="n"&gt;into&lt;/span&gt;&lt;span class="err"&gt;场景中&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;如果插入的&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="err"&gt;遇到冲突的&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;是如何处理的&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;其实是分&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="err"&gt;种场景的&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="err"&gt;如果是&lt;/span&gt; &lt;span class="n"&gt;replace&lt;/span&gt; &lt;span class="n"&gt;into&lt;/span&gt; &lt;span class="err"&gt;逻辑&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;遇到&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="err"&gt;以后&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;如果是冲突的是最后一个&lt;/span&gt; &lt;span class="n"&gt;unique&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;并且没有外键约束&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;并且没有&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt; &lt;span class="n"&gt;trigger&lt;/span&gt; &lt;span class="err"&gt;的时候&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;那么和&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="n"&gt;duplicate&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="err"&gt;一样&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;使用&lt;/span&gt; &lt;span class="n"&gt;ha_update_row&lt;/span&gt; &lt;span class="err"&gt;通过&lt;/span&gt; &lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="err"&gt;进行更新&lt;/span&gt;

    &lt;span class="err"&gt;否则通过&lt;/span&gt; &lt;span class="k"&gt;delete&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="err"&gt;重新&lt;/span&gt; &lt;span class="n"&gt;insert&lt;/span&gt; &lt;span class="err"&gt;来进行更新&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;操作更多&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;消耗也就更多&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

    &lt;span class="err"&gt;具体代码&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="err"&gt;如果&lt;/span&gt;&lt;span class="n"&gt;ha_write_row&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="err"&gt;失败&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;那么会执行&lt;/span&gt;&lt;span class="n"&gt;delete_row&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="err"&gt;操作&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;等这个操作执行完成以后&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;又跳到这个&lt;/span&gt;&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="err"&gt;循环进行重新&lt;/span&gt;&lt;span class="n"&gt;insert&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;ha_delete_row&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;record&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="k"&gt;goto&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="cm"&gt;/* Let us attempt do write_row() once more */&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;接下来是2个replace into 操作的时候, 如果Thread 1 停在replace into 第一个阶段, 也就是insert 遇到unique index 冲突, 此时持有8, 100 next-key lock.&lt;/p&gt;

&lt;p&gt;这个时候第2个Thread 2也进行replace into 操作, 在进行唯一键冲突检测, 执行row_ins_scan_sec_index_for_duplicate() 的时候需要申请8, 100 next-key lock. 该lock 被thread 1持有, 那么只能进行等待.&lt;/p&gt;

&lt;p&gt;接下来Thread 1 继续执行, 执行update 操作, 在InnoDB 里面, 对于二级索引而言需要执行delete, 然后再insert 操作, 在insert 的时候需要持有8, 100 insert intention lock. 目前 InnoDB insert intention lock 判断是否冲突的时候, 对应的 record 不论是有事务等待或者已经持有 next-key lock, 都算冲突. 此时Thread 已经等在8, 100 next-key lock 上, 那么 Thread 1 就无法获得 insert intention lock, 只能进行等待.&lt;/p&gt;

&lt;p&gt;这里有一个问题: 为什么申请insert_intention 的时候,  如果有其他事务提前等待在这个 lock 的 next-key lock 上面, 那么这个 insert_intention 会申请失败?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uzEBF7Vz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://raw.githubusercontent.com/baotiao/bb/main/uPic/image-20230611025844248.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uzEBF7Vz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://raw.githubusercontent.com/baotiao/bb/main/uPic/image-20230611025844248.png" alt="image-20230611025844248" width="800" height="192"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;在函数rec_lock_check_conflict() 解释了这个问题, 因为如果申请 intention lock 成功, 那么接下来的 insert 操作也就会成功, 那么原来等待这个 record 上面的trx 就变成需要等待 2 个 record 了.&lt;/p&gt;

&lt;p&gt;比如如果之前 trx2 wait 在(4, 10] 这个 next-key lock 上, 如果允许 trx1 插入了 7,这个 record, 那么根据锁继承机制, 7 会继承 10 这个 record 上面的 next-key lock, 那么 trx2 就变成 wait 在两个 record 上, 也就变成 2 个 waiting lock 了, 那么现有这套锁等待唤醒机制就也要改了,  现在这套锁等待唤醒机制因此一个 trx 只会等待一个 lock, 在一个 lock 释放以后, 相应等待在这个 Lock 上面的 trx 就可以唤醒了.&lt;/p&gt;

&lt;p&gt;因此为了规避这样的问题, MySQL InnoDB 里面如果申请 insert_intention lock 的时候, 如果有其他事务提前等待在这个 lock 的 next-key lock 上, 那么 insert_intention lock 是无法申请成功的.&lt;/p&gt;

&lt;p&gt;那么现在的就过就是 Thread 2 等待 Thread 1 next-key lock 释放, Thread 1 等待 Thread 2 next-key lock 获得并释放, 出现了 Thread1 &amp;lt;=&amp;gt; Thread2 互相等待的情况 因此出现的死锁.&lt;/p&gt;

</description>
      <category>mysql</category>
      <category>innodb</category>
      <category>database</category>
    </item>
  </channel>
</rss>
