http://www.javashuo.com/article/p-msbhlrah-hv.htmlhtml
MySQL大體可概括爲如下3種鎖:數據庫
- 表級鎖:開銷小,加鎖快;不會出現死鎖;鎖定粒度大,發生鎖衝突的機率最高,併發度最低。
- 行級鎖:開銷大,加鎖慢;會出現死鎖;鎖定粒度最小,發生鎖衝突的機率最低,併發度也最高。
- 頁面鎖:開銷和加鎖時間界於表鎖和行鎖之間;會出現死鎖;鎖定粒度界於表鎖和行鎖之間,併發度通常
MySQL表級鎖的鎖模式(MyISAM)
- MySQL表級鎖有兩種模式:表共享鎖(Table Read Lock)和表獨佔寫鎖(Table Write Lock)。
-
LOCK tables orders
read
local
,order_detail
read
local
;
併發
SELECT
SUM
(total)
FROM
orders;
spa
SELECT
SUM
(subtotal)
FROM
order_detail;
線程
Unlock tables;
code
MyISAM存儲引擎有一個系統變量concurrent_insert,專門用以控制其併發插入的行爲,其值分別能夠爲0、1或2。htm
- 當concurrent_insert設置爲0時,不容許併發插入。
- 當concurrent_insert設置爲1時,若是MyISAM容許在一個讀表的同時,另外一個進程從表尾插入記錄。這也是MySQL的默認設置。
- 當concurrent_insert設置爲2時,不管MyISAM表中有沒有空洞,都容許在表尾插入記錄,都容許在表尾併發插入記錄。
MyISAM的鎖調度blog
InnoDB鎖問題
- InnoDB與MyISAM的最大不一樣有兩點:
- 一是支持事務(TRANSACTION);
- 二是採用了行級鎖。
事務(Transaction)及其ACID屬性
併發事務帶來的問題排序
- 更新丟失(Lost Update):
- 髒讀(Dirty Reads):
- 不可重複讀(Non-Repeatable Reads):
- 一個事務在讀取某些數據已經發生了改變、或某些記錄已經被刪除了!這種現象叫作「不可重複讀」。
- 幻讀(Phantom Reads):
- 一個事務按相同的查詢條件從新讀取之前檢索過的數據,
- 卻發現其餘事務插入了知足其查詢條件的新數據,這種現象就稱爲「幻讀」。
數據庫實現事務隔離的方式,基本能夠分爲如下兩種。索引
隔離級別/讀數據一致性及容許的併發反作用 |
讀數據一致性 |
髒讀 |
不可重複讀 |
幻讀 |
未提交讀(Read uncommitted) |
最低級別,只能保證不讀取物理上損壞的數據 |
是 |
是 |
是 |
已提交度(Read committed) |
語句級 |
否 |
是 |
是 |
可重複讀(Repeatable read) |
事務級 |
否 |
否 |
是 |
可序列化(Serializable) |
最高級別,事務級 |
否 |
否 |
否 |
InnoDB實現瞭如下兩種類型的行鎖。
- 共享鎖(s):容許一個事務去讀一行,
- 排他鎖(X):容許獲取排他鎖的事務更新數據,
-
另外,爲了容許行鎖和表鎖共存,實現多粒度鎖機制,
InnoDB行鎖實現方式
- InnoDB行鎖是經過索引上的索引項來實現的,
- 這一點MySQL與Oracle不一樣,
- InnoDB這種行鎖實現特色意味者:
- 只有經過索引條件檢索數據,InnoDB纔會使用行級鎖,
- 不然,InnoDB將使用表鎖!
間隙鎖(Next-Key鎖)
- 當咱們用範圍條件而不是相等條件檢索數據,並請求共享或排他鎖時,
- 對於鍵值在條件範圍內但並不存在的記錄,叫作「間隙(GAP)」,
- InnoDB也會對這個「間隙」加鎖,這種鎖機制不是所謂的間隙鎖(Next-Key鎖)。
何時使用表鎖
- 對於InnoDB表,在絕大部分狀況下都應該使用行級鎖,
- 由於事務和行鎖每每是咱們之因此選擇InnoDB表的理由。
- 但在個另特殊事務中,也能夠考慮使用表級鎖。
- 第一種狀況是:事務須要更新大部分或所有數據,表又比較大,
- 若是使用默認的行鎖,不只這個事務執行效率低,
- 並且可能形成其餘事務長時間鎖等待和鎖衝突,
- 這種狀況下能夠考慮使用表鎖來提升該事務的執行速度。
- 第二種狀況是:事務涉及多個表,比較複雜,極可能引發死鎖,形成大量事務回滾。
- 這種狀況也能夠考慮一次性鎖定事務涉及的表,
- 從而避免死鎖、減小數據庫因事務回滾帶來的開銷。
- 固然,應用中這兩種事務不能太多,不然,就應該考慮使用MyISAM表。
- 在InnoDB下 ,使用表鎖要注意如下兩點。
- (1)使用LOCK TALBES雖然能夠給InnoDB加表級鎖,
- 但必須說明的是,表鎖不是由InnoDB存儲引擎層管理的,
- 而是由其上一層MySQL Server負責的,
- 僅當autocommit=0、innodb_table_lock=1(默認設置)時,
- InnoDB層才能知道MySQL加的表鎖,MySQL Server才能感知InnoDB加的行鎖,
- 這種狀況下,InnoDB才能自動識別涉及表級鎖的死鎖;
- 不然,InnoDB將沒法自動檢測並處理這種死鎖。
- (2)在用LOCAK TABLES對InnoDB鎖時要注意,要將AUTOCOMMIT設爲0,不然MySQL不會給表加鎖;
- 事務結束前,不要用UNLOCAK TABLES釋放表鎖,由於UNLOCK TABLES會隱含地提交事務;
- COMMIT或ROLLBACK產不能釋放用LOCAK TABLES加的表級鎖,必須用UNLOCK TABLES釋放表鎖。
-
SET
AUTOCOMMIT=0;
LOCAK TABLES t1 WRITE, t2
READ
, ...;
[do something
with
tables t1
and
here];
COMMIT
;
UNLOCK TABLES;
關於死鎖
- MyISAM表鎖是deadlock free的
- 可是在InnoDB中,除單個SQL組成的事務外,鎖是逐步得到的,這就決定了InnoDB發生死鎖是可能的。
- 發生死鎖後,InnoDB通常都能自動檢測到,並使一個事務釋放鎖並退回,另外一個事務得到鎖,繼續完成事務。
- 但在涉及外部鎖,或涉及鎖的狀況下,InnoDB並不能徹底自動檢測到死鎖,
- 這須要經過設置鎖等待超時參數innodb_lock_wait_timeout來解決。
下面就經過實例來介紹幾種死鎖的經常使用方法。
- (1)在應用中,若是不一樣的程序會併發存取多個表,
- 應儘可能約定以相同的順序爲訪問表,這樣能夠大大下降產生死鎖的機會。
- (2)在程序以批量方式處理數據的時候,
- 若是事先對數據排序,保證每一個線程按固定的順序來處理記錄,也能夠大大下降死鎖的可能。
- (3)在事務中,若是要更新記錄,應該直接申請足夠級別的鎖,即排他鎖,
- 而不該該先申請共享鎖,更新時再申請排他鎖,甚至死鎖。
- (4)在REPEATEABLE-READ隔離級別下,
- 若是兩個線程同時對相同條件記錄用SELECT...ROR UPDATE加排他鎖,
- 程序發現記錄尚不存在,就試圖插入一條新記錄,若是兩個線程都這麼作,就會出現死鎖。
- 這種狀況下,將隔離級別改爲READ COMMITTED,就能夠避免問題。
- (5)當隔離級別爲READ COMMITED時,
- 若是兩個線程都先執行SELECT...FOR UPDATE,判斷是否存在符合條件的記錄,若是沒有,就插入記錄。
- 此時,只有一個線程能插入成功,另外一個線程會出現鎖等待,
- 當第1個線程提交後,第2個線程會因主鍵重出錯,但雖然這個線程出錯了,卻會得到一個排他鎖!
- 這時若是有第3個線程又來申請排他鎖,也會出現死鎖。
- 對於這種狀況,能夠直接作插入操做,而後再捕獲主鍵重異常,或者在遇到主鍵重錯誤時,老是執行ROLLBACK釋放得到的排他鎖。