update的where條件要把索引的字段帶上,要不然就全表鎖


update的where條件要把索引的字段帶上,要不然就全表鎖

文章目錄

    update的where條件要把索引的字段帶上,要不然就全表鎖
        本文主要內容
        背景
        在學習中總結一下內容
        關於鎖的表
        sql加鎖思考點
        InnoDB行鎖實現方式

本文主要內容

報錯了: java.sql.BatchUpdateException: Lock wait timeout exceeded; try restarting transaction
對表進行更新和插入操作,在update的時候出現上述異常,經過排查發現是鎖表的原因。
背景

    建表語句如下:
    表名為: app_pay_log_test
    索引為:UNIQUE KEY rso_orderid_idx (rso,order_id)

    CREATE TABLE `app_pay_log_test` (
      `rso` varchar(50) NOT NULL DEFAULT '-1' COMMENT '充值渠道',
      `order_id` varchar(100) NOT NULL COMMENT '訂單id',
      `amount` decimal(20,2) NOT NULL COMMENT '充值金額',
      `product` varchar(50) DEFAULT NULL,
      UNIQUE KEY `rso_orderid_idx` (`rso`,`order_id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='手游充值訂單';

    1
    2
    3
    4
    5
    6
    7

    出現異常的執行操作為:

    UPDATE `app_pay_log_test` SET `product`=$2 WHERE `order_id`=$1 AND `product` is NULL

    1

這里全表掃描鎖表了,update的where條件把索引的字段要帶上,要不然就全表鎖。
也就是說要把檢索條件上加上索引。而本表的索引是聯合索引,上面的sql沒有走索引,導致去全表掃描,出現表鎖。

    解決辦法:

    UPDATE `app_pay_log_test` SET `product`=$2 WHERE `rso`=$0 AND `order_id`=$1 AND `product` is NULL

    1

實際上,不管是插入更新查詢都需要走索引才不會那么容易出現全表掃描。
在學習中總結一下內容

InnoDB行鎖是通過給索引上的索引項加鎖來實現的。
InnoDB這種行鎖實現特點意味着:只有通過索引條件檢索數據,InnoDB才使用行級鎖,否則,InnoDB將使用表鎖!
在實際應用中,要特別注意InnoDB行鎖的這一特性,不然的話,可能導致大量的鎖沖突,從而影響並發性能。
關於鎖的表

由於MySQL的行鎖是針對索引加的鎖,不是針對記錄加的鎖,所以雖然是訪問不同行的記錄,但是如果是使用相同的索引鍵,是會出現鎖沖突的。
MySQL 5.5 中,information_schema庫中新增了三個關於鎖的表,即 innodb_trx、innodb_locks 和 innodb_lock_waits.
其中 innodb_trx 表記錄當前運行的所有事務,innodb_locks 表記錄當前出現的鎖,innodb_lock_waits 表記錄鎖等待的對應關系。
具體用法可以參考:表結構說明

SELECT * FROM information_schema.INNODB_TRX\G;
SELECT * FROM information_schema.innodb_locks \G
SELECT * FROM information_schema.innodb_lock_waits \G

    1
    2
    3

在 innodb_trx 表的第一行,trx_id為360F表示事務id,狀態(trx_state)為等待狀態,線程ID為2,事務用到的表為1(trx_tables_in_use),有1(trx_tables_locked)個表被鎖。可以使用 kill trx_mysql_thread_id 殺死這個sql線程。

    mysql> SELECT * FROM information_schema.innodb_trx \G
    *************************** 1. row ***************************
                        trx_id: 360F
                     trx_state: LOCK WAIT
           trx_mysql_thread_id: 2
                     trx_query: UPDATE user SET name="lock_waits" WHERE ID = 2
             trx_tables_in_use: 1
             trx_tables_locked: 1

    1
    2
    3
    4
    5
    6
    7
    8

sql加鎖思考點

    前提一:篩選列是不是主鍵?
    前提二:當前系統的隔離級別是什么?(不同隔離級別 鎖類型不同)
    前提三:篩選列如果不是主鍵,那么篩選列上有索引嗎?
    前提四:篩選列上如果有二級索引,那么這個索引是唯一索引嗎?(唯一索引只鎖一行)
    前提五:兩個SQL的執行計划是什么?索引掃描?全表掃描?(走行鎖還是表鎖或者間隙鎖)

InnoDB行鎖實現方式

    InnoDB行鎖是通過給索引上的索引項加鎖來實現的,只有通過索引條件檢索數據,InnoDB才使用行級鎖,否則,InnoDB將使用表鎖
    在實際應用中,要特別注意InnoDB行鎖的這一特性,不然的話,可能導致大量的鎖沖突,從而影響並發性能。下面通過一些實際例子來加以說明。
    (1)在不通過索引條件查詢的時候,InnoDB確實使用的是表鎖,而不是行鎖。
    (2)由於MySQL的行鎖是針對索引加的鎖,不是針對記錄加的鎖,所以雖然是訪問不同行的記錄,但是如果是使用相同的索引鍵,是會出現鎖沖突的。
    (3)當表有多個索引的時候,不同的事務可以使用不同的索引鎖定不同的行,另外,不論是使用主鍵索引、唯一索引或普通索引,InnoDB都會使用行鎖來對數據加鎖。
    (4)即便在條件中使用了索引字段,但是否使用索引來檢索數據是由MySQL通過判斷不同執行計划的代價來決定的,如果MySQL認為全表掃描效率更高,比如對一些很小的表,它就不會使用索引,這種情況下InnoDB將使用表鎖,而不是行鎖。因此,在分析鎖沖突時,別忘了檢查SQL的執行計划,以確認是否真正使用了索引。

參考:
MySQL SHOW PROCESSLIST協助故障診斷 http://www.ywnds.com/?p=9337
記一次數據庫死鎖Deadlock https://www.jianshu.com/p/1c8fecb00563
MySQL 5.5 InnoDB 鎖等待 https://dbarobin.com/2015/01/27/innodb-lock-wait-under-mysql-5.5/
————————————————
版權聲明:本文為CSDN博主「Agly_Charlie」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/Agly_Clarlie/article/details/83032289


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM