最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

MySQL鎖等待超時錯誤詳細(xì)解釋原因和解決方案

 更新時間:2025年12月22日 11:47:52   作者:動亦定  
鎖等待超時是指在一個事務(wù)嘗試獲取某個資源上的鎖時,如果等待的時間超過了預(yù)設(shè)的閾值,MySQL將返回一個錯誤,這篇文章主要介紹了MySQL鎖等待超時錯誤詳細(xì)解釋原因和解決方案,需要的朋友可以參考下

這是一個典型的 MySQL 鎖等待超時 錯誤。下面為您詳細(xì)解釋原因和解決方案。

核心原因

簡單來說:有一個事務(wù)正在長時間鎖定這條SQL要操作的數(shù)據(jù)行,導(dǎo)致當(dāng)前事務(wù)一直等待鎖釋放,最終超過了MySQL的最大等待時間(innodb_lock_wait_timeout,默認(rèn)50秒),從而失敗回滾。

詳細(xì)原因分析

  1. 鎖競爭:SQL語句是一個復(fù)雜的操作。
  2. 阻塞事務(wù):在這個事務(wù)開始之前,很可能已經(jīng)存在另一個未提交的事務(wù)(比如一個長時間的查詢、更新或插入操作),這個“前輩”事務(wù)已經(jīng)鎖定了您SQL語句中想要更新的部分或全部數(shù)據(jù)行。
  3. 等待與超時:這個事務(wù)(報錯的事務(wù))因為拿不到鎖,只能進(jìn)入等待隊列。在MySQL默認(rèn)的50秒內(nèi),如果那個“阻塞事務(wù)”一直沒有提交或回滾,您的這個事務(wù)就會因超時而失敗,拋出 CannotAcquireLockException。

解決方案

1. 立即處理(治標(biāo))

  • 重啟事務(wù):正如錯誤信息提示的 try restarting transaction,最簡單的方法就是讓您的應(yīng)用程序自動或手動重試這個操作。確保重試邏輯有次數(shù)限制和延遲。

  • 找出并終止阻塞進(jìn)程

    1. 連接到您的MySQL數(shù)據(jù)庫。

    2. 執(zhí)行以下SQL,查看當(dāng)前正在運(yùn)行的事務(wù)和鎖信息:

      -- 查看當(dāng)前所有事務(wù)(重點(diǎn)關(guān)注trx_state為'LOCK WAIT'和'RUNNING'的)
      SELECT * FROM information_schema.INNODB_TRX;
      
      -- 或者更詳細(xì)的鎖信息查詢
      SELECT 
          r.trx_id waiting_trx_id,
          r.trx_mysql_thread_id waiting_thread,
          r.trx_query waiting_query,
          b.trx_id blocking_trx_id,
          b.trx_mysql_thread_id blocking_thread,
          b.trx_query blocking_query
      FROM information_schema.INNODB_LOCK_WAITS w
      INNER JOIN information_schema.INNODB_TRX b ON b.trx_id = w.blocking_trx_id
      INNER JOIN information_schema.INNODB_TRX r ON r.trx_id = w.requesting_trx_id;
      
    3. 從結(jié)果中找到 blocking_trx_id(阻塞者的事務(wù)ID)或 blocking_thread(阻塞者的連接ID)。

    4. 強(qiáng)制殺死阻塞的數(shù)據(jù)庫連接(請謹(jǐn)慎操作,確認(rèn)不影響業(yè)務(wù)):

      KILL [blocking_thread_id];
      

      殺死后,您的等待事務(wù)應(yīng)該就能繼續(xù)執(zhí)行了。

