日B视频 亚洲,啪啪啪网站一区二区,91色情精品久久,日日噜狠狠色综合久,超碰人妻少妇97在线,999青青视频,亚洲一区二卡,让本一区二区视频,日韩网站推荐

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線(xiàn)課程
  • 觀(guān)看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

MySQL死鎖問(wèn)題的排查方法與解決思路

馬哥Linux運(yùn)維 ? 來(lái)源:馬哥Linux運(yùn)維 ? 2026-05-12 09:41 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

問(wèn)題背景

MySQL 死鎖是生產(chǎn)環(huán)境中讓人頭疼的問(wèn)題之一。很多運(yùn)維工程師看到死鎖報(bào)錯(cuò),第一反應(yīng)是重啟數(shù)據(jù)庫(kù)——重啟確實(shí)能解決當(dāng)下的阻塞,但根因還在,下次還會(huì)發(fā)生,而且可能在更糟糕的時(shí)間點(diǎn)爆發(fā)。

死鎖的本質(zhì)是并發(fā)事務(wù)對(duì)資源加鎖順序不一致,導(dǎo)致循環(huán)等待。在 MySQL 的 InnoDB 引擎里,死鎖并不一定是配置問(wèn)題或 SQL 寫(xiě)得不好——高并發(fā)場(chǎng)景下,即使業(yè)務(wù)邏輯合理,也可能出現(xiàn)死鎖。但死鎖頻繁發(fā)生,一定說(shuō)明業(yè)務(wù)邏輯或索引設(shè)計(jì)存在優(yōu)化空間。

本文面向 MySQL 5.7 和 MySQL 8.0,以一線(xiàn)運(yùn)維工程師的角度,系統(tǒng)講解死鎖的形成機(jī)制、排查方法、解決思路和預(yù)防措施。涉及命令、SQL、配置均基于生產(chǎn)環(huán)境常用版本,MySQL 5.7 和 8.0 的差異會(huì)單獨(dú)說(shuō)明。

適用場(chǎng)景

線(xiàn)上頻繁出現(xiàn)死鎖報(bào)錯(cuò),業(yè)務(wù)受阻

監(jiān)控發(fā)現(xiàn) InnoDB Lock wait timeout exceeded 錯(cuò)誤增多

需要分析死鎖日志定位根因

優(yōu)化業(yè)務(wù) SQL 減少死鎖發(fā)生概率

死鎖形成機(jī)制

什么是死鎖

死鎖是兩個(gè)或多個(gè)事務(wù)在執(zhí)行過(guò)程中,因互相持有對(duì)方需要的鎖而形成循環(huán)等待,導(dǎo)致都無(wú)法繼續(xù)執(zhí)行下去。

MySQL InnoDB 的死鎖通常發(fā)生在以下場(chǎng)景:

行鎖沖突:事務(wù) A 鎖住了行 X,事務(wù) B 鎖住了行 Y,然后 A 要鎖 Y、B 要鎖 X,形成循環(huán)

間隙鎖沖突:范圍查詢(xún)導(dǎo)致間隙鎖,多個(gè)事務(wù)對(duì)相交的索引范圍加鎖

唯一索引沖突:多個(gè)事務(wù)同時(shí)插入相同的唯一鍵

鎖升級(jí):行鎖升級(jí)為頁(yè)鎖或表鎖,擴(kuò)大了沖突范圍

InnoDB 鎖的類(lèi)型

理解死鎖要先理解 InnoDB 的鎖機(jī)制:

-- 查看當(dāng)前 InnoDB 鎖信息(MySQL 5.7+)
SELECT*FROMinformation_schema.INNODB_LOCKS;

-- 查看鎖等待關(guān)系
SELECT*FROMinformation_schema.INNODB_LOCK_WAITS;

-- 查看當(dāng)前事務(wù)及持有的鎖
SELECT*FROMinformation_schema.INNODB_TRX;

InnoDB 的鎖分為:

共享鎖(S鎖):允許事務(wù)讀取一行

排他鎖(X鎖):允許事務(wù)更新或刪除一行

記錄鎖(Record Lock):鎖住索引記錄

間隙鎖(Gap Lock):鎖住索引記錄之間的間隙,防止幻讀

Next-Key Lock:記錄鎖 + 間隙鎖的組合

意向鎖(Intention Lock):表級(jí)鎖,表示事務(wù)要在某行加鎖

-- 查看事務(wù)隔離級(jí)別
SELECT@@transaction_isolation;
-- REPEATABLE READ 是最容易產(chǎn)生間隙鎖的隔離級(jí)別

-- 修改隔離級(jí)別(僅當(dāng)前會(huì)話(huà))
SETSESSIONtransaction_isolation ='READ-COMMITTED';

死鎖與鎖等待的區(qū)別

死鎖和普通的鎖等待不是一回事:

鎖等待(Lock wait timeout):事務(wù) A 等事務(wù) B 釋放鎖,超過(guò)innodb_lock_wait_timeout(默認(rèn) 50 秒)后報(bào)錯(cuò)。這是單向等待,不是死鎖。

死鎖(Deadlock):事務(wù) A 等事務(wù) B,B 又等事務(wù) A,MySQL 的死鎖檢測(cè)機(jī)制主動(dòng)回滾其中一個(gè)事務(wù),打破循環(huán)等待。

MySQL 8.0 中,死鎖檢測(cè)默認(rèn)開(kāi)啟,innodb_deadlock_detect默認(rèn) ON。死鎖發(fā)生時(shí),MySQL 會(huì)自動(dòng)回滾代價(jià)最小的事務(wù)(通常是持有最少行級(jí)鎖的事務(wù)),讓其他事務(wù)繼續(xù)執(zhí)行。

排查工具:如何拿到死鎖信息

第一步:開(kāi)啟死鎖日志

MySQL 的死鎖信息會(huì)記錄在 error log 中。確認(rèn)以下配置:

# my.cnf / my.ini
[mysqld]
# 開(kāi)啟死鎖詳細(xì)信息記錄到 error log
innodb_print_all_deadlocks = ON

# 鎖等待超時(shí)時(shí)間(秒),超時(shí)后報(bào)錯(cuò)并回滾
innodb_lock_wait_timeout = 50

# 死鎖檢測(cè)開(kāi)關(guān)(MySQL 8.0+ 默認(rèn) ON,MySQL 5.7 也默認(rèn) ON)
innodb_deadlock_detect = ON
# 修改后重載配置,不重啟數(shù)據(jù)庫(kù)的方式:
# MySQL 8.0+ 支持動(dòng)態(tài)修改部分參數(shù)
SET GLOBAL innodb_print_all_deadlocks = ON;
SET GLOBAL innodb_lock_wait_timeout = 50;

