一則由于索引導(dǎo)致的MySQL死鎖分析

涉及死鎖的 authorized_user 表的 DDL

CREATE TABLE `authorized_user` (
  `id`          INT(11) UNSIGNED NOT NULL AUTO_INCREMENT
  COMMENT '自增id',
  `username`    VARCHAR(50)      NOT NULL
  COMMENT '用戶名',
  `password`    VARCHAR(200)     NOT NULL
  COMMENT '加密后的密碼',
  `description` VARCHAR(500)     NOT NULL DEFAULT ''
  COMMENT '描述',
  `status`      TINYINT(4)       NOT NULL DEFAULT 0
  COMMENT '狀態(tài),0:已注冊(cè)令野,1:已激活焰枢,3:已鎖定丢郊,4:已注銷',
  `phone_no`    VARCHAR(15)      NOT NULL DEFAULT ''
  COMMENT '手機(jī)號(hào)',
  `email`       VARCHAR(75)      NOT NULL DEFAULT ''
  COMMENT '電子郵件',
  `create_time` TIMESTAMP        NOT NULL DEFAULT CURRENT_TIMESTAMP
  COMMENT '創(chuàng)建時(shí)間',
  `update_time` TIMESTAMP        NOT NULL DEFAULT CURRENT_TIMESTAMP
  ON UPDATE CURRENT_TIMESTAMP
  COMMENT '最近修改時(shí)間',
  PRIMARY KEY (`id`),
  KEY `index_username` (`username`)
)
  ENGINE = InnoDB
  DEFAULT CHARSET = utf8
  COMMENT = '用戶表';

死鎖日志

2016-11-27 15:57:13 7fe166a92700InnoDB: transactions deadlock detected, dumping detailed information.
2016-11-27 15:57:13 7fe166a92700
*** (1) TRANSACTION:
TRANSACTION 80660631141, ACTIVE 0 sec fetching rows
mysql tables in use 1, locked 1
LOCK WAIT 7 lock struct(s), heap size 2936, 8 row lock(s)
MySQL thread id 126366554, OS thread handle 0x7fe18143e700, query id 2011631849 172.18.3.148 acc Searching rows for update
UPDATE authorized_user SET status = 1 WHERE username = 'wcy'
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 669 page no 937 n bits 136 index `PRIMARY` of table `test`.`authorized_user` trx table locks 1 total table locks 2  trx id 80660631141 lock_mode X locks rec but not gap waiting lock hold time 0 wait time before grant 0 
*** (2) TRANSACTION:
TRANSACTION 80660631143, ACTIVE 0 sec updating or deleting, thread declared inside InnoDB 4999
mysql tables in use 1, locked 1
4 lock struct(s), heap size 1184, 3 row lock(s), undo log entries 1
MySQL thread id 126366547, OS thread handle 0x7fe166a92700, query id 2011631851 172.18.3.154 acc updating
UPDATE authorized_user SET username = 'wcy100' WHERE id = 1
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 669 page no 937 n bits 136 index `PRIMARY` of table `test`.`authorized_user` trx table locks 1 total table locks 2  trx id 80660631143 lock_mode X locks rec but not gap lock hold time 0 wait time before grant 0 
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 669 page no 13359 n bits 1176 index `index_username` of table `test`.`authorized_user` trx table locks 1 total table locks 2  trx id 80660631143 lock_mode X locks rec but not gap waiting lock hold time 0 wait time before grant 0 
*** WE ROLL BACK TRANSACTION (2)

根據(jù) MySQL 日志分析出來的涉及死鎖的 SQL 語句分析

mysql> explain UPDATE authorized_user SET status = 1 WHERE username = 'wcy' \G
*************************** 1. row ***************************
           id: 1
  select_type: UPDATE
        table: authorized_user
   partitions: NULL
         type: range
possible_keys: index_username
          key: index_username
      key_len: 152
          ref: const
         rows: 1
     filtered: 100.00
        Extra: Using where