2. 長期優(yōu)化(治本)

  • 優(yōu)化事務(wù)設(shè)計
    • 縮小事務(wù)范圍:確保事務(wù)盡可能短小精悍。不要在事務(wù)中包含不必要的業(yè)務(wù)邏輯、遠(yuǎn)程調(diào)用或文件操作。執(zhí)行完數(shù)據(jù)庫操作后立即提交。
    • 避免長事務(wù):這是最根本的原因。檢查代碼中是否有事務(wù)方法執(zhí)行時間過長。
  • 優(yōu)化SQL和索引
    • 您的SQL使用了 IN (?) 和多個 JOIN。確保 tcqp.id、連接條件上的字段(如 tcqpd.contract_process_id, tcaf.contract_process_id)都有合適的索引。沒有索引會導(dǎo)致鎖升級(例如鎖表而不是鎖行),大大增加鎖沖突的概率。
    • 使用 EXPLAIN 分析您的SQL,看是否使用了正確的索引。
  • 業(yè)務(wù)邏輯分離
    • 考慮將這個大而復(fù)雜的更新操作拆分成多個較小、較快的事務(wù)。例如,可以先更新主表,再更新子表。
  • 調(diào)整數(shù)據(jù)庫參數(shù)(需DBA評估)
    • innodb_lock_wait_timeout:可以適當(dāng)調(diào)大這個參數(shù)(例如調(diào)到120秒),但這只是延長了等待時間,并未解決根本問題。
    • innodb_rollback_on_timeout:默認(rèn)是OFF,超時后只回滾最后一條語句??梢栽O(shè)置為ON,讓整個事務(wù)回滾,但這可能會影響業(yè)務(wù)邏輯。
  • 使用不同的隔離級別
    • 默認(rèn)的 REPEATABLE READ 隔離級別更容易產(chǎn)生鎖。如果業(yè)務(wù)允許,可以考慮在特定查詢中使用 READ COMMITTED 級別,但這需要仔細(xì)評估一致性要求。

總結(jié)

這個錯誤的根本原因是 數(shù)據(jù)庫中存在“長事務(wù)”阻塞了您的正常操作。

建議處理流程:

  1. 緊急處理:通過 information_schema 找到并 KILL 阻塞進(jìn)程。
  2. 分析原因:審查代碼,找到是哪個業(yè)務(wù)操作導(dǎo)致了長事務(wù)。
  3. 根本解決:優(yōu)化事務(wù)代碼和SQL,確保所有數(shù)據(jù)庫操作快速完成并及時提交。

SELECT * FROM information_schema.INNODB_TRX; 這個sql執(zhí)行完的返回的數(shù)據(jù)怎么看?

執(zhí)行 SELECT * FROM information_schema.INNODB_TRX; 后,您會看到當(dāng)前所有InnoDB事務(wù)的詳細(xì)信息。以下是關(guān)鍵字段的解釋和如何分析:

關(guān)鍵字段解釋

字段說明重點(diǎn)關(guān)注
trx_idInnoDB內(nèi)部事務(wù)ID用于識別特定事務(wù)
trx_state事務(wù)狀態(tài)LOCK WAIT(鎖等待中), RUNNING(運(yùn)行中), ROLLING BACK(回滾中)
trx_started事務(wù)開始時間判斷事務(wù)運(yùn)行了多久
trx_requested_lock_id正在等待的鎖ID僅在trx_state='LOCK WAIT'時有值
trx_wait_started開始等待的時間判斷等待了多久
trx_weight事務(wù)權(quán)重值越大越可能被回滾
trx_mysql_thread_idMySQL連接線程ID用于KILL命令
trx_query當(dāng)前正在執(zhí)行的SQL查看事務(wù)在做什么
trx_operation_state當(dāng)前操作狀態(tài)
trx_tables_in_use涉及的表數(shù)量
trx_tables_locked被鎖定的表數(shù)量
trx_lock_structs鎖結(jié)構(gòu)數(shù)量
trx_lock_memory_bytes鎖內(nèi)存占用
trx_rows_locked被鎖定的行數(shù)值過大可能是問題
trx_rows_modified修改的行數(shù)值過大可能是長事務(wù)

如何分析結(jié)果

1. 識別問題事務(wù)

-- 按事務(wù)開始時間排序,查看運(yùn)行時間最長的事務(wù)
SELECT 
    trx_id,
    trx_state,
    trx_started,
    TIMEDIFF(NOW(), trx_started) as running_time,
    trx_mysql_thread_id,
    trx_query,
    trx_rows_locked,
    trx_rows_modified
FROM information_schema.INNODB_TRX
ORDER BY trx_started ASC;

2. 重點(diǎn)關(guān)注的情況

鎖等待事務(wù)(trx_state = 'LOCK WAIT')

-- 查找正在等待鎖的事務(wù)
SELECT * FROM information_schema.INNODB_TRX 
WHERE trx_state = 'LOCK WAIT';
  • 這類事務(wù)就是您的報錯事務(wù)
  • 查看 trx_query 了解它在等待什么
  • 記錄 trx_mysql_thread_id 以備需要時終止

長時間運(yùn)行事務(wù)(RUNNING狀態(tài)但運(yùn)行時間很長)