# 確認(rèn)配置生效
SHOW VARIABLES LIKE'innodb_print_all_deadlocks';
SHOW VARIABLES LIKE'innodb_lock_wait_timeout';
SHOW VARIABLES LIKE'innodb_deadlock_detect';

第二步:查看死鎖日志

# 查看 error log 中的死鎖信息
grep -A 50"TRANSACTION"/var/log/mysql/error.log | grep -A 30"DEADLOCK"

# 如果不知道 error log 位置
SHOW VARIABLES LIKE'log_error';

# 查看最近的死鎖記錄(MySQL 5.7+)
-- 需要先執(zhí)行:SET GLOBAL innodb_print_all_deadlocks = ON;
-- 然后觸發(fā)死鎖,查看 errorlog

第三步:分析 performance_schema 中的鎖信息

MySQL 5.7 開(kāi)始,可以通過(guò) performance_schema 獲取更詳細(xì)的鎖信息:

-- 開(kāi)啟鎖監(jiān)控(需要提前在 my.cnf 中配置)
-- performance_schema 需要在啟動(dòng)時(shí)開(kāi)啟

-- 查看當(dāng)前所有鎖信息
SELECT
  l.lock_id,
  l.lock_mode,
  l.lock_type,
  l.lock_table,
  l.lock_index,
  l.lock_space,
  l.lock_page,
  l.lock_rec,
  l.lock_data,
  t.trx_id,
  t.trx_state,
  t.trx_started,
  t.trx_rows_locked,
  t.trx_rows_modified,
  t.trx_is_read_only,
  p.ps_table_name
FROMinformation_schema.INNODB_LOCKS l
JOINinformation_schema.INNODB_TRX tONl.lock_trx_id = t.trx_id
LEFTJOINperformance_schema.metadata_locks pONp.lock_object = l.lock_table;
-- 查看鎖等待圖(誰(shuí)在等誰(shuí)的鎖)
SELECT
  r.trx_idASwaiting_trx_id,
  r.trx_mysql_thread_idASwaiting_thread,
  r.trx_queryASwaiting_query,
  b.trx_idASblocking_trx_id,
  b.trx_mysql_thread_idASblocking_thread,
  b.trx_queryASblocking_query,
  b.trx_startedASblocking_trx_started,
  b.trx_rows_lockedASblocking_trx_rows_locked
FROMinformation_schema.INNODB_LOCK_WAITS w
JOINinformation_schema.INNODB_TRX bONw.blocking_trx_id = b.trx_id
JOINinformation_schema.INNODB_TRX rONw.requesting_trx_id = r.trx_id;

MySQL 8.0 中,information_schema.INNODB_LOCKS和INNODB_LOCK_WAITS被移除了,鎖信息需要從performance_schema.data_locks和performance_schema.data_lock_waits中查詢(xún):

-- MySQL 8.0 鎖信息查詢(xún)
SELECT*FROMperformance_schema.data_locks;

-- MySQL 8.0 鎖等待關(guān)系
SELECT*FROMperformance_schema.data_lock_waits;

-- MySQL 8.0 綜合查看
SELECT
  dl.lock_id,
  dl.lock_mode,
  dl.lock_type,
  dl.lock_status,
  dl.object_name,
  dl.index_name,
  dl.lock_data,
  dt.trx_id,
  dt.trx_state,
  dt.trx_started,
  dt.trx_query
FROMperformance_schema.data_locks dl
JOINperformance_schema.data_locks dtONdl.lock_trx_id = dt.trx_id
WHEREdl.lock_type ='RECORD'ANDdt.trx_state ='RUNNING';

實(shí)戰(zhàn)案例一:行鎖沖突導(dǎo)致的死鎖

現(xiàn)象

某訂單表orders,高并發(fā)下單時(shí)出現(xiàn)死鎖:

ERROR 1213 (40001): Deadlock found when trying to get lock;
try restarting transaction

業(yè)務(wù)日志中看到部分訂單創(chuàng)建失敗,頻率約為千分之一。

排查過(guò)程

查看 error log 中的死鎖記錄:

tail -n 200 /var/log/mysql/error.log | grep -A 50"DEADLOCK"

死鎖日志格式如下:

*** (1) TRANSACTION:
TRANSACTION 98765, ACTIVE 2 sec inserting
mysql thread id 12345, OS thread handle 0x7f8a2c3d4e0, query id 67890 localhost root update
INSERT INTO orders (user_id, product_id, amount, status) VALUES (1001, 2001, 99.00,'pending')

*** (1) HOLDS THE LOCK(S):
RECORD LOCKS space id 123 page no 4 n bits 200 index PRIMARY of table `shop`.`orders` trx id 98765 lock_mode X locks rec but not gap
Record lock, heap no 2 PHYSICAL RECORD: n_fields 5; compact format; info bits 0
0: len 4; hex 80000001; asc  ;; (primary key)
1: len 6; hex 00000001871a; asc   ;; (transaction id)
2: len 6; hex 000000001b5a; asc   ;; (roll ptr)
3: len 4; hex 80000001; asc  ;; (user_id)
4: len 4; hex 800003e8; asc  ;; (amount)

*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 123 page no 4 n bits 200 index PRIMARY of table `shop`.`orders` trx id 98766 lock_mode X locks rec but not gap waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 5; compact format; info bits 0
0: len 4; hex 80000001; asc  ;; (primary key)

*** (2) TRANSACTION:
TRANSACTION 98766, ACTIVE 2 sec inserting
mysql thread id 12346, OS thread handle 0x7f8a2c3d5f1, query id 67891 localhost root update
INSERT INTO orders (user_id, product_id, amount, status) VALUES (1001, 2002, 149.00,'pending')

分析死鎖日志:

從日志可以看出:

事務(wù) 98765 和 98766 都在插入orders表

兩者都持有某條記錄的排他鎖(X locks rec but not gap),并且在等待對(duì)方的鎖grant

這說(shuō)明兩條 INSERT 語(yǔ)句插入的主鍵值相同或產(chǎn)生了相同的自增 ID 沖突

查看當(dāng)前訂單表狀態(tài):

-- 查看訂單表結(jié)構(gòu)
SHOWCREATETABLEordersG

-- 查看最近新增的訂單
SELECT*FROMordersORDERBYidDESCLIMIT10;

-- 查看是否有訂單缺失(連續(xù) ID 不連續(xù)說(shuō)明有回滾)
SELECTid, user_id, created_atFROMordersORDERBYidDESCLIMIT20;

查看當(dāng)時(shí)并發(fā)插入的記錄:

-- 查看特定時(shí)間范圍內(nèi)創(chuàng)建的訂單(根據(jù) error log 中的時(shí)間推算)
SELECTid, user_id, product_id,status, created_at
FROMorders
WHEREcreated_atBETWEEN'2024-01-01 1000'AND'2024-01-01 1000'
ORDERBYidDESC;

根因定位

