<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          為什么不建議使用ON DUPLICATE KEY UPDATE?

          共 7536字,需瀏覽 16分鐘

           ·

          2022-06-14 11:56

          往期熱門(mén)文章:

          1、3種常見(jiàn)的數(shù)據(jù)脫敏方案
          2、這個(gè)隊(duì)列的思路真的好,現(xiàn)在它是我簡(jiǎn)歷上的亮點(diǎn)了。
          3、痛快!SpringBoot終于禁掉了循環(huán)依賴!
          4、BigDecimal使用不當(dāng),造成P0事故!
          5、Spring Boot 啟動(dòng)時(shí)自動(dòng)執(zhí)行代碼的幾種方式,還有誰(shuí)不會(huì)??

          章來(lái)源:https://c1n.cn/BeR1Q


          目錄

          • 背景

          • 官方資料

          • 進(jìn)行驗(yàn)證

          • 總結(jié)


          背景


          昨天評(píng)審代碼時(shí),大佬同事看到我代碼里使用了 mysql 的 on duplicate key update 語(yǔ)法實(shí)現(xiàn)了對(duì)數(shù)據(jù)的 save or update,說(shuō)這個(gè)語(yǔ)法有嚴(yán)重的性能和其他隱患問(wèn)題,讓我必須改成先查詢一次分出新增集合和修改集合,再分別進(jìn)行批量新增和批量修改的方式進(jìn)行,并對(duì)批量修改時(shí)使用 case when 的方式實(shí)現(xiàn)。

          對(duì)于批量修改,在 mybatis 中也就是類(lèi)似這種的 xml:
              <update id="updateByIds">
                  update tb_user
                  <trim prefix="set" suffixOverrides=",">
                      <trim prefix="name = case" suffix="end,">
                          <foreach collection="list" item="i" index="index">
                              when id= #{i.id,jdbcType=VARCHAR} then #{i.name,jdbcType=VARCHAR}
                          </foreach>
                      </trim>
                      <trim prefix="weight = case" suffix="end,">
                          <foreach collection="list" item="i" index="index">
                              when id= #{i.id,jdbcType=VARCHAR} then #{i.weight,jdbcType=DECIMAL}
                          </foreach>
                      </trim>
                      <trim prefix="high = case" suffix="end,">
                          <foreach collection="list" item="i" index="index">
                              when id= #{i.id,jdbcType=VARCHAR} then #{i.high,jdbcType=DECIMAL}
                          </foreach>
                      </trim>
                  </trim>
                  where id in
                  <foreach collection="list" item="item" open="(" close=")" separator=",">
                      #{item.id,jdbcType=VARCHAR}
                  </foreach>
              </update>

          對(duì)于這種做法我也表示認(rèn)同,但我還是很想了解一下 on duplicate key update 到底有什么問(wèn)題,問(wèn)大佬同事也說(shuō)不出具體的性能和隱患原因在哪里,所以我決定還是靠自己研究一下。

          官方資料


          為了能更直接獲取出最權(quán)威的信息,直接上 mysql 的官方說(shuō)明查看有無(wú)對(duì)應(yīng)的資料。

          根據(jù)使用的 mysql 版本查看對(duì)應(yīng)的說(shuō)明,如我這里的 mysql5.7 為例,其官方說(shuō)明地址如下:
          https://dev.mysql.com/doc/refman/5.7/en/insert-on-duplicate.html

          其中對(duì)于 on duplicate key update 的使用方法也有非常詳細(xì)的說(shuō)明。
          這里對(duì)于它的使用方法不做介紹,感興趣的可以點(diǎn)開(kāi)上面的鏈接進(jìn)行詳細(xì)的查看。

          但為了對(duì)官方文檔中的說(shuō)明進(jìn)行驗(yàn)證,這里根據(jù)官方的說(shuō)明進(jìn)行一個(gè)小實(shí)驗(yàn)進(jìn)行驗(yàn)證。

          進(jìn)行驗(yàn)證

          ?

          創(chuàng)建一個(gè) t1 表:
          CREATE TABLE `t1` (
            `a` bigint(20unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主鍵ID',
            `b` int(11),
            `c` int(11),
            PRIMARY KEY (`a`)
          ENGINE=InnoDB  DEFAULT CHARSET=utf8 COMMENT='臨時(shí)測(cè)試表'

          | 驗(yàn)證主鍵插入并更新功能

          空表創(chuàng)建好后,多次執(zhí)行如下 sql(此時(shí)只有自增主鍵 a 列):
          INSERT INTO t1 (a,b,c) VALUES (1,2,3)
          ON DUPLICATE KEY UPDATE c=c+1;

          執(zhí)行 1 次的結(jié)果:
          執(zhí)行 2 次的結(jié)果:
          執(zhí)行 3 次的結(jié)果:
          執(zhí)行 4 次的結(jié)果:
          執(zhí)行 5 次的結(jié)果:
          通過(guò)觀察可知,上面的 sql 在主鍵已經(jīng)存在時(shí)相當(dāng)于如下 sql:
          UPDATE t1 SET c=c+1 WHERE a=1

          再試下新增的 sql:
          INSERT INTO t1 (b,c) VALUES (20,30)
          ON DUPLICATE KEY UPDATE c=c+1;

          新增記錄成功,id 也自增正常。

          | 驗(yàn)證多字段唯一索引問(wèn)題

          在官方資料中有這樣的一句話:

          If column b is also unique, the INSERT is equivalent to this UPDATE statement instead:


          UPDATE t1 SET c=c+1 WHERE a=1 OR b=2 LIMIT 1;
          ?

          If a=1 OR b=2 matches several rows, only one row is updated. In general, you should try to avoid using an ON DUPLICATE KEY UPDATE clause on tables with multiple unique indexes.


          接下來(lái)實(shí)驗(yàn)一下,給 t1 加的 b 也加上唯一索引:
          ALTER TABLE t1 ADD UNIQUE INDEX uniq_b (b ASC);

          然后執(zhí)行如下 sql:
          INSERT INTO t1 (a,b,c) VALUES (3,20,30)
          ON DUPLICATE KEY UPDATE c=c+1;

          其 t1 表結(jié)果如下:
          從上面的結(jié)果可以看出,其只執(zhí)行了 update 的操作,從而告訴了我們?cè)谑褂?on duplicate key update 語(yǔ)句時(shí),應(yīng)當(dāng)避免多個(gè)唯一索引的場(chǎng)景。

          當(dāng) a 是一個(gè)唯一索引(unique index)時(shí),并且 t1 表中已經(jīng)存在 a 為 1 的記錄時(shí),如下兩個(gè) sql 的效果是一樣的。
          INSERT INTO t1 (a,b,c) VALUES (1,2,3)
          ON DUPLICATE KEY UPDATE c=c+1;

          UPDATE t1 SET c=c+1 WHERE a=1;

          ALTER TABLE t1 DROP INDEX uniq_b ;

          ALTER TABLE ntocc_test.t1
          ADD UNIQUE INDEX uniq_b (b ASC);
          ;

          但在 innoBD 存儲(chǔ)類(lèi)型的表中,當(dāng) a 是一個(gè)自增主鍵時(shí),其效果官方文檔中的解釋是這樣的:

          The effects are not quite identical: For an InnoDB table where a is an auto-increment column, the INSERT statement increases the auto-increment value but the UPDATE does not.


          也就是如果只有一個(gè)主鍵,則會(huì)執(zhí)行新增操作,但當(dāng) b 也是一個(gè)唯一索引時(shí),就會(huì)執(zhí)行更新操作。

          上面的語(yǔ)句就會(huì)變成這樣的:
          UPDATE t1 SET c=c+1 WHERE a=1 OR b=2 LIMIT 1;
          ?????????????

          If a=1 OR b=2 matches several rows, only one row is updated. In general, you should try to avoid using an ON DUPLICATE KEY UPDATE clause on tables with multiple unique indexes.

          ?

          因此應(yīng)當(dāng)避免多唯一索引用 on deplicate key update 語(yǔ)法。?????????????

          | 涉及到的鎖說(shuō)明

          同時(shí),在查看官網(wǎng)資料中底部對(duì)于此語(yǔ)法的說(shuō)明,從中看到如下描述:

          An INSERT … ON DUPLICATE KEY UPDATE on a partitioned table using a storage engine such as MyISAM that employs table-level locks locks any partitions of the table in which a partitioning key column is updated. (This does not occur with tables using storage engines such as InnoDB that employ row-level locking.) For more information, see Section 22.6.4, “Partitioning and Locking”.


          主要是說(shuō)在 MyISAM 的存儲(chǔ)引擎中,on duplicate key update 使用的是表級(jí)鎖來(lái)進(jìn)行實(shí)現(xiàn)的,那么就可以存在表級(jí)鎖時(shí)的事務(wù)并發(fā)性能問(wèn)題。

          但是 innoDB 引擎中,on duplicate key update 是用的行級(jí)鎖進(jìn)行實(shí)現(xiàn)的。

          但同時(shí)查看了官方的 bug 列表,發(fā)現(xiàn)如下記錄:https://bugs.mysql.com/bug.php?id=52020
          其中有如下記錄:

          Hi,

          I am facing this same issue in version 5.7.18. Deadlock error when multiple threads execute INSERT… ON DUPLICATE KEY UPDATE for bulk insert/update.

          How it can be fixed?


          I am facing the same issue when multiple threads are trying to insert in same table with primary key and unique index. Records are being inserted are different. Also It seems to be taking next-key lock here.

          ?

          主要是說(shuō)在并發(fā)事務(wù)的情況下,可能會(huì)導(dǎo)致死鎖。

          為了對(duì)此進(jìn)行驗(yàn)證,我使用連接工具進(jìn)行了驗(yàn)證,但可能是因?yàn)椴l(fā)不夠的原因,并沒(méi)有產(chǎn)生死鎖。

          總結(jié)

          ?

          如下:
          • on duplicate key update 在 MyISAM 存儲(chǔ)引擎下使用的是表鎖,性能不好。

          • on duplicate key update 在 InnoDB 下并發(fā)事務(wù)情況下可能會(huì)存在鎖表/死鎖問(wèn)題。

          • 應(yīng)盡量避免在多唯一索引的情況下使用此語(yǔ)句。


          往期熱門(mén)文章:

          1、MySQL 暴跌!
          2、超越 Xshell!號(hào)稱下一代 Terminal 終端神器,用完愛(ài)不釋手!
          3、IDEA 官宣全新默認(rèn) UI,太震撼了!!
          4、讓你直呼「臥槽」的 GitHub 項(xiàng)目!
          5、Kafka又笨又重,為啥不選Redis?
          6、50多個(gè)高頻免費(fèi) API 接口分享
          7、IDEA公司再發(fā)新神器!超越 VS Code 騷操作!
          8、我懷疑這是 IDEA 的 BUG,但是我翻遍全網(wǎng)沒(méi)找到證據(jù)!
          9、Spring MVC 中的 Controller 是線程安全的嗎?
          10、Gitee 倒下了???

          瀏覽 41
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  91久久国产综合久久91精品网站 | 国产黄色三级片 | 99国产香蕉| 中文字幕无码乱伦 | 99在线免费 |