1 row in set (0.00 sec)
mysql> explain UPDATE authorized_user SET username = 'wcy100' WHERE id = 1 \G
*************************** 1. row ***************************
           id: 1
  select_type: UPDATE
        table: authorized_user
   partitions: NULL
         type: range
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 4
          ref: const
         rows: 1
     filtered: 100.00
        Extra: Using where
1 row in set (0.00 sec)

原因分析

InnoDB 引擎,更新操作默認(rèn)會(huì)加行級(jí)鎖医咨,行級(jí)鎖會(huì)對(duì)索引加鎖枫匾。如果更新語句使用多個(gè)索引,行鎖會(huì)先鎖非聚簇索引拟淮,再鎖聚簇索引干茉。如果兩個(gè)事務(wù)中的 SQL 用到了不同的非聚簇索引或者一個(gè)用了一個(gè)沒有使用(即使用索引的情況不同),這樣的話就會(huì)導(dǎo)致這兩個(gè)事務(wù)加行鎖的順序不一致很泊,形成了多個(gè)事務(wù)之間資源(行鎖)的循環(huán)等待角虫,構(gòu)成了死鎖的四個(gè)必要條件之一,而其他3個(gè)條件(互斥委造、請(qǐng)求與保持戳鹅、不剝奪)已經(jīng)滿足,所以最終導(dǎo)致了死鎖昏兆。

具體分析(以死鎖日志1為例)

由日志分析可得:

事務(wù)1用到的 SQL 語句:
UPDATE authorized_user SET status = 1 WHERE username = 'wcy'
事務(wù)1獲取的鎖:X lock on (669,13359,1176) index_username
事務(wù)1等待的鎖:X lock on (669,937,136) PRIMARY

事務(wù)2用到的 SQL 語句:
UPDATE authorized_user SET username = 'wcy100' WHERE id = 1
事務(wù)2獲取的鎖:X lock on (669,937,136) PRIMARY
事務(wù)2等待的鎖:X lock on (669,13359,1176) index_username

導(dǎo)致這種情況的 SQL 執(zhí)行流程說明:

事務(wù)1的 update 語句正常的執(zhí)行步驟如下:
1.由于用到了非聚簇索引枫虏,首先需要獲取 index_username 上的行級(jí)鎖。
2.緊接著根據(jù)主鍵進(jìn)行更新爬虱,所以需要獲取 PRIMAEY 上的行級(jí)鎖隶债。
3.更新完畢后,提交跑筝,并釋放所有鎖死讹。

但是,如果在步驟1和2之間突然插入事務(wù)2的這條 SQL 語句:
UPDATE authorized_user SET username = 'wcy100' WHERE id = 1, 這條語句會(huì)先鎖住聚簇索引曲梗,然后由于其更新的字段 username 有非聚簇索引赞警,所以這條語句需要鎖住 index_username。

這時(shí)候的情況變成了:事務(wù)1獲取了 index_username 上的鎖虏两,等待 PRIMARY 上的鎖愧旦;事務(wù)2獲取了 PRIMARY 上的鎖,等待 index_username 上的鎖碘举,這樣就出現(xiàn)了死鎖忘瓦。

解決方案

可以將原先的單條批量 update 語句分拆成如下步驟(此處以事務(wù)1的 SQL 語句為例):

1.先獲取需要更新的記錄的主鍵

select id from authorized_user WHERE username = 'wcy'

2.開啟一個(gè)事務(wù),逐條更新

for (Integer id : ids) {    
    updateStatusById(id,1);
}