-- 查找運(yùn)行超過30秒的事務(wù)
SELECT * FROM information_schema.INNODB_TRX 
WHERE trx_state = 'RUNNING' 
  AND trx_started < NOW() - INTERVAL 30 SECOND;

3. 實際分析示例

假設(shè)查詢結(jié)果如下:

trx_idtrx_statetrx_startedtrx_mysql_thread_idtrx_querytrx_rows_locked
12345RUNNING2024-01-01 10:00:00100UPDATE table SET ...5000
67890LOCK WAIT2024-01-01 10:01:30101UPDATE tb_contract_quantities_process ...0

分析結(jié)論:

  • 線程ID 100 的事務(wù)從 10:00 開始運(yùn)行,已經(jīng)鎖定了5000行,很可能是阻塞事務(wù)
  • 線程ID 101 的事務(wù)在等待鎖,這就是您的報錯事務(wù)

4. 進(jìn)一步定位阻塞關(guān)系

-- 更精確地查找誰阻塞了誰
SELECT 
    r.trx_id AS waiting_trx_id,
    r.trx_mysql_thread_id AS waiting_thread,
    r.trx_query AS waiting_query,
    b.trx_id AS blocking_trx_id, 
    b.trx_mysql_thread_id AS blocking_thread,
    b.trx_query AS blocking_query
FROM information_schema.INNODB_LOCK_WAITS w
INNER JOIN information_schema.INNODB_TRX b ON b.trx_id = w.blocking_trx_id
INNER JOIN information_schema.INNODB_TRX r ON r.trx_id = w.requesting_trx_id;

5. 處理建議

  1. 如果是測試環(huán)境:直接終止阻塞事務(wù)

    KILL 100;  -- 終止線程ID為100的連接
    
  2. 如果是生產(chǎn)環(huán)境

    • 聯(lián)系相關(guān)業(yè)務(wù)負(fù)責(zé)人確認(rèn)該長時間運(yùn)行的事務(wù)是否可以終止
    • 分析 trx_query 中的SQL,優(yōu)化其性能
    • 考慮在業(yè)務(wù)低峰期執(zhí)行相關(guān)操作

通過這種方法,您就能準(zhǔn)確識別出導(dǎo)致鎖等待超時的根本原因了。

阻塞事務(wù) 可能產(chǎn)生的原因

阻塞事務(wù)的產(chǎn)生原因多種多樣,以下是主要的分類和具體原因:

1. 事務(wù)設(shè)計問題

長事務(wù)

-- 典型的長事務(wù)模式
BEGIN;
-- 執(zhí)行復(fù)雜的業(yè)務(wù)邏輯
UPDATE large_table SET ... WHERE ...; -- 耗時操作
-- 中間可能包含業(yè)務(wù)邏輯、外部API調(diào)用等
COMMIT; -- 很久之后才提交

特征:事務(wù)開始和提交時間間隔很長

未提交的事務(wù)

// 代碼中忘記提交或回滾
@Transactional
public void processData() {
    // 執(zhí)行更新操作
    updateTableA(...);
    // 如果這里發(fā)生異常,事務(wù)可能一直掛起
    if (someCondition) {
        return; // 忘記提交或回滾
    }
    // ... 其他操作
}

2. SQL性能問題

缺乏合適的索引

-- 沒有索引的更新操作
UPDATE tb_contract_quantities_process 
SET del_flag = 1 
WHERE contract_name LIKE '%某合同%';  -- 全表掃描,鎖住大量行

-- 有索引的高效更新
UPDATE tb_contract_quantities_process 
SET del_flag = 1 
WHERE id IN (1, 2, 3);  -- 使用主鍵索引,只鎖特定行

全表掃描操作

-- 導(dǎo)致鎖表的操作
UPDATE table_a SET status = 1 WHERE unindexed_column = 'value';
DELETE FROM large_table WHERE create_time < '2023-01-01';

3. 鎖機(jī)制相關(guān)

鎖升級

  • 行鎖升級為表鎖:當(dāng)一條SQL需要鎖定大量數(shù)據(jù)行時,InnoDB可能將鎖升級為表鎖
  • 間隙鎖(Gap Lock):在REPEATABLE READ隔離級別下,范圍查詢會鎖定不存在的記錄區(qū)間

死鎖循環(huán)

-- 事務(wù)A
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;

-- 事務(wù)B (同時執(zhí)行)
UPDATE accounts SET balance = balance - 50 WHERE id = 2;
UPDATE accounts SET balance = balance + 50 WHERE id = 1;