通過(guò)日志分析,發(fā)現(xiàn)根因是:兩個(gè)并發(fā)事務(wù)同時(shí)對(duì)同一用戶(hù)插入訂單時(shí),由于表上有user_id索引,InnoDB 在 REPEATABLE READ 隔離級(jí)別下對(duì)相同user_id的記錄加了 Next-Key Lock,導(dǎo)致相互等待。

但更直接的根因是:應(yīng)用層的重試邏輯導(dǎo)致相同的 INSERT 被執(zhí)行了兩次,而兩次執(zhí)行在極短時(shí)間內(nèi)同時(shí)到達(dá)。

解決修復(fù)

方案一:應(yīng)用層加分布式鎖(推薦)

importredis
importuuid

defcreate_order(user_id, product_id, amount):
 # 使用 Redis 分布式鎖,防止同一用戶(hù)的并發(fā)下單
  lock_key =f"order{user_id}"
  lock_value = str(uuid.uuid4())
  lock_timeout =5

 ifnotredis.set(lock_key, lock_value, nx=True, ex=lock_timeout):
   raiseException("下單過(guò)于頻繁,請(qǐng)稍后重試")

 try:
   withdb_connection.cursor()ascursor:
      sql ="INSERT INTO orders (user_id, product_id, amount, status) VALUES (%s, %s, %s, 'pending')"
      cursor.execute(sql, (user_id, product_id, amount))
      db_connection.commit()
     returncursor.lastrowid
 finally:
   # 釋放鎖(只釋放自己持有的鎖)
   ifredis.get(lock_key) == lock_value:
      redis.delete(lock_key)

方案二:數(shù)據(jù)庫(kù)層加唯一約束(兜底方案)

-- 給 orders 表加業(yè)務(wù)唯一鍵約束(假設(shè) user_id + product_id 組合唯一)
ALTERTABLEordersADDCONSTRAINTuk_user_product
 UNIQUE(user_id, product_id);

-- 插入時(shí)使用 INSERT ... ON DUPLICATE KEY UPDATE
INSERTINTOorders (user_id, product_id, amount,status)
VALUES(1001,2001,99.00,'pending')
ONDUPLICATEKEYUPDATEamount =VALUES(amount),status='pending';

方案三:降低隔離級(jí)別

如果業(yè)務(wù)允許,可以將隔離級(jí)別從 REPEATABLE READ 降到 READ COMMITTED,減少 Next-Key Lock 的范圍:

-- 當(dāng)前會(huì)話(huà)修改
SETSESSIONtransaction_isolation ='READ-COMMITTED';

-- 全局修改(需要權(quán)限)
SETGLOBALtransaction_isolation ='READ-COMMITTED';
# 寫(xiě)入 my.cnf 永久生效
[mysqld]
transaction-isolation = READ-COMMITTED

方案四:調(diào)整 SQL 寫(xiě)入順序

如果是多個(gè)事務(wù)并發(fā)更新不同行的同一批數(shù)據(jù)產(chǎn)生死鎖,可以讓所有事務(wù)按相同順序操作資源:

-- 原來(lái):事務(wù) A 先鎖用戶(hù)再鎖訂單,事務(wù) B 先鎖訂單再鎖用戶(hù) -> 死鎖
-- 改為:所有事務(wù)都按 user_id 順序加鎖

-- 錯(cuò)誤寫(xiě)法(不同事務(wù)順序不同)
-- 事務(wù) A: UPDATE orders SET status='paid' WHERE id=1; UPDATE users SET balance=balance-100 WHERE id=100;
-- 事務(wù) B: UPDATE users SET balance=balance-100 WHERE id=100; UPDATE orders SET status='paid' WHERE id=1;

-- 正確寫(xiě)法(所有事務(wù)統(tǒng)一先鎖用戶(hù)再鎖訂單)
-- 事務(wù) A: UPDATE users SET balance=balance-100 WHERE id=100; UPDATE orders SET status='paid' WHERE id=1;
-- 事務(wù) B: UPDATE users SET balance=balance-100 WHERE id=100; UPDATE orders SET status='paid' WHERE id=1;

驗(yàn)證方法

-- 1. 檢查死鎖是否解決:連續(xù)觀(guān)察 error log 中是否還有新死鎖
tail -f /var/log/mysql/error.log | grep DEADLOCK

-- 2. 檢查是否有 INSERT 重復(fù)數(shù)據(jù)(唯一約束生效)
SELECTuser_id, product_id,COUNT(*)ascnt
FROMorders
GROUPBYuser_id, product_id
HAVINGcnt >1;

-- 3. 檢查應(yīng)用層重試次數(shù)是否降低
-- 查看應(yīng)用日志中 "Deadlock found" 錯(cuò)誤的出現(xiàn)頻率

-- 4. 壓力測(cè)試驗(yàn)證
-- 用 mysqlslap 或 sysbench 模擬并發(fā)插入
mysqlslap--concurrency=20 --iterations=100 
 --query="INSERT INTO orders (user_id, product_id, amount, status) VALUES (FLOOR(1+RAND()*1000), FLOOR(1+RAND()*1000), 99.00, 'pending')" 
 --create-schema=shop

風(fēng)險(xiǎn)提醒

加唯一約束前,確認(rèn)業(yè)務(wù)邏輯上user_id + product_id確實(shí)應(yīng)該唯一,否則會(huì)導(dǎo)致正常業(yè)務(wù)報(bào) duplicate key 錯(cuò)誤

降低隔離級(jí)別到 READ COMMITTED 會(huì)降低隔離性,可能出現(xiàn)不可重復(fù)讀,需要評(píng)估業(yè)務(wù)是否接受

應(yīng)用層加分布式鎖引入了對(duì) Redis 的依賴(lài),如果 Redis 不可用可能導(dǎo)致下單失敗,需要考慮降級(jí)策略

DELETE + INSERT 混合場(chǎng)景的死鎖比純 INSERT 更復(fù)雜,可能涉及記錄鎖和間隙鎖的組合

實(shí)戰(zhàn)案例二:間隙鎖導(dǎo)致的死鎖

現(xiàn)象

一張商品庫(kù)存表products,多個(gè)事務(wù)同時(shí)執(zhí)行以下語(yǔ)句扣減庫(kù)存:

UPDATEproductsSETstock = stock -1WHEREid=1001ANDstock >0;

在商品庫(kù)存只有 1 個(gè)、同時(shí)有 3 個(gè)請(qǐng)求并發(fā)扣減時(shí),出現(xiàn)了死鎖。只有一個(gè)請(qǐng)求成功,另外兩個(gè)報(bào) deadlock error。

排查過(guò)程

-- 查看 products 表結(jié)構(gòu)和索引
SHOWCREATETABLEproductsG

-- 查看當(dāng)時(shí) products 表的讀操作
SELECT*FROMperformance_schema.events_statements_history
WHERESQL_TEXTLIKE'%products%'
ORDERBYEND_EVENT_IDDESCLIMIT10;

死鎖日志:

*** (1) TRANSACTION:
TRANSACTION 123456, ACTIVE 0 sec updating
mysql thread id 111, OS thread handle 0x7f8a1b2c3d4e, query id 999 localhost root
UPDATE products SET stock = stock - 1 WHERE id = 1001 AND stock > 0
RECORD LOCKS space id 99 page no 3 n bits 100 index PRIMARY of table `shop`.`products` trx id 123456 lock_mode X locks rec but not gap
Record lock, heap no 2 PHYSICALL RECORD: n_fields 5; info bits 0
0: len 4; hex 800003e9; asc  ;;

*** (1) HOLDS THE LOCK(S):
RECORD LOCKS space id 99 page no 3 n bits 100 index PRIMARY of table `shop`.`products` trx id 123456 lock_mode X locks rec but not gap

*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 99 page no 3 n bits 100 index PRIMARY of table `shop`.`products` trx id 123457 lock_mode X locks rec but not gap waiting

*** (2) TRANSACTION:
TRANSACTION 123457, ACTIVE 0 sec updating
mysql thread id 112, OS thread handle 0x7f8a1b2c3d5f, query id 1000 localhost root
UPDATE products SET stock = stock - 1 WHERE id = 1001 AND stock > 0
RECORD LOCKS space id 99 page no 3 n bits 100 index PRIMARY of table `shop`.`products` trx id 123457 lock_mode X locks rec but not gap

根因定位

這個(gè)場(chǎng)景的死鎖原因是:三個(gè)事務(wù)同時(shí)執(zhí)行相同的 UPDATE 語(yǔ)句,在 stock=1 時(shí),三個(gè)事務(wù)都讀到了 stock>0 的記錄。由于 InnoDB 在當(dāng)前讀(current read)時(shí)會(huì)對(duì)讀取的行加鎖,三個(gè)事務(wù)實(shí)際上在競(jìng)爭(zhēng)同一行記錄的 X 鎖。

更具體的根因在于WHERE stock > 0這個(gè)條件——在 REPEATABLE READ 隔離級(jí)別下,范圍查詢(xún)不僅鎖住存在的記錄,還可能鎖住不存在的"間隙"。如果庫(kù)存為 1 時(shí)有兩個(gè)并發(fā)請(qǐng)求,它們可能分別鎖住了不同的間隙區(qū)間。

解決修復(fù)

方案一:使用樂(lè)觀(guān)鎖代替悲觀(guān)鎖

-- 悲觀(guān)鎖:直接 UPDATE,行鎖,可能死鎖
UPDATEproductsSETstock = stock -1WHEREid=1001ANDstock >0;

-- 樂(lè)觀(guān)鎖:通過(guò)版本號(hào)或 stock 值作為條件UPDATE,不鎖定行
UPDATEproducts
SETstock = stock -1,version=version+1
WHEREid=1001ANDstock >0ANDversion=1;
defdeduct_stock(product_id, quantity=1):
 """使用樂(lè)觀(guān)鎖扣減庫(kù)存"""
 withdb_connection.cursor()ascursor:
   # 先檢查庫(kù)存
    cursor.execute(
     "SELECT stock FROM products WHERE id = %s FOR UPDATE",
      (product_id,)
    )
    result = cursor.fetchone()
   ifnotresultorresult['stock'] < quantity:
? ? ? ? ? ??raise?Exception("庫(kù)存不足")

? ? ? ??# 樂(lè)觀(guān)鎖更新:檢查 stock 是否變化
? ? ? ? affected = cursor.execute(
? ? ? ? ? ??"UPDATE products SET stock = stock - %s WHERE id = %s AND stock >= %s",
      (quantity, product_id, quantity)
    )

   ifaffected ==0:
     raiseException("庫(kù)存已變化,請(qǐng)重試")

    db_connection.commit()

方案二:使用 SELECT ... FOR UPDATE 預(yù)鎖定(避免重試)

STARTTRANSACTION;

-- 先鎖定行,再檢查和更新
SELECTstockINTO@current_stock
FROMproducts
WHEREid=1001
FORUPDATE;

IF @current_stock >= 1 THEN
 UPDATEproductsSETstock = stock -1WHEREid=1001;
 COMMIT;
ELSE
 ROLLBACK;
 -- 拋出庫(kù)存不足異常
ENDIF;

方案三:使用 INSERT ... ON DUPLICATE KEY 代替 UPDATE

如果業(yè)務(wù)允許,可以將扣減庫(kù)存改為"預(yù)占庫(kù)存"的思路:

-- 創(chuàng)建庫(kù)存預(yù)占表
CREATETABLEstock_reserve (
 idBIGINTAUTO_INCREMENT PRIMARYKEY,
  product_idINTNOTNULL,
  quantityINTNOTNULLDEFAULT1,
 statusENUM('pending','confirmed','cancelled')DEFAULT'pending',
  created_atTIMESTAMPDEFAULTCURRENT_TIMESTAMP,
 INDEXidx_product_status (product_id,status),
 UNIQUEKEYuk_reserve (product_id,id)
);

-- 預(yù)占庫(kù)存(插入一條記錄)
INSERTINTOstock_reserve (product_id, quantity)
VALUES(1001,1);

-- 確認(rèn)庫(kù)存(更新預(yù)占記錄狀態(tài))
UPDATEstock_reserve, products
SETstock_reserve.status ='confirmed',
  products.stock = products.stock - stock_reserve.quantity
WHEREstock_reserve.id = ??
ANDstock_reserve.product_id = products.id
ANDstock_reserve.status ='pending';

驗(yàn)證方法

-- 1. 檢查死鎖頻率是否降低
SELECT
 TIME,
 COUNT(*)asdeadlock_count
FROMmysql.general_log
WHEREcommand_type ='Query'
ANDargumentLIKE'%Deadlock%'
GROUPBYTIME
ORDERBYTIMEDESCLIMIT20;

-- 2. 檢查庫(kù)存扣減是否準(zhǔn)確
SELECTid, stock,versionFROMproductsWHEREid=1001;

-- 3. 對(duì)比修改前后的庫(kù)存流水
SELECTCOUNT(*)astotal_reserve,SUM(quantity)astotal_quantity
FROMstock_reserve
WHEREproduct_id =1001ANDstatus='confirmed';

-- 4. 壓測(cè)驗(yàn)證
-- 使用 sysbench 模擬并發(fā)扣庫(kù)存,觀(guān)察死鎖頻率

風(fēng)險(xiǎn)提醒

樂(lè)觀(guān)鎖的 retry 邏輯需要應(yīng)用層配合,如果 retry 次數(shù)過(guò)多會(huì)造成用戶(hù)等待,建議設(shè)置最大重試次數(shù)

SELECT ... FOR UPDATE 在高并發(fā)下會(huì)顯著降低吞吐量,因?yàn)樗鼤?huì)鎖定行直到事務(wù)結(jié)束

預(yù)占庫(kù)存方案增加了數(shù)據(jù)量和復(fù)雜度,需要額外的定時(shí)任務(wù)清理 cancelled 狀態(tài)的預(yù)占記錄

實(shí)戰(zhàn)案例三:唯一索引沖突導(dǎo)致的死鎖

現(xiàn)象

用戶(hù)注冊(cè)場(chǎng)景,多個(gè)線(xiàn)程同時(shí)為新用戶(hù)創(chuàng)建賬戶(hù),執(zhí)行相同的 INSERT 語(yǔ)句:

INSERTINTOusers(username, email, password_hash, created_at)
VALUES('john_doe','john@example.com','hashed_pw',NOW());

數(shù)據(jù)庫(kù)表有username和email兩個(gè)唯一索引。高并發(fā)注冊(cè)時(shí),出現(xiàn)死鎖。

排查過(guò)程

死鎖日志分析:

*** (1) TRANSACTION:
TRANSACTION 5555, ACTIVE 0 sec inserting
mysql thread id 88, query id 333 localhost root
INSERT INTO users (username, email, ...) VALUES ('john_doe','john@example.com', ...)
RECORD LOCKS space id 10 page no 3 n bits 100 index username of table `app`.`users` trx id 5555 lock_mode X locks rec but not gap waiting
Record lock, heap no 3 PHYSICAL RECORD: (name='john_doe')

*** (2) TRANSACTION:
TRANSACTION 5556, ACTIVE 0 sec inserting
mysql thread id 89, query id 334 localhost root
INSERT INTO users (username, email, ...) VALUES ('john_doe','john@example.com', ...)
RECORD LOCKS space id 10 page no 3 n bits 100 index username of table `app`.`users` trx id 5556 lock_mode X
Record lock, heap no 3 PHYSICAL RECORD: (name='john_doe')

根因定位

兩個(gè)并發(fā) INSERT 插入相同的唯一鍵值(username='john_doe'),在唯一索引上形成沖突:

事務(wù) A 插入 username='john_doe',在唯一索引上請(qǐng)求排他鎖

事務(wù) B 也插入 username='john_doe',也在唯一索引上請(qǐng)求排他鎖

唯一索引檢查發(fā)現(xiàn)鍵值重復(fù),事務(wù) A 或 B 被通知等待對(duì)方釋放鎖

形成循環(huán)等待——死鎖

MySQL 檢測(cè)到死鎖后會(huì)回滾其中一個(gè)事務(wù),通常是回滾插入較晚的那個(gè)。

解決修復(fù)

方案一:應(yīng)用層做插入前檢查

defregister_user(username, email, password):
 # 先檢查是否已存在
  existing = db.query(
   "SELECT id FROM users WHERE username = %s OR email = %s",
    (username, email)
  ).fetchone()

 ifexisting:
   raiseValueError("用戶(hù)名或郵箱已被注冊(cè)")

 # 插入(理論上可能有并發(fā)重復(fù),但概率極低)
  db.execute(
   "INSERT INTO users (username, email, password_hash) VALUES (%s, %s, %s)",
    (username, email, hash_password(password))
  )
  db.commit()

方案二:使用 INSERT IGNORE 或 ON DUPLICATE KEY(適合冪等場(chǎng)景)

-- INSERT IGNORE:插入沖突時(shí)忽略,不報(bào)錯(cuò)
INSERTIGNOREINTOusers(username, email, password_hash)
VALUES('john_doe','john@example.com','hashed_pw');

-- 檢查是否插入成功
SELECTROW_COUNT();-- 1 = 插入成功,0 = 被忽略(重復(fù))

-- ON DUPLICATE KEY UPDATE:插入沖突時(shí)更新
INSERTINTOusers(username, email, password_hash)
VALUES('john_doe','john@example.com','hashed_pw')
ONDUPLICATEKEYUPDATEemail =VALUES(email), password_hash =VALUES(password_hash);

方案三:使用 REPLACE(刪除舊記錄,插入新記錄)

-- REPLACE:如果唯一鍵沖突,先刪除舊記錄,再插入新記錄
-- 注意:這是 DELETE + INSERT,不是 UPDATE
-- 會(huì)觸發(fā) DELETE 和 INSERT 兩條語(yǔ)句,可能導(dǎo)致自增 ID 不連續(xù)
REPLACEINTOusers(username, email, password_hash)
VALUES('john_doe','john@example.com','hashed_pw');

方案四:分布式鎖(最可靠)

defregister_user_with_lock(username, email, password):
  lock_key =f"user{username}"
  lock_val = str(uuid.uuid4())

 ifnotredis.set(lock_key, lock_val, nx=True, ex=5):
   raiseException("注冊(cè)進(jìn)行中,請(qǐng)稍后")

 try:
   # 檢查是否存在
   ifdb.query("SELECT 1 FROM users WHERE username = %s", (username,)).fetchone():
     raiseValueError("用戶(hù)名已存在")

    db.execute(
     "INSERT INTO users (username, email, password_hash) VALUES (%s, %s, %s)",
      (username, email, hash_password(password))
    )
    db.commit()
 finally:
   ifredis.get(lock_key) == lock_val:
      redis.delete(lock_key)

驗(yàn)證方法

-- 1. 檢查是否有重復(fù)用戶(hù)名(說(shuō)明唯一約束沒(méi)有兜底攔住)
SELECTusername,COUNT(*)FROMusersGROUPBYusernameHAVINGCOUNT(*) >1;

-- 2. 檢查表上唯一約束是否生效
SHOWCREATETABLEusersG

-- 3. 模擬并發(fā)注冊(cè)測(cè)試
-- 啟動(dòng)多個(gè) mysql client 同時(shí)執(zhí)行相同 INSERT
-- 查看只有一個(gè)成功,其余報(bào) deadlock 或 duplicate key error

-- 4. 監(jiān)控死鎖頻率
SHOWGLOBALSTATUSLIKE'Innodb_deadlock%';
-- Innodb_deadlock_graph_writes: 死鎖圖更新次數(shù)
-- Innodb_deadlock_recursive_locks: 遞歸鎖檢測(cè)次數(shù)

風(fēng)險(xiǎn)提醒

REPLACE INTO是 DELETE + INSERT,會(huì)觸發(fā) DELETE 的行鎖和 INSERT 的排他鎖,鎖范圍比普通 INSERT 大,不適合高并發(fā)場(chǎng)景

ON DUPLICATE KEY UPDATE在 MySQL 5.7+ 支持,但注意:如果唯一鍵上有多個(gè)列的組合唯一索引,任意一個(gè)沖突都會(huì)觸發(fā) UPDATE

