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

歡迎訪問 生活随笔!

生活随笔

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

数据库

mysql insert on duplicate_一条Insert on duplicate引发的血案

發布時間:2023/12/18 数据库 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 mysql insert on duplicate_一条Insert on duplicate引发的血案 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

今天濤哥跟我說mysql死鎖了,問我怎么回事,我對于mysql的自我感覺一直很良好,覺得不會有啥大問題,結果真的把我難住了。

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;

已有數據

11hi

22dcdcdf

303dcdcdf2

335043494

3420dcdcdf473894739

58200dcdcdf

mysql的版本和隔離級別

Repeatable read

mysql 5.7.17.

濤哥的操作

多個線程,每個線程執行許多這樣的操作,但是保證每個線程執行的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。

死鎖重現第一個事務執行

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

成功

第二個事務執行

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

事務陷入等待

第一個事務執行

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

顯示死鎖。

mysql可以打開鎖統計,通過以下語句打開mysql的鎖的統計

set GLOBAL innodb_status_output_locks=ON;

set GLOBAL innodb_status_output=ON;

在第二步的時候我們運行show engine innodb status;查看鎖的情況發現:

事務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鎖)。

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

原因分析第二個事務在獲取insert intention lock之前先獲取了gap lock,導致第一個事務也不能獲取insert intention lock。gap lock不是被第一個事務獲取了么?是互斥鎖呀?因為mysql的gap鎖是兼容的,與互斥還是只讀無關。所以第二個gap鎖才能獲取。

為什么insert intention lock的獲取在gap lock獲取之后,導致悲劇的發生。如果intention insert lock在之前那么就不會有死鎖了。這可能是mysql5.7的一個bug吧.

解決方案單線程執行數據庫寫入

減小事務的大小

修改事務隔離級別為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)。此時執行一個插入num=0的事務一定會阻塞。

不要使用 insert on duplicate,使用普通的insert。

insert會在num_index和pk中加record x locks,而不是gap lock或者next key lock,所以不會有死鎖。

盡量減少在數據庫中使用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中我們發現了這樣一條。

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隔離級別下的數據不一致,所以加強了。好吧。

總結

以上是生活随笔為你收集整理的mysql insert on duplicate_一条Insert on duplicate引发的血案的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。