最近在開發中使用到了多執行緒對同個表進行讀寫操作,由於資料庫基礎渣渣,寫完**後程式跑起來出現了死鎖。於是對日誌進行分析跟蹤,發現在執行以下sql時出現死鎖:
update linkgoo_message_queue set state = 3 where state = 2;
很明顯在執行上述sql時,由於某個原因導致表linkgoo_message_queue被鎖或者表中相關記錄被鎖。那麼問題來了,mysql中update的時候會怎麼去加鎖呢?
網上查詢資料並進行試驗後得到如下結論:
1.mysql行鎖除了會鎖記錄之外,還有可能對索引加鎖,這取決於你的sql寫法;
2.update資料時,若條件中包含主鍵,那麼只鎖該主鍵對應的記錄;(注:網上找到的資料還提及:若操作了某個索引字段,比如 set state=3 ,state為索引字段,那麼state這個索引也會被鎖上。但通過實驗證明,若兩條sql條件中都存在主鍵,且操作了相同的索引字段,是不會引發資源等待的!如果第一條sql以主鍵為條件操作了索引字段,第二條sql以索引欄位為條件進行update,那麼第二條sql需要等待直到第一條sql釋放鎖資源)
3.update資料時,若條件中不包含主鍵但含有索引字段,那麼mysql會先對索引加鎖,再對受影響的記錄加鎖。且如果sql中還對索引字段進行操作,那麼還會對該索引字段追加鎖。(注意加鎖是有順序的!正是因為這樣才導致的死鎖)
4.通過實驗證明,對索引加鎖,是對整個字段加鎖,而不是對索引的某個值(比如state=3)加鎖。
通過上述的資訊,我們再來分析一下這條sql:
update linkgoo_message_queue set state = 3 where state = 2;
首先mysql會對state這個索引加鎖,然後再對state=2的所有記錄加鎖。由於存在set state = 3,還會對state這個索引進行加鎖,但之前已經加過了,不會再重複加。
ok,我們再回顧一下《微控制器原理與嵌入式系統設計》裡邊(別問我為什麼是這本書,我會告訴你我是自動化專業出身的萬金油嗎?)對死鎖的定義:指兩個或兩個以上的程序在執行過程中,由於競爭資源或者由於彼此通訊而造成的一種阻塞的現象。
對於上邊的sql,由於其占用了state索引和相關的資料庫記錄,倘若要在併發條件下發生死鎖,那麼若存在有執行緒先把記錄給鎖了,再要鎖state索引,那麼就有可能出現死鎖。
重新梳理**和業務邏輯,在處理完message後,會根據message.id對message的state進行更新。問題到此已經很明顯了,執行update linkgoo_message_queue set state = 3 where state = 2時,若state索引已經鎖了,但還未對記錄進行上鎖。此時根據message.id 去更新state,會對id=***這條記錄進行上鎖,並等待state索引的鎖釋放。而第一條sql進入記錄上鎖階段發現記錄已經被鎖了,進入了死迴圈發生死鎖。
那麼上邊的問題怎麼去解決呢?
在mysql中update時不要直接將索引字段放到條件中進行篩選,應該採用select先找出需要修改的資料,再逐條對資料進行update。注意需要考慮如何讓select出來的記錄在修改前還是查詢時的狀態,而不是被其他執行緒update。
mysql 行鎖 訂票 mysql 行鎖
在電子商務裡,經常會出現庫存數量少,購買的人又特別多,大併發情況下如何確保商品數量不會被多次購買.其實很簡單,利用事務 for update就可以解決.我們都知道for update實際上是共享鎖,是可以被讀取的.但是如何在執行時,不被讀取呢.簡單來說 假設現在庫存為1,現在有a和b同時購買 先開啟...
mysql行鎖詳解 詳解MySQL行鎖
鎖是計算機協調多個程序或執行緒併發訪問某一資源的機制。鎖保證資料併發訪問的一致性 有效性 鎖衝突也是影響資料庫併發訪問效能的乙個重要因素。鎖是mysql在伺服器層和儲存引擎層的的併發控制。mysql中從對資料操作的粒度分為表鎖和行鎖。表鎖是指對一整張表加鎖,一般是 ddl 處理時使用 而行鎖則是鎖定...
mysql行鎖的特性 MySql的表鎖行鎖及間隙鎖
常用命令 手動新增表鎖 lock table 表名稱 read write 表名稱2 read write 檢視表上加過的鎖 show open tables 刪除表鎖 unlock tables 1.表鎖 特點 1.每次操作鎖住整張表,開銷小,加鎖快 2.不會出現死鎖 3.鎖定粒度大,發生鎖衝突的...