應(yīng)用層檢查 + INSERT 的組合不是原子操作,并發(fā)時(shí)仍可能兩個(gè)事務(wù)都通過(guò)檢查然后同時(shí)插入。需要配合唯一約束作為最后防線(xiàn)

死鎖排查的核心命令速查

-- 1. 查看當(dāng)前所有事務(wù)及持有的鎖(最常用)
SELECT
  trx_id,
  trx_state,
  trx_started,
  trx_requested_lock_id,
  trx_weight,
  trx_mysql_thread_id,
  trx_query,
  trx_rows_locked,
  trx_rows_modified
FROMinformation_schema.INNODB_TRX;

-- MySQL 8.0
SELECT*FROMperformance_schema.data_locks;
SELECT*FROMperformance_schema.data_lock_waits;

-- 2. 查看當(dāng)前鎖信息
SELECT*FROMinformation_schema.INNODB_LOCKS; -- MySQL 5.7
SELECT*FROMinformation_schema.INNODB_LOCK_WAITS; -- MySQL 5.7

-- 3. 查看當(dāng)前阻塞的 SQL
SELECT
  r.trx_idASwaiting_trx_id,
  r.trx_mysql_thread_idASwaiting_thread,
  r.trx_queryASwaiting_query,
  b.trx_idASblocking_trx_id,
  b.trx_mysql_thread_idASblocking_thread,
  b.trx_queryASblocking_query
FROMinformation_schema.INNODB_LOCK_WAITS w
JOINinformation_schema.INNODB_TRX bONw.blocking_trx_id = b.trx_id
JOINinformation_schema.INNODB_TRX rONw.requesting_trx_id = r.trx_id;

-- 4. 查看死鎖次數(shù)統(tǒng)計(jì)
SHOWGLOBALSTATUSLIKE'Innodb_deadlock%';

-- 5. 查看事務(wù)等待時(shí)間超過(guò)閾值的事件(需要開(kāi)啟 performance_schema)
SELECT*FROMperformance_schema.events_transactions_history
WHERESTATE ='ROLLING BACK'ANDEVENT_NAME ='transaction'
ORDERBYEND_EVENT_IDDESCLIMIT10;

-- 6. 查看當(dāng)前正在執(zhí)行的 SQL(不包括空閑連接)
SELECT
  p.IDASthread_id,
  p.USER,
  p.HOST,
  d.COMMAND,
  t.trx_state,
  t.trx_started,
  t.trx_rows_locked,
 SUBSTRING(SQL.text,1,100)AScurrent_sql
FROMinformation_schema.PROCESSLIST p
LEFTJOINinformation_schema.INNODB_TRX tONp.ID = t.trx_mysql_thread_id
LEFTJOINperformance_schema.events_statements_currentSQLONp.ID = SQL.THREAD_ID
WHEREp.COMMAND !='Sleep'
ORDERBYt.trx_started;

死鎖預(yù)防措施

從 SQL 層面預(yù)防

按固定順序訪(fǎng)問(wèn)數(shù)據(jù)多個(gè)事務(wù)對(duì)多行數(shù)據(jù)進(jìn)行操作時(shí),按主鍵或唯一鍵的順序訪(fǎng)問(wèn),避免反向順序?qū)е滤梨i。

減少事務(wù)持有時(shí)間事務(wù)時(shí)間越長(zhǎng),持有鎖的時(shí)間越長(zhǎng),死鎖概率越高。盡量減少在事務(wù)內(nèi)的網(wǎng)絡(luò) IO 和復(fù)雜計(jì)算。

-- 好的做法:批量操作在事務(wù)內(nèi)一次完成
STARTTRANSACTION;
INSERTINTOorder_items (order_id, product_id, quantity)
VALUES(1,101,2), (1,102,1), (1,103,3);
UPDATEordersSETstatus='confirmed'WHEREid=1;
COMMIT;

-- 不好的做法:事務(wù)時(shí)間過(guò)長(zhǎng)
STARTTRANSACTION;
-- 讀取數(shù)據(jù)到應(yīng)用層處理(中間有網(wǎng)絡(luò)延遲)
items = db.query("SELECT*FROMorder_itemsWHEREorder_id=1");
for item in items:
  # 調(diào)用外部服務(wù)(很慢)
  result = call_external_service(item)
  if result:
    db.execute("UPDATEorder_itemsSETstatus='done'WHEREid=%s", item.id)
COMMIT;

使用合理的索引缺失索引的查詢(xún)會(huì)導(dǎo)致全表掃描和更大的鎖范圍。

-- 檢查查詢(xún)是否走了索引
EXPLAINSELECT*FROMordersWHEREuser_id =1001ANDstatus='pending';
-- 看 type 列,如果是 ALL 說(shuō)明全表掃描,需要加索引

-- 給常用查詢(xún)條件加索引
CREATEINDEXidx_orders_user_statusONorders(user_id,status);

盡量使用主鍵或唯一索引查詢(xún)基于主鍵或唯一索引的等值查詢(xún)只會(huì)鎖定一行,普通索引可能鎖定多行( Next-Key Lock)。

從數(shù)據(jù)庫(kù)配置層面預(yù)防

[mysqld]
# 鎖等待超時(shí)時(shí)間(秒),不要設(shè)置過(guò)長(zhǎng)
innodb_lock_wait_timeout = 10

# 死鎖檢測(cè)開(kāi)關(guān)(默認(rèn) ON,關(guān)閉后用鎖等待超時(shí)兜底)
innodb_deadlock_detect = ON

# 開(kāi)啟死鎖日志
innodb_print_all_deadlocks = ON

# 監(jiān)控鎖信息(performance_schema)
performance-schema-instrument = 'lock%=ON'

# 事務(wù)隔離級(jí)別(高并發(fā)場(chǎng)景可考慮 READ-COMMITTED)
transaction-isolation = REPEATABLE-READ

# InnoDB 表鎖(避免鎖升級(jí)為表鎖)
innodb_table_locks = ON

從應(yīng)用架構(gòu)層面預(yù)防

使用消息隊(duì)列削峰庫(kù)存扣減、余額變動(dòng)等高并發(fā)操作,通過(guò)消息隊(duì)列串行化處理,完全避免數(shù)據(jù)庫(kù)層面的并發(fā)沖突。

應(yīng)用層加分布式鎖在數(shù)據(jù)庫(kù)操作前通過(guò) Redis/ZooKeeper 分布式鎖限制并發(fā)度。

讀寫(xiě)分離讀操作走從庫(kù),寫(xiě)操作走主庫(kù),減少主庫(kù)的鎖沖突。

分庫(kù)分表將熱點(diǎn)數(shù)據(jù)按用戶(hù) ID 或訂單 ID 拆分到不同庫(kù)表,從物理上減少鎖沖突概率。

常見(jiàn)誤區(qū)與避坑指南

誤區(qū)一:死鎖了就是數(shù)據(jù)庫(kù)的問(wèn)題

死鎖是業(yè)務(wù)邏輯和數(shù)據(jù)庫(kù)交互模式共同造成的。單純重啟數(shù)據(jù)庫(kù)或增加數(shù)據(jù)庫(kù)資源不能解決根本問(wèn)題。需要分析死鎖日志,找出是哪些 SQL 之間的鎖沖突導(dǎo)致的。

誤區(qū)二:加大innodb_lock_wait_timeout就不會(huì)超時(shí)

這個(gè)參數(shù)只是讓鎖等待的錯(cuò)誤晚一點(diǎn)出現(xiàn),不會(huì)減少死鎖的發(fā)生次數(shù)。相反,設(shè)置過(guò)長(zhǎng)會(huì)讓事務(wù)長(zhǎng)時(shí)間占用連接資源,反而加劇資源緊張。

誤區(qū)三:把 REPEATABLE READ 改成 READ COMMITTED 就不會(huì)死鎖

降低隔離級(jí)別確實(shí)會(huì)減少 Next-Key Lock 的范圍,但不能完全消除死鎖。行鎖沖突在任意隔離級(jí)別下都可能發(fā)生。調(diào)整隔離級(jí)別前要評(píng)估對(duì)業(yè)務(wù)一致性的影響。

誤區(qū)四:大事務(wù)不會(huì)有問(wèn)題

大事務(wù)持有鎖的時(shí)間長(zhǎng),鎖定范圍大,是死鎖的高發(fā)場(chǎng)景。應(yīng)該拆分為多個(gè)小事務(wù),避免在單個(gè)事務(wù)中處理過(guò)多數(shù)據(jù)。

誤區(qū)五:加了唯一約束就不會(huì)重復(fù)插入

唯一約束只保證數(shù)據(jù)庫(kù)層面的約束,應(yīng)用層仍可能出現(xiàn)并發(fā)檢查通過(guò)后同時(shí)插入的情況。唯一約束是兜底方案,不是預(yù)防方案。

總結(jié)

MySQL 死鎖的排查與解決,核心在于三點(diǎn):

第一,讀懂死鎖日志。innodb_print_all_deadlocks = ON開(kāi)啟后,error log 中的死鎖記錄包含了兩個(gè)事務(wù)持有的鎖和等待的鎖,這是定位根因的第一手材料。重點(diǎn)看HOLDS THE LOCK(S)和WAITING FOR THIS LOCK TO BE GRANTED部分,判斷是行鎖沖突、間隙鎖沖突還是唯一索引沖突。

第二,理解 InnoDB 鎖機(jī)制。Next-Key Lock 在 REPEATABLE READ 隔離級(jí)別下會(huì)鎖定索引范圍,而不僅僅是命中的行。理解這一點(diǎn),就能明白為什么范圍查詢(xún)比等值查詢(xún)更容易引發(fā)死鎖。

第三,從業(yè)務(wù)邏輯和 SQL 兩層共同優(yōu)化。單純改 SQL 不能解決所有問(wèn)題——如果應(yīng)用層并發(fā)模式不合理,即使 SQL 再優(yōu)化也可能產(chǎn)生死鎖。分布式鎖、消息隊(duì)列、樂(lè)觀(guān)鎖等架構(gòu)層面的手段往往比單純調(diào)整 SQL 更有效。

死鎖是 MySQL 高并發(fā)下的正?,F(xiàn)象,完全消除死鎖幾乎不可能。目標(biāo)是將死鎖頻率控制在可接受范圍內(nèi),并確保死鎖發(fā)生時(shí)業(yè)務(wù)能夠正確處理(重試、冪等)。不要一遇到死鎖就重啟數(shù)據(jù)庫(kù),那只是在掩蓋問(wèn)題,不是在解決問(wèn)題。

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀(guān)點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 死鎖
    +關(guān)注

    關(guān)注

    0

    文章

    26

    瀏覽量

    8335
  • MySQL
    +關(guān)注

    關(guān)注

    1

    文章

    937

    瀏覽量

    29815

原文標(biāo)題:MySQL 死鎖問(wèn)題的排查與解決:別只會(huì)重啟服務(wù)