4. 應(yīng)用架構(gòu)問題

同步批量操作

// 在事務(wù)中處理大量數(shù)據(jù)
@Transactional
public void batchProcessContracts(List<Long> contractIds) {
    for (Long id : contractIds) {  // 循環(huán)處理,事務(wù)時間很長
        updateContractStatus(id);
        insertProcessLog(id);
        // ... 其他操作
    }
}

嵌套事務(wù)問題

@Transactional
public void mainProcess() {
    // 主事務(wù)開始
    updateMainTable();
    
    // 調(diào)用另一個事務(wù)方法
    subProcess();  // 如果subProcess有@Transactional(propagation=REQUIRES_NEW)
    
    // 主事務(wù)繼續(xù)...
}

5. 業(yè)務(wù)邏輯缺陷

用戶交互式事務(wù)

@Transactional
public void approveContract(Long contractId) {
    // 開始事務(wù)
    updateContractStatus(contractId, "APPROVING");
    // 等待用戶確認(rèn)(事務(wù)一直打開?。?
    waitForUserConfirmation();  // 錯誤做法!
    updateContractStatus(contractId, "APPROVED");
}

循環(huán)依賴更新

-- 事務(wù)A
UPDATE table_a SET status = (SELECT count FROM table_b WHERE ...);
-- 事務(wù)B  
UPDATE table_b SET count = (SELECT status FROM table_a WHERE ...);

6. 數(shù)據(jù)庫配置問題

不合理的隔離級別

  • READ UNCOMMITTED:臟讀風(fēng)險
  • REPEATABLE READ:容易產(chǎn)生間隙鎖(MySQL默認(rèn))
  • SERIALIZABLE:嚴(yán)格的鎖機(jī)制,性能差

連接池配置不當(dāng)

# 連接池配置問題
spring.datasource.hikari.maximum-pool-size=200  # 連接數(shù)過多,競爭激烈
spring.datasource.hikari.connection-timeout=30000  # 連接超時時間過長

7. 監(jiān)控和診斷方法

識別阻塞事務(wù)的SQL

-- 查看當(dāng)前執(zhí)行的所有SQL
SHOW PROCESSLIST;

-- 查看詳細(xì)的鎖信息
SELECT 
    r.trx_id waiting_trx_id,
    r.trx_query waiting_query,
    b.trx_id blocking_trx_id,
    b.trx_query blocking_query,
    b.trx_started blocking_started,
    TIMEDIFF(NOW(), b.trx_started) blocking_duration
FROM information_schema.INNODB_LOCK_WAITS w
INNER JOIN information_schema.INNODB_TRX b ON b.trx_id = w.blocking_trx_id
INNER JOIN information_schema.INNODB_TRX r ON r.trx_id = w.requesting_trx_id;

8. 預(yù)防和解決方案

優(yōu)化事務(wù)設(shè)計

// 正確的做法:短事務(wù)
public void processInShortTransactions(List<Long> ids) {
    for (Long id : ids) {
        transactionTemplate.execute(status -> {
            // 每個ID在獨(dú)立事務(wù)中處理
            updateContract(id);
            insertLog(id);
            return null;
        });
    }
}

添加合適的索引

-- 為查詢和更新條件添加索引
CREATE INDEX idx_contract_process ON tb_contract_quantities_process(id);
CREATE INDEX idx_process_details ON tb_contract_quantities_process_details(contract_process_id);
CREATE INDEX idx_arrival_file ON tb_contract_arrival_file(contract_process_id);

使用樂觀鎖

// 使用版本號避免悲觀鎖
@Transactional
public boolean updateWithOptimisticLock(Long id, String newValue) {
    Entity entity = entityDao.findById(id);
    int version = entity.getVersion();
    
    int affected = entityDao.updateWithVersion(id, newValue, version, version + 1);
    return affected > 0;  // 如果失敗可以重試
}

通過分析這些可能的原因,您可以系統(tǒng)地排查和解決阻塞事務(wù)問題。

總結(jié)

