數據庫是一個多用戶使用的共享資源。當多個用戶并發地存取數據時,在數據庫中就會產生多個事務同時存取同一數據的情況。若對并發操作不加控制就可能會讀取和存儲不正確的數據,破壞數據庫的一致性。
加鎖是實現數據庫并發控制的一個非常重要的技術。當事務在對某個數據對象進行操作前,先向系統發出請求,對其加鎖。加鎖后事務就對該數據對象有了一定的控制,在該事務釋放鎖之前,其他的事務不能對此數據對象進行更新操作。
1.從對數據操作的類型分類
讀鎖(共享鎖):針對同一份數據,多個讀操作可以同時進行,不會互相影響
寫鎖(排他鎖):當前寫操作沒有完成前,它會阻斷其他寫鎖和讀鎖
2.從對數據操作的范圍分類
為了盡可能提高數據庫的并發度,理論上每次只鎖定當前操作的數據,即每次鎖定的數據范圍越小就會得到最大的并發度,但是管理鎖是很耗資源的事情(涉及獲取,檢查,釋放鎖等動作),因此數據庫系統需要在高并發響應和系統性能兩方面進行平衡,這樣就產生了“鎖粒度(Lock granularity)”的概念。
表級鎖:開銷小,加鎖快;不會出現死鎖;鎖定粒度大,發生鎖沖突的概率最高,并發度最低(MyISAM 和 MEMORY 存儲引擎采用的是表級鎖);適合于以查詢為主,只有少量按索引條件更新數據的應用,如Web應用;
行級鎖:開銷大,加鎖慢;會出現死鎖;鎖定粒度最小,發生鎖沖突的概率最低,并發度也最高(InnoDB 存儲引擎既支持行級鎖也支持表級鎖,但默認情況下是采用行級鎖); 適合于有大量按索引條件并發更新少量不同數據,同時又有并發查詢的應用,如一些在線事務處理(OLTP)系統。
頁面鎖:開銷和加鎖時間界于表鎖和行鎖之間;會出現死鎖;鎖定粒度界于表鎖和行鎖之間,并發度一般。
悲觀鎖:每次去拿數據的時候都認為別人會修改,所以每次在拿數據的時候都會上鎖,這樣別人想拿這個數據都會block直到它拿到鎖。因此,悲觀鎖需要耗時比較的多,跟樂觀鎖比較,悲觀鎖是有數據庫自己實現的,用的時候我們直接調用數據的相關語句就可以。
樂觀鎖:用數據版本記錄機制實現,這是樂觀鎖最常用的方式,所謂的數據版本,為數據增加一個版本號的字段,一般是通過為數據表增加一個數據類型的version字段實現,當讀取數據時,將把二十年字段的值一同讀取出來,數據每次更新都需要對version值加一,在我們提交更新的時候,判斷數據表對應記錄的當前版本信息與第一次取出來的version值進行對比,如果數據庫的表當前版本號魚取出來的version值相等,則給與更新否則認為過期數據不給與更新。
是指二個或者二個以上的進程在執行時候,因為爭奪資源造成相互等待的現象,進程一直處于等待中,無法得到釋放,這種狀態就叫做死鎖。如批量入庫時,存在則更新,不存在則插入,insert into tab(xx,xx) on duplicate key update xx=‘xx’。
1)使用命令 show engine innodb status 查看最近的一次死鎖。
2)InnoDB Lock Monitor 打開鎖監控,每 15s 輸出一次日志。使用完畢后建議關閉,否則會影響數據庫性能。
1:通過innodblockwait_timeout來設置超時時間,一直等待直到超時。其中innodb默認是使用設置死鎖時間來讓死鎖超時的策略,默認innodblockwait_timeout設置的時長是50s。
2:發起死鎖檢測,發現死鎖之后,主動回滾死鎖中的事務,不需要其他事務繼續。
1)為了在單個innodb表上執行多個并發寫入操作時避免死鎖,可以在事務開始時,通過為預期要修改行,使用select …for update語句來獲取必要的鎖,即使這些行的更改語句是在之后才執行的
2)在事務中,如果要更新記錄,應該直接申請足夠級別的鎖,即排他鎖,而不應先申請共享鎖,更新時在申請排他鎖。因為這時候當用戶在申請排他鎖時,其他事務可能又已經獲得了相同記錄的共享鎖,
3)如果事務需要修改或鎖定多個表,則應在每個事務中以相同的順序使用加鎖語句。在應用中,如果不同的程序會并發獲取多個表,應盡量約定以相同的順序來訪問表,這樣可以大大降低產生死鎖的機會。
4)通過 select …lock in share mode獲取行的讀鎖后,如果當前事務在需要對該記錄進行更新操作,則很有可能造成死鎖;
5)改變事務隔離級別.