這樣每一次更新操作都是針對(duì)單條記錄先獲取 PRIMARY 上的鎖,再獲取 index_username 上的鎖耕皮,避免了由于在獲取行鎖的時(shí)候境蜕,鎖索引的順序不一致造成的死鎖。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末凌停,一起剝皮案震驚了整個(gè)濱河市粱年,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌罚拟,老刑警劉巖台诗,帶你破解...
    沈念sama閱讀 211,423評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異赐俗,居然都是意外死亡拉队,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,147評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門阻逮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來粱快,“玉大人,你說我怎么就攤上這事叔扼∈驴蓿” “怎么了?”我有些...
    開封第一講書人閱讀 157,019評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵瓜富,是天一觀的道長鳍咱。 經(jīng)常有香客問我,道長与柑,這世上最難降的妖魔是什么谤辜? 我笑而不...
    開封第一講書人閱讀 56,443評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮仅胞,結(jié)果婚禮上每辟,老公的妹妹穿的比我還像新娘。我一直安慰自己干旧,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,535評(píng)論 6 385
  • 文/花漫 我一把揭開白布妹蔽。 她就那樣靜靜地躺著椎眯,像睡著了一般。 火紅的嫁衣襯著肌膚如雪胳岂。 梳的紋絲不亂的頭發(fā)上编整,一...
    開封第一講書人閱讀 49,798評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音乳丰,去河邊找鬼掌测。 笑死,一個(gè)胖子當(dāng)著我的面吹牛产园,可吹牛的內(nèi)容都是我干的汞斧。 我是一名探鬼主播夜郁,決...
    沈念sama閱讀 38,941評(píng)論 3 407
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼粘勒!你這毒婦竟也來了竞端?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,704評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤庙睡,失蹤者是張志新(化名)和其女友劉穎事富,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體乘陪,經(jīng)...
    沈念sama閱讀 44,152評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡统台,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,494評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了啡邑。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片饺谬。...
    茶點(diǎn)故事閱讀 38,629評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖谣拣,靈堂內(nèi)的尸體忽然破棺而出募寨,到底是詐尸還是另有隱情,我是刑警寧澤森缠,帶...
    沈念sama閱讀 34,295評(píng)論 4 329
  • 正文 年R本政府宣布拔鹰,位于F島的核電站,受9級(jí)特大地震影響贵涵,放射性物質(zhì)發(fā)生泄漏列肢。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,901評(píng)論 3 313
  • 文/蒙蒙 一宾茂、第九天 我趴在偏房一處隱蔽的房頂上張望瓷马。 院中可真熱鬧,春花似錦跨晴、人聲如沸欧聘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽怀骤。三九已至,卻和暖如春焕妙,著一層夾襖步出監(jiān)牢的瞬間蒋伦,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,978評(píng)論 1 266
  • 我被黑心中介騙來泰國打工焚鹊, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留痕届,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,333評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像研叫,于是被迫代替她去往敵國和親锤窑。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,499評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • 當(dāng)一個(gè)系統(tǒng)訪問量上來的時(shí)候蓝撇,不只是數(shù)據(jù)庫性能瓶頸問題了果复,數(shù)據(jù)庫數(shù)據(jù)安全也會(huì)浮現(xiàn),這時(shí)候合理使用數(shù)據(jù)庫鎖機(jī)制就顯得異...
    初來的雨天閱讀 3,560評(píng)論 0 22
  • 1背景1 1.1MVCC:Snapshot Read vs Current Read2 1.2Cluster In...
    簡小鹿奔跑ing閱讀 4,145評(píng)論 1 50
  • MySQL 加鎖處理分析 轉(zhuǎn)載2013年12月13日 16:43:55 7598 原文地址:http://hede...
    初來的雨天閱讀 445評(píng)論 0 2
  • MySQL 的加鎖處理分析 MySQL/InnoDB的加鎖分析渤昌,一直是一個(gè)比較困難的話題虽抄。我在工作過程中,經(jīng)常會(huì)有...
    meng_philip123閱讀 794評(píng)論 0 12
  • 最近独柑,常常做夢迈窟,夢里有他,有她忌栅,有他們车酣。欣喜若狂的夢里行走自己的另一個(gè)故事,那種感覺真好索绪。說不出湖员,道不明。每...
    我是葉子閱讀 760評(píng)論 4 1