日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

mysql Insert on duplicate引发的死锁

發(fā)布時間:2024/1/23 数据库 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 mysql Insert on duplicate引发的死锁 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

CREATE TABLE `test_dup` (

`id` int(11) NOT NULL AUTO_INCREMENT,

`num` int(10) unsigned DEFAULT NULL,

`name` varchar(45) DEFAULT NULL,

PRIMARY KEY (`id`),

UNIQUE KEY `num_index` (`num`)

) ENGINE=InnoDB AUTO_INCREMENT=138 DEFAULT CHARSET=utf8;

已有數(shù)據(jù)

1 1 hi

2 2 dcdcdf

30 3 dcdcdf2

33 50 43494

34 20 dcdcdf473894739

58 200 dcdcdf

?

mysql的版本和隔離級別

Repeatable read

mysql 5.7.17.

濤哥的操作

多個線程,每個線程執(zhí)行許多這樣的操作,但是保證每個線程執(zhí)行的num是絕對不一樣的:

insert into test_dup(num,name) values(38,'no') on duplicate key update name='dcdcdf4738';

事務大小設置為100。

?

死鎖日志

?

LATEST DETECTED DEADLOCK

------------------------

2017-09-14 22:35:44 0x7f8f447c6700

*** (1) TRANSACTION:

TRANSACTION 6559008, ACTIVE 7 sec inserting

mysql tables in use 1, locked 1

LOCK WAIT 3 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1

MySQL thread id 67521, OS thread handle 140253305255680, query id 16260084 10.47.54.38 sync_master update

insert into test_dup(num,name) values(39,'no') on duplicate key update name='dcdcdf4738'

*** (1) WAITING FOR THIS LOCK TO BE GRANTED:

RECORD LOCKS space id 2392 page no 4 n bits 80 index num_index of table `idoo`.`test_dup` trx id 6559008 lock_mode X locks gap before rec insert intention waiting

Record lock, heap no 10 PHYSICAL RECORD: n_fields 2; compact format; info bits 0

0: len 4; hex 00000028; asc (;;

1: len 4; hex 8000008a; asc ;;

?

*** (2) TRANSACTION:

TRANSACTION 6558977, ACTIVE 21 sec inserting

mysql tables in use 1, locked 1

4 lock struct(s), heap size 1136, 4 row lock(s), undo log entries 2

MySQL thread id 67531, OS thread handle 140253306054400, query id 16260339 10.47.54.38 sync_master update

insert into test_dup(num,name) values(38,'no') on duplicate key update name='dcdcdf4738'

*** (2) HOLDS THE LOCK(S):

RECORD LOCKS space id 2392 page no 4 n bits 80 index num_index of table `idoo`.`test_dup` trx id 6558977 lock_mode X locks gap before rec

Record lock, heap no 5 PHYSICAL RECORD: n_fields 2; compact format; info bits 0

0: len 4; hex 00000032; asc 2;;

1: len 4; hex 80000021; asc !;;

?

Record lock, heap no 10 PHYSICAL RECORD: n_fields 2; compact format; info bits 0

0: len 4; hex 00000028; asc (;;

1: len 4; hex 8000008a; asc ;;

?

*** (2) WAITING FOR THIS LOCK TO BE GRANTED:

RECORD LOCKS space id 2392 page no 4 n bits 80 index num_index of table `idoo`.`test_dup` trx id 6558977 lock_mode X locks gap before rec insert intention waiting

Record lock, heap no 10 PHYSICAL RECORD: n_fields 2; compact format; info bits 0

0: len 4; hex 00000028; asc (;;

1: len 4; hex 8000008a; asc ;;

?

從日志上來看似乎是兩個事務各自在等待對方的gap lock。

死鎖重現(xiàn)

  • 第一個事務執(zhí)行
    insert into test_dup(num,name) values(41,'no') on duplicate key update name='dcdcdf4738';?
    成功
  • 第二個事務執(zhí)行
    insert into test_dup(num,name) values(40,'no') on duplicate key update name='dcdcdf4738';
    事務陷入等待
  • 第一個事務執(zhí)行
    insert into test_dup(num,name) values(38,'no') on duplicate key update name='dcdcdf4738';
    顯示死鎖。
  • mysql可以打開鎖統(tǒng)計,通過以下語句打開mysql的鎖的統(tǒng)計

    set GLOBAL innodb_status_output_locks=ON;

    set GLOBAL innodb_status_output=ON;

    在第二步的時候我們運行show engine innodb status;查看鎖的情況發(fā)現(xiàn):

    事務1持有:IX鎖(表鎖),gap x鎖(在num_index上num=50之前的gap),gap x鎖(在num_index上num=41之前的gap),num_index上的record lock(num=41),

    事務2持有:IX鎖(表鎖),gap x鎖(在num_index上num=41之前的gap),insert intention lock(在等待事務1的第二個gap鎖)。

    如果這時候第三步執(zhí)行,那么事務1的insert intention也會等待事務2的gap鎖,死鎖形成。

    原因分析

  • 第二個事務在獲取insert intention lock之前先獲取了gap lock,導致第一個事務也不能獲取insert intention lock。gap lock不是被第一個事務獲取了么?是互斥鎖呀?因為mysql的gap鎖是兼容的,與互斥還是只讀無關(guān)。所以第二個gap鎖才能獲取。
  • 為什么insert intention lock的獲取在gap lock獲取之后,導致悲劇的發(fā)生。如果intention insert lock在之前那么就不會有死鎖了。這可能是mysql5.7的一個bug吧.
  • ?

    解決方案

    • 單線程執(zhí)行數(shù)據(jù)庫寫入
    • 減小事務的大小
    • 修改事務隔離級別為read committed,read committed隔離級別鎖的粒度是index lock。只有在foreign-key constraint checking 和duplicate-key checking.的時候才會使用gap lock。
      這里什么叫duplicate-key checking.的時候才會使用gap lock?
      • 舉個例子,update test_dup set num=1 where num=50;會引起Duplicate entry '1' for key 'num_index'。此時查看此事務的加鎖。index lock(num=50),index lock(num=1), s next key lock(num=1)。此時執(zhí)行一個插入num=0的事務一定會阻塞。
    • 不要使用 insert on duplicate,使用普通的insert。
      insert會在num_index和pk中加record x locks,而不是gap lock或者next key lock,所以不會有死鎖。
    • 盡量減少在數(shù)據(jù)庫中使用unique index和foreign key
      因為unique key 和foreign key會引起額外的index檢查,需要更大的開銷。
    • mysql版本改為5.6
      這里要特別說一下,mysql5.6是沒有這個問題的,insert into test_dup(num,name) values(41,'no') on duplicate key update name='dcdcdf4738'; 會在最終的num的index上加index lock(num=41),所以不會有死鎖。


    思考

    為什么mysql對于5.6和5.7中的insert on duplicate的加鎖處理會變化如此大?其實如此大的變更我們可以直接去看mysql的release note。在5.7的release note中我們發(fā)現(xiàn)了這樣一條。

    INSERT INTO ... SELECT ... ON DUPLICATE KEY UPDATE?and?LOAD DATA CONCURRENT REPLACE?took too weak a lock, leading to the possibility of concurrent SELECT statements returning inconsistent results. (Bug #38046, Bug #11749055)

    大意就是原來加鎖太弱了,會引起RR隔離級別下的數(shù)據(jù)不一致,所以加強了。好吧。

    總結(jié)

    以上是生活随笔為你收集整理的mysql Insert on duplicate引发的死锁的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。