記一次mysql的update死鎖

2019.11.20 賬戶系統死鎖問題排查分析。

原文入口: http://www.qianshan.tech

問題出現

項目出現死鎖告警,日誌中出現報錯:

Error updating database. Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction

聯繫dba查看死鎖日誌如下:
死鎖日誌

mysql查看死鎖日誌命令: show engine innodb status;

背景

對業務邏輯不做贅述,簡單理解爲賬戶交易採用TCC的模式,先凍結,再確認入賬或撤銷。
對應的有三個事務,

  • 請求處理事務: 鎖定賬戶、請求記錄入庫、更新賬戶(凍結金額+ 可用餘額 -)
  • 入賬確認事務: 鎖定請求記錄、鎖定賬戶、更新賬戶(凍結金額-)
  • 入賬取消事務: 鎖定請求記錄、鎖定賬戶、更新賬戶(凍結金額- 可用餘額 +)

對應主要有兩張表:賬戶主體表(account)和請求記錄表(trans_log),因爲問題主要出在賬戶表操作上,在此只貼出賬戶表關鍵字段。如下

CREATE TABLE `account` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主鍵字段',
  `account_no` varchar(32) NOT NULL COMMENT '賬戶號',
  `customer_no` varchar(32) NOT NULL COMMENT '客戶號',
  `balance` decimal(15,2) NOT NULL DEFAULT '0.00' COMMENT '有效餘額',
  `frozen_amount` decimal(15,2) NOT NULL DEFAULT '0.00' COMMENT '凍結餘額',
  `status` varchar(32) NOT NULL COMMENT '賬戶狀態(正常:NORMAL 凍結:FROZEN 失效:DISABLED)',
  `account_type` varchar(32) NOT NULL COMMENT '賬戶類型碼',
  PRIMARY KEY (`id`) COMMENT '主鍵',
  UNIQUE KEY `uk_account_no` (`account_no`),
  UNIQUE KEY `uk_customer_acctype` (`customer_no`,`account_type`)
) ENGINE=InnoDB AUTO_INCREMENT=4491 DEFAULT CHARSET=utf8mb4 COMMENT='賬戶主體表';

INSERT INTO `account`(`id`,`account_no`,`customer_no`,`balance`, `frozen_amount`, `status`, `account_type`) 
VALUES (1, '888888','123456',10.00, 0.00, 'NORMAL', 'BALANCE');

賬戶表有兩個唯一索引,一是根據賬戶號索引,二是根據客戶id和客戶類型聯合索引。

請求處理事務入賬確認事務

問題排查

檢測到死鎖拋出異常的地方在請求處理事務的update的時候,根據死鎖日誌查看引起死鎖的是PRIMARY主鍵索引和uk_uk_account_no索引。(雖然insert動作也會有加鎖動作,但根據死鎖日誌排除,以下對insert不用關注)

採用以下時序復現了死鎖場景:
導致死鎖的事務執行時序

時序分析:

  1. 請求處理事務: 先根據uk_customer_acctype索引進行鎖定,實際上鎖定了uk_customer_acctype索引簇記錄和主鍵索引簇上對應的記錄。
  2. 入賬確認事務: 根據唯一索引uk_account_no鎖定,先對uk_account_no索引簇記錄加X鎖成功,對主鍵記錄上鎖時阻塞等待
  3. 請求處理事務: update更新時因爲是根據account_no條件進行更新,所以嘗試對uk_account_no索引簇加鎖,而對應的鎖已經被入賬確認事務佔用,形成死鎖環。
    死鎖環

總結

注意for update對二級索引(非聚簇索引)加X鎖時,也會同時對主鍵索引(聚簇索引)記錄加鎖。

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章