文章出處:【微信號(hào):magedu-Linux,微信公眾號(hào):馬哥Linux運(yùn)維】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評(píng)論

    相關(guān)推薦
    熱點(diǎn)推薦

    Linux系統(tǒng)CPU占用率100%的排查思路

    今天浩道跟大家分享linux硬核干貨,工作中當(dāng)你服務(wù)器CPU達(dá)到100%時(shí),干著急是沒(méi)有用的,該查問(wèn)題還得自己去查。本文將給大家羅列排查異常故障思路,并且文末附上相關(guān)shell腳本,去實(shí)際一番,你會(huì)發(fā)現(xiàn)原來(lái)解決問(wèn)題的方法如此之簡(jiǎn)
    的頭像 發(fā)表于 01-23 10:26 ?7537次閱讀
    Linux系統(tǒng)CPU占用率100%的<b class='flag-5'>排查</b><b class='flag-5'>思路</b>

    科普小課堂|LCD 問(wèn)題排查思路解析

    在嵌入式系統(tǒng)開(kāi)發(fā)中,液晶顯示器(LCD)作為人機(jī)交互的重要界面,其穩(wěn)定性和可靠性至關(guān)重要。然而在實(shí)際應(yīng)用中,LCD難免會(huì)遇到各種問(wèn)題。今天和各位小伙伴分享一些常見(jiàn)的LCD問(wèn)題及其排查思路,幫助大家快速定位并解決這些問(wèn)題。
    的頭像 發(fā)表于 08-05 17:06 ?2756次閱讀
    科普小課堂|LCD 問(wèn)題<b class='flag-5'>排查</b><b class='flag-5'>思路</b>解析

    怎樣去設(shè)計(jì)一個(gè)死鎖計(jì)算機(jī)系統(tǒng)

    一、概述上一篇講述了“程序崩潰檢測(cè)”功能,感興趣同學(xué)可以 點(diǎn)擊這里進(jìn)行回顧。此篇我們開(kāi)始設(shè)計(jì)第二功能模塊“進(jìn)程死鎖監(jiān)測(cè)”。這里借鑒了博主“程序喵大人”的設(shè)計(jì)思路,利用“gdb+python腳本
    發(fā)表于 12-21 08:16

    RS-485 總線(xiàn)的死鎖檢測(cè)與解除

    針對(duì)RS-485 接口收發(fā)電路的特點(diǎn),討論RS-485 總線(xiàn)在Polling 和CSMA/CD 通信方式中死鎖檢測(cè)和解除死鎖方法。該方法同樣適用于RS-422 接口。
    發(fā)表于 05-13 16:12 ?21次下載

    基于排序的避免死鎖方法

    針對(duì)多數(shù)據(jù)庫(kù)事務(wù)下批量更新記錄時(shí)產(chǎn)生死鎖的問(wèn)題,提出了一種新的數(shù)據(jù)更新方法。這種處理方法采用預(yù)先對(duì)要批量更新的記錄進(jìn)行排序,使所有的記錄都能按某一個(gè)固定的順
    發(fā)表于 12-30 13:04 ?9次下載

    MySQL死鎖原因排查技巧詳解

    在查詢(xún)相關(guān)資料和咨詢(xún)jameszhou后,知道了這個(gè)實(shí)際和innodb 引擎的寫(xiě)機(jī)制有關(guān),innodb執(zhí)行寫(xiě)事務(wù)操作時(shí),實(shí)際是先取得索引中該行的行鎖(即使該表上沒(méi)有任何索引,那么innodb會(huì)在后臺(tái)創(chuàng)建一個(gè)隱藏的聚集主鍵索引),再在緩存里寫(xiě)入,最后事務(wù)commit后正式寫(xiě)入DB中并釋放鎖。
    發(fā)表于 10-19 16:38 ?5209次閱讀
    <b class='flag-5'>MySQL</b><b class='flag-5'>死鎖</b>原因<b class='flag-5'>排查</b>技巧詳解

    建立一個(gè)方法和套路來(lái)對(duì) Load 高問(wèn)題排查

    講解 Linux Load 高如何排查的話(huà)題屬于老生常談了,但多數(shù)文章只是聚焦了幾個(gè)點(diǎn),缺少整體排查思路的介紹。所謂 “授人以魚(yú)不如授人以漁"。本文試圖建立一個(gè)方法和套路,來(lái)幫助讀者對(duì)
    的頭像 發(fā)表于 12-28 14:18 ?6350次閱讀
    建立一個(gè)<b class='flag-5'>方法</b>和套路來(lái)對(duì) Load 高問(wèn)題<b class='flag-5'>排查</b>

    嵌入式系統(tǒng)死鎖檢測(cè)方法

    棘手。死鎖是并發(fā)缺陷的典型問(wèn)題,有時(shí)會(huì)導(dǎo)致整個(gè)嵌入式系統(tǒng)陷入癱瘓,嚴(yán)重影響嵌入式系統(tǒng)的穩(wěn)定性、可靠性。由于死鎖難以再現(xiàn)和修正,如何有效檢測(cè)死鎖成為嵌入式軟件領(lǐng)域的研究重點(diǎn)。 目前死鎖
    發(fā)表于 01-31 10:27 ?0次下載
    嵌入式系統(tǒng)<b class='flag-5'>死鎖</b>檢測(cè)<b class='flag-5'>方法</b>

    網(wǎng)絡(luò)故障排查思路和處理方法

    網(wǎng)絡(luò)故障是最容易出現(xiàn)的,且難以解決的問(wèn)題。本文提供的網(wǎng)絡(luò)故障排查思路和處理方法,可解決日常工作中大部分網(wǎng)絡(luò)問(wèn)題。
    發(fā)表于 10-31 09:14 ?1.4w次閱讀

    網(wǎng)絡(luò)二層環(huán)路的排查思路與技巧

    今天浩道跟大家分享一篇關(guān)于網(wǎng)絡(luò)二層環(huán)路的排查思路與技巧,希望能夠在日常工作中幫助到你,得到老板的雞腿大餐!
    的頭像 發(fā)表于 12-29 15:38 ?5159次閱讀

    Linux內(nèi)核死鎖lockdep功能

    的編程思路,也不可能避免會(huì)發(fā)生死鎖。在Linux內(nèi)核中,常見(jiàn)的死鎖有如下兩種: 遞歸死鎖:如在中斷延遲操作中使用了鎖,和外面的鎖構(gòu)成了遞歸死鎖
    的頭像 發(fā)表于 09-27 15:13 ?1627次閱讀
    Linux內(nèi)核<b class='flag-5'>死鎖</b>lockdep功能

    淺談MySQL常見(jiàn)死鎖場(chǎng)景

    這里問(wèn)題的原因是這個(gè) table 里面只有record 2, 所以這里認(rèn)真看, 死鎖的時(shí)候是等待在 supremum 上的, 因?yàn)閟upremum 的特殊性, supremum 沒(méi)有g(shù)ap lock, 只有 next-key lock
    的頭像 發(fā)表于 03-21 14:10 ?1682次閱讀
    淺談<b class='flag-5'>MySQL</b>常見(jiàn)<b class='flag-5'>死鎖</b>場(chǎng)景

    MySQL磁盤(pán)空間問(wèn)題的成因和排查方法

    運(yùn)維工程師經(jīng)常會(huì)遇到這樣的場(chǎng)景:MySQL 服務(wù)器的磁盤(pán)空間告警,但查看數(shù)據(jù)目錄時(shí)發(fā)現(xiàn)數(shù)據(jù)庫(kù)本身并不大。大量磁盤(pán)空間被未知文件消耗。通過(guò)排查發(fā)現(xiàn),二進(jìn)制日志(Binary Log)是主要的磁盤(pán)空間消耗者。
    的頭像 發(fā)表于 04-13 13:57 ?223次閱讀

    如何排查和解決MySQL死鎖問(wèn)題

    MySQL死鎖是數(shù)據(jù)庫(kù)運(yùn)維和后端開(kāi)發(fā)中最棘手的問(wèn)題之一。與普通查詢(xún)超時(shí)不同,死鎖意味著兩個(gè)或多個(gè)事務(wù)相互持有對(duì)方需要的鎖,形成循環(huán)依賴(lài),導(dǎo)致涉及的表或行無(wú)法被任何事務(wù)繼續(xù)修改。業(yè)務(wù)系統(tǒng)一旦出現(xiàn)
    的頭像 發(fā)表于 05-06 11:18 ?351次閱讀

    服務(wù)器負(fù)載過(guò)高的系統(tǒng)性排查方法

    本文以這次故障的完整排查過(guò)程為線(xiàn)索,展示服務(wù)器負(fù)載過(guò)高的系統(tǒng)性排查方法。文章以第一人稱(chēng)敘事展開(kāi),每步都給出實(shí)際命令輸出和決策思路。
    的頭像 發(fā)表于 05-08 14:26 ?167次閱讀
    淮北市| 平度市| 略阳县| 吴桥县| 克什克腾旗| 策勒县| 东乌| 桓仁| 迭部县| 广西| 黎川县| 东乡县| 隆回县| 库车县| 元江| 柘城县| 六枝特区| 泾川县| 阳新县| 正定县| 南充市| 正安县| 五峰| 洮南市| 北安市| 松溪县| 博罗县| 新疆| 修文县| 榆社县| 张家口市| 休宁县| 大方县| 饶阳县| 永川市| 星子县| 内乡县| 思南县| 同仁县| 星子县| 张家界市|