<必看>談談數據庫的鎖機制!!

頁級:引擎 BDB。
表級:引擎 MyISAM , 理解爲鎖住整個表,能夠同時讀,寫不行
行級:引擎 INNODB , 單獨的一行記錄加鎖mysql

表級,直接鎖定整張表,在你鎖按期間,其它進程沒法對該表進行寫操做。若是你是寫鎖,則其它進程則讀也不容許
行級,,僅對指定的記錄進行加鎖,這樣其它進程仍是能夠對同一個表中的其它記錄進行操做。
頁級,表級鎖速度快,但衝突多,行級衝突少,但速度慢。因此取了折衷的頁級,一次鎖定相鄰的一組記錄。sql

MySQL 5.1支持對MyISAM和MEMORY表進行表級鎖定,對BDB表進行頁級鎖定,對InnoDB表進行行級鎖定。
對WRITE,MySQL使用的表鎖定方法原理以下:
若是在表上沒有鎖,在它上面放一個寫鎖。
不然,把鎖定請求放在寫鎖定隊列中。shell

對READ,MySQL使用的鎖定方法原理以下:
若是在表上沒有寫鎖定,把一個讀鎖定放在它上面 
不然,把鎖請求放在讀鎖定隊列中。數據庫

InnoDB使用行鎖定,BDB使用頁鎖定。對於這兩種存儲引擎,均可能存在死鎖。這是由於,在SQL語句處理期間,InnoDB自動得到行鎖定和BDB得到頁鎖定,而不是在事務啓動時得到。緩存

行級鎖定的優勢:
· 當在許多線程中訪問不一樣的行時只存在少許鎖定衝突。
· 回滾時只有少許的更改。
· 能夠長時間鎖定單一的行。性能優化

行級鎖定的缺點:
· 比頁級或表級鎖定佔用更多的內存。
· 當在表的大部分中使用時,比頁級或表級鎖定速度慢,由於你必須獲取更多的鎖。
· 若是你在大部分數據上常常進行GROUP BY操做或者必須常常掃描整個表,比其它鎖定明顯慢不少。
· 用高級別鎖定,經過支持不一樣的類型鎖定,你也能夠很容易地調節應用程序,由於其鎖成本小於行級鎖定。bash

在如下狀況下,表鎖定優先於頁級或行級鎖定:
· 表的大部分語句用於讀取。
· 對嚴格的關鍵字進行讀取和更新,你能夠更新或刪除能夠用單一的讀取的關鍵字來提取的一行:
· UPDATE tbl_name SET column=value WHERE unique_key_col=key_value;
· DELETE FROM tbl_name WHERE unique_key_col=key_value;
· SELECT 結合並行的INSERT語句,而且只有不多的UPDATE或DELETE語句。
· 在整個表上有許多掃描或GROUP BY操做,沒有任何寫操做。多線程

/* ========================= mysql 鎖表類型和解鎖語句 ========================= */併發

若是想要在一個表上作大量的 INSERT 和 SELECT 操做,可是並行的插入卻不可能時,能夠將記錄插入到臨時表中,而後按期將臨時表中的數據更新到實際的表裏。能夠用如下命令實現:性能

mysql> LOCK TABLES real_table WRITE, insert_table WRITE; mysql> INSERT INTO real_table SELECT * FROM insert_table; mysql> TRUNCATE TABLE insert_table; mysql> UNLOCK TABLES; 

行級鎖的優勢有:
在不少線程請求不一樣記錄時減小衝突鎖。
事務回滾時減小改變數據。
使長時間對單獨的一行記錄加鎖成爲可能。

行級鎖的缺點有:
比頁級鎖和表級鎖消耗更多的內存。
鎖是計算機協調多個進程或線程併發訪問某一資源的機制,不一樣的數據庫的鎖機制大同小異。因爲數據庫資源是一種供許多用戶共享的資源,因此如何保證數據併發訪問的一致性、有效性是全部數據庫必須解決的一個問題,鎖衝突也是影響數據庫併發訪問性能的一個重要因素。瞭解鎖機制不只可使咱們更有效的開發利用數據庫資源,也使咱們可以更好地維護數據庫,從而提升數據庫的性能。

MySQL的鎖機制比較簡單,其最顯著的特色是不一樣的存儲引擎支持不一樣的鎖機制。

例如,MyISAM和MEMORY存儲引擎採用的是表級鎖(table-level-locking);BDB存儲引擎採用的是頁面鎖(page-level-locking),同時也支持表級鎖;InnoDB存儲引擎既支持行級鎖,也支持表級鎖,默認狀況下是採用行級鎖。

上述三種鎖的特性可大體概括以下:
1) 表級鎖:開銷小,加鎖快;不會出現死鎖;鎖定粒度大,發生鎖衝突的機率最高,併發度最低。
2) 行級鎖:開銷大,加鎖慢;會出現死鎖;鎖定粒度最小,發生鎖衝突的機率最低,併發度也最高。
3) 頁面鎖:開銷和加鎖時間界於表鎖和行鎖之間;會出現死鎖;鎖定粒度界於表鎖和行鎖之間,併發度通常。

三種鎖各有各的特色,若僅從鎖的角度來講,表級鎖更適合於以查詢爲主,只有少許按索引條件更新數據的應用,如WEB應用;行級鎖更適合於有大量按索引條件併發更新少許不一樣數據,同時又有併發查詢的應用,如一些在線事務處理(OLTP)系統。

MySQL表級鎖有兩種模式:表共享讀鎖(Table Read Lock)和表獨佔寫鎖(Table Write Lock)。什麼意思呢,就是說對MyISAM表進行讀操做時,它不會阻塞其餘用戶對同一表的讀請求,但會阻塞 對同一表的寫操做;而對MyISAM表的寫操做,則會阻塞其餘用戶對同一表的讀和寫操做。

MyISAM表的讀和寫是串行的,即在進行讀操做時不能進行寫操做,反之也是同樣。但在必定條件下MyISAM表也支持查詢和插入的操做的併發進行,其機制是經過控制一個系統變量(concurrent_insert)來進行的,當其值設置爲0時,不容許併發插入;當其值設置爲1 時,若是MyISAM表中沒有空洞(即表中沒有被刪除的行),MyISAM容許在一個進程讀表的同時,另外一個進程從表尾插入記錄;當其值設置爲2時,不管MyISAM表中有沒有空洞,都容許在表尾併發插入記錄。

MyISAM鎖調度是如何實現的呢,這也是一個很關鍵的問題。例如,當一個進程請求某個MyISAM表的讀鎖,同時另外一個進程也請求同一表的寫鎖,此時MySQL將會如優先處理進程呢?經過研究代表,寫進程將先得到鎖(即便讀請求先到鎖等待隊列)。但這也形成一個很大的缺陷,即大量的寫操做會形成查詢操做很難得到讀鎖,從而可能形成永遠阻塞。所幸咱們能夠經過一些設置來調節MyISAM的調度行爲。咱們可經過指定參數low-priority-updates,使MyISAM默認引擎給予讀請求以優先的權利,設置其值爲1(set low_priority_updates=1),使優先級下降。

InnoDB鎖與MyISAM鎖的最大不一樣在於:一是支持事務(TRANCSACTION),二是採用了行級鎖。咱們知道事務是由一組SQL語句組成的邏輯處理單元,其有四個屬性(簡稱ACID屬性),分別爲:

原子性(Atomicity):事務是一個原子操做單元,其對數據的修改,要麼所有執行,要麼全都不執行;
一致性(Consistent):在事務開始和完成時,數據都必須保持一致狀態;
隔離性(Isolation):數據庫系統提供必定的隔離機制,保證事務在不受外部併發操做影響的「獨立」環境執行;
持久性(Durable):事務完成以後,它對於數據的修改是永久性的,即便出現系統故障也可以保持。

InnoDB有兩種模式的行鎖:

1)共享鎖:容許一個事務去讀一行,阻止其餘事務得到相同數據集的排他鎖。
( Select * from table_name where ......lock in share mode)

2)排他鎖:容許得到排他鎖的事務更新數據,阻止其餘事務取得相同數據集的共享讀鎖和 排他寫鎖。(select * from table_name where.....for update)
爲了容許行鎖和表鎖共存,實現多粒度鎖機制;同時還有兩種內部使用的意向鎖(都是表鎖),分別爲意向共享鎖和意向排他鎖。
InnoDB行鎖是經過給索引項加鎖來實現的,即只有經過索引條件檢索數據,InnoDB才使用行級鎖,不然將使用表鎖!

另外:插入,更新性能優化的幾個重要參數

複製代碼 代碼以下:

bulk_insert_buffer_size
批量插入緩存大小, 這個參數是針對MyISAM存儲引擎來講的.適用於在一次性插入100-1000+條記錄時, 提升效率.默認值是8M.能夠針對數據量的大小,翻倍增長.
concurrent_insert
併發插入, 當表沒有空洞(刪除過記錄), 在某進程獲取讀鎖的狀況下,其餘進程能夠在表尾部進行插入.
值能夠設0不容許併發插入, 1當表沒有空洞時, 執行併發插入, 2不論是否有空洞都執行併發插入.
默認是1 針對表的刪除頻率來設置.
delay_key_write
針對MyISAM存儲引擎,延遲更新索引.意思是說,update記錄時,先將數據up到磁盤,但不up索引,將索引存在內存裏,當表關閉時,將內存索引,寫到磁盤. 值爲 0不開啓, 1開啓. 默認開啓. delayed_insert_limit, delayed_insert_timeout, delayed_queue_size 延遲插入, 將數據先交給內存隊列, 而後慢慢地插入.可是這些配置,不是全部的存儲引擎都支持, 目前來看, 經常使用的InnoDB不支持, MyISAM支持. 根據實際狀況調大, 通常默認夠用了 

/* ==================== MySQL InnoDB 鎖表與鎖行 ======================== */

因爲InnoDB預設是Row-Level Lock,因此只有「明確」的指定主鍵,MySQL纔會執行Row lock (只鎖住被選取的資料例) ,不然MySQL將會執行Table Lock (將整個資料表單給鎖住)。

舉個例子: 假設有個表單products ,裏面有id跟name二個欄位,id是主鍵。

例1: (明確指定主鍵,而且有此筆資料,row lock)

複製代碼 代碼以下:

SELECT * FROM products WHERE id='3' FOR UPDATE; SELECT * FROM products WHERE id='3' and type=1 FOR UPDATE; 

例2: (明確指定主鍵,若查無此筆資料,無lock)

複製代碼 代碼以下:

SELECT * FROM products WHERE id='-1' FOR UPDATE; 

例3: (無主鍵,table lock)

複製代碼 代碼以下:

SELECT * FROM products WHERE name='Mouse' FOR UPDATE; 

例4: (主鍵不明確,table lock)

複製代碼 代碼以下:

SELECT * FROM products WHERE id<>'3' FOR UPDATE; 

例5: (主鍵不明確,table lock)

複製代碼 代碼以下:

SELECT * FROM products WHERE id LIKE '3' FOR UPDATE; 

注1: FOR UPDATE僅適用於InnoDB,且必須在交易區塊(BEGIN/COMMIT)中才能生效。
注2: 要測試鎖定的情況,能夠利用MySQL的Command Mode ,開二個視窗來作測試。

在MySql 5.0中測試確實是這樣的

另外:MyAsim 只支持表級鎖,InnerDB支持行級鎖添加了(行級鎖/表級鎖)鎖的數據不能被其它事務再鎖定,也不被其它事務修改(修改、刪除)是表級鎖時,不論是否查詢到記錄,都會鎖定表此外,若是A與B都對錶id進行查詢但查詢不到記錄,則A與B在查詢上不會進行row鎖,但A與B都會獲取排它鎖,此時A再插入一條記錄的話則會由於B已經有鎖而處於等待中,此時B再插入一條一樣的數據則會拋出Deadlock found when trying to get lock; try restarting transaction而後釋放鎖,此時A就得到了鎖而插入成功

相關文章
相關標籤/搜索