到此這篇關(guān)于MySQL鎖等待超時錯誤詳細(xì)解釋原因和解決方案的文章就介紹到這了,更多相關(guān)MySQL鎖等待超時錯誤內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • mysql8 公用表表達(dá)式CTE的使用方法實例分析

    mysql8 公用表表達(dá)式CTE的使用方法實例分析

    這篇文章主要介紹了mysql8 公用表表達(dá)式CTE的使用方法,結(jié)合實例形式分析了mysql8 公用表表達(dá)式CTE的基本功能、原理使用方法及相關(guān)操作注意事項,需要的朋友可以參考下
    2020-02-02
  • 高效利用mysql索引指南

    高效利用mysql索引指南

    這篇文章主要介紹了高效利用mysql索引指南,你知道如何創(chuàng)建恰當(dāng)?shù)乃饕龁??在?shù)據(jù)量小的時候,不合適的索引對性能并不會有太大的影響,但是當(dāng)數(shù)據(jù)逐漸增大時,性能便會急劇的下降。,需要的朋友可以參考下
    2019-06-06
  • MySQL 撤銷日志與重做日志(Undo Log與Redo Log)相關(guān)總結(jié)

    MySQL 撤銷日志與重做日志(Undo Log與Redo Log)相關(guān)總結(jié)

    這篇文章主要介紹了MySQL 撤銷日志與重做日志(Undo Log與Redo Log)相關(guān)總結(jié),幫助大家更好的理解和學(xué)習(xí)使用MySQL,感興趣的朋友可以了解下
    2021-03-03
  • MySQL中文亂碼問題解決方案

    MySQL中文亂碼問題解決方案

    這篇文章主要介紹了MySQL中文亂碼問題解決方案,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-09-09
  • mysql中l(wèi)imit查詢踩坑實戰(zhàn)記錄

    mysql中l(wèi)imit查詢踩坑實戰(zhàn)記錄

    在MySQL中我們常常用order by來進(jìn)行排序,使用limit來進(jìn)行分頁,下面這篇文章主要給大家介紹了關(guān)于mysql中l(wèi)imit查詢踩坑的相關(guān)資料,文中通過實例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-03-03
  • 探討:sql插入空,默認(rèn)1900-01-01 00:00:00.000的解決方法詳解

    探討:sql插入空,默認(rèn)1900-01-01 00:00:00.000的解決方法詳解

    本篇文章是對sql插入空,默認(rèn)1900-01-01 00:00:00.000的解決方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-06-06
  • MySQL的Grant命令詳解

    MySQL的Grant命令詳解

    mysql中可以通過Grant命令為數(shù)據(jù)庫賦予用戶權(quán)限,這里簡單介紹下Grant的使用方法,需要的朋友可以參考下
    2013-10-10
  • .Net Core導(dǎo)入千萬級數(shù)據(jù)至Mysql的步驟

    .Net Core導(dǎo)入千萬級數(shù)據(jù)至Mysql的步驟

    最近在工作中,涉及到一個數(shù)據(jù)遷移功能,從一個txt文本文件導(dǎo)入到MySQL功能。數(shù)據(jù)遷移,在互聯(lián)網(wǎng)企業(yè)可以說經(jīng)常碰到,而且涉及到千萬級、億級的數(shù)據(jù)量是很常見的。今天我們就來談?wù)凪ySQL怎么高性能插入千萬級的數(shù)據(jù)。
    2021-05-05
  • MySQL中SELECT+UPDATE處理并發(fā)更新問題解決方案分享

    MySQL中SELECT+UPDATE處理并發(fā)更新問題解決方案分享

    這篇文章主要介紹了MySQL中SELECT+UPDATE處理并發(fā)更新問題解決方案分享,需要的朋友可以參考下
    2014-05-05
  • 使用phpMyAdmin批量修改Mysql數(shù)據(jù)表前綴的方法

    使用phpMyAdmin批量修改Mysql數(shù)據(jù)表前綴的方法

    這篇文章主要介紹了使用phpMyAdmin批量修改Mysql數(shù)據(jù)表前綴的方法,需要的朋友可以參考下
    2015-09-09

最新評論

安乡县| 襄城县| 盱眙县| 龙泉市| 江安县| 洛浦县| 柯坪县| 阿尔山市| 元氏县| 盖州市| 望都县| 棋牌| 高清| 锦屏县| 隆子县| 巫溪县| 汝城县| 奉化市| 陇西县| 马边| 清徐县| 江安县| 都昌县| 台湾省| 攀枝花市| 炉霍县| 漳浦县| 翁牛特旗| 衡东县| 佛教| 尉氏县| 康乐县| 方正县| 大悟县| 页游| 武义县| 阳西县| 安岳县| 隆林| 南城县| 公安县|