03.08 開發中那些事兒:為啥update會超時呢?

前一段時間,生產環境碰到一個異常,更新數據庫的時候,提示鎖等待超時(Lock wait timeout exceeded; try restarting transaction),超時時間是50秒,修改一條數據需要等待50秒+,我就有點納悶了。異常如下:

開發中那些事兒:為啥update會超時呢?


第一時間沒想太多,覺的是可能操作數據庫太頻繁了。代碼中,一般的業務邏輯是,請求過來了,先數據入庫,每次調用一個接口(不通的業務)就會修改一下數據庫中數據的狀態。最後被我優化為,所有的業務處理完畢,執行一次數據插入,接口部分加了try/catch,保證數據可以肯定會入庫。我真的是太天真了,問題依然存在。


然後,就去查資料,網上有說可以把超時時間設置長一點。允許可以解決問題,但是並不能真正的解決問題,並且會影響正常的業務,比如對接接口超時,觸發重試,數據就會有問題(已經出現這個問題了)。


諮詢了我們的DBA,一般都是減少超時時間的,還是應該減少鎖和資源的爭用,說白了就是再去排查一下自己的sql。查詢數據庫鎖超時時間如下:

開發中那些事兒:為啥update會超時呢?


其實,更新語句數據庫會默認添加行級鎖,可以整個業務流程(同步流程和異步流程),操作這個表就只有一個插入數據,和兩個更新語句,已經不能再優化了。


難道是表鎖了?繼續查詢資料,查看數據庫事務級別為已提交讀(Read Committed),網上說這種數據級別不會鎖表,其實這種看法是錯誤的,誤導了我很長一段時間。


在什麼情況下會鎖表呢?真實情況是,mysql的默認引擎是InnoDB,屬於悲觀鎖,默認鎖級別是:行鎖。


加鎖規則

1、查詢或者修改的where篩選條件中使用索引字段的,加的是行鎖;不是使用索引字段篩選的,加的是表鎖。


2、對於UPDATE、DELETE和INSERT語句,InnoDB會自動給涉及數據集加排他鎖(行鎖);


3、對於普通SELECT語句,InnoDB不會加任何鎖。

我上面的是根據主鍵更新的,所以不可能鎖表。經過排查,是因為同事批量更新數據庫的where條件沒有添加索引,造成數據表鎖,然後鎖鎖超時。添加索引以後問題解決。


普及知識點:


一、事務的四大特性(ACID):


如果一個數據庫聲稱支持事務的操作,那麼該數據庫必須要具備以下四個特性:


1、原子性(Atomicity)

:原子性是指事務包含的所有操作要麼全部成功,要麼全部失敗回滾,因此事務的操作如果成功就必須要完全應用到數據庫,如果操作失敗則不能對數據庫有任何影響。


2、一致性(Consistency):一致性是指事務必須使數據庫從一個一致性狀態變換到另一個一致性狀態,也就是說一個事務執行之前和執行之後都必須處於一致性狀態。


3、隔離性(Isolation):隔離性是當多個用戶併發訪問數據庫時,比如操作同一張表時,數據庫為每一個用戶開啟的事務,不能被其他事務的操作所幹擾,多個併發事務之間要相互隔離。


4、持久性(Durability):持久性是指一個事務一旦被提交了,那麼對數據庫中的數據的改變就是永久性的,即便是在數據庫系統遇到故障的情況下也不會丟失提交事務的操作。

二、事務的隔離級別:

開發中那些事兒:為啥update會超時呢?


1、髒讀:髒讀是指在一個事務處理過程裡讀取了另一個未提交的事務中的數據。


2、不可重複讀:不可重複讀是指在對於數據庫中的某個數據,一個事務範圍內多次查詢卻返回了不同的數據值,這是由於在查詢間隔,被另一個事務修改並提交了。


3、虛讀(幻讀):幻讀是事務在操作過程中進行兩次查詢,第二次查詢的結果包含了第一次查詢中未出現的數據或者缺少了第一次查詢中出現的數據。幻讀和不可重複讀都是讀取了另一條已經提交的事務(這點就髒讀不同),所不同的是不可重複讀查詢的都是同一個數據項,而幻讀針對的是一批數據整體(比如數據的個數)


分享到:


相關文章: