mysql鎖


1. MySQL鎖概論:

Mysql的鎖機制比較簡單,其最顯著的特定就是:不同存儲引擎支持不同的鎖機制!!!

  • MyISAMMEMORY存儲引擎采用的是表級鎖(table-level locking);
  • BDB存儲引擎采用的是頁面鎖(page-level locking),但也支持表級鎖
  • InnoDB存儲引擎既支持行級鎖(row-level locking),也支持表級鎖,但是默認情況下采用行級鎖

那么什么叫做表級鎖、行級鎖、頁面鎖呢?

  • 表級鎖:開銷小,加鎖快;不會出現死鎖;鎖粒度大。發生沖突幾率最大,並發度最低。

  • 行級鎖:開銷大,加鎖慢;會出現死鎖,發生鎖沖突幾率最低,並發度最高。

  • 頁面鎖:介於表級鎖和行級鎖之間。

那選用那種鎖最好呢?
從上述特點來說,無法確切的說出那種鎖更好,只能根據具體應用的特點來說那種最合適。

  • 表級鎖:更適合於查詢為主的場景。

  • 行級鎖:更適合於大量按索引條件 並發更新 少量不同的數據,同時又有 並發查詢 的應用。

1.1那么mysql何時使用MyISAM,什么時候使用InnoDB

INNODBMyISAMmysql數據庫提供的兩種存儲引擎。INNODB會支持一些關系數據庫的高級功能,如事務功能和行級鎖MyISAM不支持,但是MyISAM的性能更優,占用的空間少。

如果應用程序一定要使用事務,那么要選擇INNODB引擎。但是INNODB的行級鎖是有條件的。在where條件沒有使用主鍵時,照樣會鎖住全表。比如delete from mytable

若是應用程序對查詢性能要求高,就要使用MyISAMMyISAM索引和數據是分開的,而且索引是壓縮的,可以更好的利用內存。所以它的查詢性能明細優於InnodbMyISAM擁有全文索引的功能,這可以極大的優化like查詢的效率。

InnoDB什么時候使用表鎖,什么時候使用行鎖?

  1. 事務需要更新大部分或者全部數據,表又比較大。如果使用默認的行鎖,那么不僅事務效率低,而且可能造成其他事務長時間鎖等待和鎖沖突,這種情況下可以考慮使用表鎖來提高事務的執行速度。
  2. 事務設計到多個表,比較復雜,很可能造成死鎖,造成大量事務回滾。也可以考慮一次性鎖定事務涉及到的表,從而避免死鎖,減少數據庫事務回滾的開銷。

當然應用中若是1,2比較多,那么就可以考慮使用MyISAM表了。


MyISAM和InnoDB的區別:

  1. InnoDB支持事務和外鍵以及行級鎖,MyISAM不支持。
  2. MyISAM讀性能優於InnoDB。
  3. MyISAM索引和數據是分開的,而且索引是壓縮的,而InnoDB索引和數據是緊密捆綁在一起的,無法壓縮,所以InnoDB的體積比MyISAM龐大。
  4. MyISAM引擎索引結構的葉子節點的數據域,存放的並不是實際的數據類型,而是數據記錄的地址。索引文件與數據文件分離,這樣的索引稱之:“非聚簇索引”
  5. InnoDB引擎索引結構的葉子節點的數據域,存放是就是實際的數據記錄。這樣的索引被稱為“聚簇索引”,一個表只能有一個聚簇索引。
  6. InnoDB並不保存表的具體行數,也就是說,執行select count(*) from table時,InnoDB要掃描整個表來計算有多少行,但是MyISAM只要簡單讀出保存好的行數即可。注意的是,當count(*)語句包含where條件時,兩個表的操作是一樣的。
  7. InnoDB表的行鎖也不是絕對的,假如在執行一個SQL語句時MySQL不能確定掃描的范圍,InnoDB表同樣也會鎖全表。
    where條件沒有主鍵時,InnoDB照樣會鎖全表。

2. MyISAM和MEMORY引擎的表鎖

MySQL表級鎖有兩種模式:表共享讀鎖(Table Read Lock)和表獨占寫鎖(Table Write Lock)。

對於MyISAM表的讀操作,不會阻塞其他用戶對同一表的讀操作。但會阻塞對同一個表的寫操作

對於MyISAM表的寫操作,則會阻塞其他用戶對同一表的讀操作和寫操作

2.1 如何加表鎖

MyISAM在執行查詢語句(SELECT)前,會自動給涉及的所有表加讀鎖,再執行查詢操作。
(update、delete、insert)前,會自動給涉及到的表加寫鎖。用戶一般不需要顯示加鎖。

2.2 如何優化MyISAM表鎖

對於MyISAM存儲引擎,雖然使用表級鎖加鎖和解鎖的開銷最小,但是由於加鎖粒度大,所以會造成更多的資源爭用的情況。會較大程度上降低並發處理能力。
優化MyISAM存儲引擎關鍵是提高並發度。

2.2.1 查詢表級鎖爭用情況:

使用show status like 'table%'可以查詢系統內部鎖資源爭用的情況。

 
表級優化

Table_locks_immediate:產生表級鎖定的次數;
Table_locks_waited:出現表級鎖定爭用而等待的次數。

兩個參數值都是系統啟動后開始記錄的,出現一次對應的事件,則數量加一。當Table_locks_waited狀態值比較高時,那么說明系統中表級鎖定爭用現象比較明顯。

2.2.2 縮短鎖定時間:

如何讓鎖定時間盡可能的短呢?唯一的辦法就是讓我們的Query執行的時間盡可能短。

  1. 盡量減少大的復雜的sql,將復雜的query分拆成為小的query分步進行。
  2. 盡可能建立高效索引,讓數據檢索更迅速。
  3. 盡量讓MyISAM存儲引擎的表只存放必要信息,控制字段類型。
  4. 利用合適的機會優化MyISAM表的數據。

2.2.3 分離能並行的操作:

雖然MyISAM是讀寫相互阻塞的表鎖,但是MyISAM存儲引擎還有一個有用的特性,那就是ConcurrentInsert(並發插入)的特性。

  • concurrent_insert=0時,此時讀不能與insert寫共存。
  • concurrent_insert=1時,如果表中沒有空數據塊時,讀可以與insert寫共存,insert新增加的數據將插入到數據文件尾部。(默認設置)。
  • concurrent_insert=2時,不論有沒有空數據塊,insert新增加的數據都將插入到數據文件尾部。

注:在myisam里,如果沒有空的數據塊,新增加的數據都會附加到數據文件的尾部,但是如果經常做update和delete操作,數據文件就不再是連續的,出現了許多空的數據塊,此時再插入數據,按照缺省設置會先看看這些空的數據塊是否能夠容納新插入的數據,如果可以則直接存儲到這些空的數據塊里,如果不行再直接插入到數據文件的尾部。MyISAM的默認的這種處理方式是為了減少數據文件的大小和碎片,以免造成IO性能問題。

如果我們要設置concurrent_insert=2,后面插入的數據都會直接追加到數據文件的尾部,而系統如果有頻繁的deleteupdate操作,可能就會有過多的碎片造成過多的IO消耗,小魚建議如果需要設置concurrent_insert=2一定要定期對表進行(優化)optimizer,以免造成過多的碎片。

可以利用MyISAM存儲引擎的並發插入特性,來解決應用中對同一表的插入和查詢操作。可以將concurrent_insert設置為2,總是允許並發插入;同時通過定期在系統空閑時段執行optimizer ['ɑ:ptɪmaɪzər] table語句來整理空間碎片,

2.2.4 合理利用讀寫優先級:

MyISAM存儲引擎的讀寫時相互阻塞的,但是,一個進程請求某個MyISAM表的讀鎖,同時另一個進程也請求同一個表的寫鎖,MySQL如何處理呢?
默認情況下:寫進程先獲得鎖,不僅如此,即便讀請求先到鎖等待隊列,寫請求后到,寫鎖也會插入到讀鎖之前。

這是由於Mysql的表級鎖定對於讀和寫是有不同優先級設定的,默認情況下寫的優先級要大於讀的優先級。

所以可以利用各自系統的差異覺得寫與讀的優先級:

通過執行命令:set low_priority_updates=1 [praɪˈɒrəti],使該連接的讀請求優先級比寫的優先級高。如果系統是一個以讀為主,可以設置改參數,否則,不需要設置。

通過指定INSERTUPDATEDELETE語句的LOW_PRIORITY屬性,降低該語句的優先級。

另外MySQL也提供了一種折中的方法調節讀寫沖突,即給系統參數max_write_lock_count設置一個合適的值,當一個表的讀鎖達到這個值后,MySQL就暫時將寫請求的優先級降低,給讀進程一定的獲取鎖的機會。

還有一點:一些需要長時間運行的查詢操作,也會使得寫進程“餓死”,因此,應用中盡量避免出現長時間運行的查詢操作。

2.3 Innodb行鎖

總的來說,InnoDB的鎖定機制和Oracle數據庫有不少相似之處。InnoDB的行級鎖分為兩種類型:共享鎖和排他鎖。而在鎖定機制的實現過程中,為了讓行級鎖和表級鎖共存InnoDB也同樣使用了意向鎖(表級鎖定)的概念,於是也就有了意向共享鎖意向排他鎖兩種。

當一個事務需要給自己需要的資源加鎖時,如果遇到有一個共享鎖正鎖定自己需要的資源的時候,自己可以再加一個共享鎖,不過不能加排他鎖。但是,如果遇到自己需要的資源已經被排它鎖占用之后,則只能等待該排他鎖釋放資源之后自己才能獲取資源,並將其鎖定。

2.3.1 什么叫做意向鎖

可以說:Inoodb的鎖定模式實際上可以分為四種:共享鎖(S),排它鎖(X),意向共享鎖(IS),意向排他鎖(IX)

首先,意向鎖到底是做什么用的。
知乎的回答——InnoDB 的意向鎖有什么作用?

首先,申請意向鎖的動作是數據庫完成的,也就是說,事務A申請一行行鎖的時候,數據庫會自動先開始申請表的意向鎖。

那么到底意向鎖有什么用?

首先,意向鎖是——表級鎖。
(1)事務A鎖住了表中的一行,這一行只能讀,不能寫。【事務A加了共享鎖】
(2)事務B申請整個表的寫鎖。【請注意:是整個表!!!】
(3)如果事務B申請成功,那么理論上它就能修改表中的任意一行,這與A持有的共享鎖時沖突的。【事務A不允許修改】
(4)數據庫為避免這種沖突,怎么辦的?就是讓B的申請阻塞,直到A釋放行鎖。
解決方案:
step1:判斷表是否已被其他事務用表鎖鎖住。
step2:判斷表中每一行是否已經被行鎖鎖住。

請注意step2,需要遍歷整個表。效率實在不高。
改進版:
step1:不變
step2:發現表上有意向共享鎖,說明表中有些行被共享行鎖鎖住了,因此,事務B申請表的寫鎖會被阻塞。

 
鎖之間的兼容關系

假如一個表被加了意向排他鎖(IX),證明此時有事務在修改表中具體某行的數據,那么對應的某行可能加了x鎖,1.如果這時候其他事務要再加意向鎖,那么可以加成功(因為加了意向鎖之后,后續查詢或者修改的是某行的數據,這行和上面的x鎖未必沖突)所以意向鎖之間是兼容的。2.如果此時其他事務加的是全表共享鎖S,因為前面表中的數據正在被修改,所以S鎖是加不成功的。所以意向排他鎖和表共享鎖是沖突的。

(敲黑板,划重點)意向鎖的作用就是協調行鎖和表鎖之間的關系的,是將行鎖從另一個角度提高到了表鎖的等級(偽表鎖),與表鎖進行判斷。
注意:select語句不是加鎖!!!

意向鎖是InnDB自動加的,不需要用戶的干預。

對於updatedeleteinsert語句,Innodb會自動給涉及數據集加排他鎖(X);對於普通的select語句,Innodb不會加任何鎖!!!事務可以通過以下語句顯式的給記錄集加鎖:

//共享鎖 select * from table_name where ... lock in share mode; //排它鎖 select * from table_name where ... for update; 

2.3.2 使用共享鎖注意事項

使用 select ... in share mode獲取共享鎖,主要用在數據依存關系時,確認某行記錄是否存在,並且確保沒有人對這個記錄進行update或者delete操作。

(敲黑板,划重點)但是如果當前事務也需要對該記錄進行更新操作,則很有可能造成死鎖,對於鎖定記錄后進行更新的應用,應該使用select...for update方式獲得排他鎖。

2.3.3 InnoDB行鎖實現方式

InnoDB行鎖是通過給索引項加鎖來實現的,只有通過索引條件檢索數據,InnoDB才使用行級鎖,否則,InnoDB將使用表鎖。
在實際開發中,要特別注意InnoDB這一特性,不然,可能造成大量的鎖沖突,從而影響並發!!!

InnoDB使用索引的條件:

  1. 在不通過索引條件查詢的時候,InnoDB確實使用的是表鎖,而不是行鎖。
  2. mysql的行鎖是針對索引加的鎖。不是針對記錄加的鎖,雖然是訪問不同的行,但是若是相同的索引,會出現鎖鎖沖突的。
  3. 當表中含有多個索引的時候,不同的事務可以使用不同的索引鎖定不同的行。
  4. 即使在條件中使用了索引,但是是否使用索引來檢索數據是由MySQL通過判斷不同執行計划的代價決定的,如果MySQL認為全表掃描效率更高,比如很小的表,他也不會使用索引,此時InnoDB將使用表鎖,而不是行鎖。因此,在分析鎖沖突的時候,不要忘記檢查SQL的執行計划,以確定是否真正使用了索引。

關於InnoDB到底是使用行鎖還是表鎖,我們需要依據索引來決定的,本質上行鎖是針對索引加的鎖,而非記錄!!!雖然是訪問不同的行,但是若是含有相同的索引,還是會發生鎖沖突的!!!而且就算條件里面使用了索引,Mysql也不一定走索引,還是要看SQL的執行計划!!!

2.3 間隙鎖

當我們用范圍條件而不是相等條件檢索數據的時候,並請求共享或者排他鎖時,InnoDB會給符合條件的已有的數據記錄的索引項加鎖。

對於鍵值在條件范圍內但是不存在的記錄,叫做間隙(GAP)。InnoDB也會對這個“間隙”加鎖,這種鎖機制就是間隙鎖(Next-Key鎖)

InnoDB使用間隙鎖的目的:

  1. 防止幻讀,以滿足相關隔離級別的要求;
  2. 滿足恢復和復制的需要;

InnoDB的危害:

使用范圍條件檢索並鎖定記錄時,即使某些不存在的鍵值也會被無辜的鎖定。而造成在鎖定的時候無法插入鎖定鍵值范圍內的任何數據。在某些時刻可能會造成很大的危害。

在實際應用開發中,尤其是並發插入比較多的應用,盡量優化業務邏輯,盡量使用相等的條件來訪問更新數據,避免使用范圍條件。

需要特別說明的是:InnoDB除了通過范圍條件加鎖使用間隙鎖外,如果使用相等條件請求給一個不存在的記錄加鎖,InnoDB也會使用間隙鎖。

3. 死鎖

MyISAM表鎖總是一次性獲得所需的全部鎖,要么全部滿足,要不等待,因此不會出現死鎖。

InnoDB中,處理單個sql組成的事務外,鎖是逐步獲得的,當兩個事務都需要獲得對方持有的排他鎖才能完成事務時,這種循環等待就是典型的死鎖。

在InnoDB的事務管理和鎖機制中,有專門檢測死鎖的機制,會在系統產生死鎖之后的很短的時間內就檢測到死鎖的存在。當InnoDB檢測到系統產生了死鎖之后,InnoDB會通過相應的判斷來選出產生死鎖兩個事務中較小的事務回滾,而讓較大的事務成功完成。

但是需要注意,當產生死鎖的場景涉及到的不只是InnoDB存儲引擎的情況下,InnoDB是無法檢測到死鎖的,只能通過鎖的超時限制參數InnoDB_lock_wait_timeout來解決。

需要說明的是,這個參數並不是解決死鎖問題的,而事故在並發高的情況下,如果大量事務因無法立即獲得所需的鎖而掛起,會占用大量計算機資源,造成嚴重的性能問題。甚至拖垮數據庫。我們可以通過設置合適的鎖等待超時的閾值,避免這種情況的發送。

通常來說,死鎖都是應用設計的問題,我們可以通過:

  1. 若不同程序並發存取多個表,應約定以相同的順序來訪問表,這樣可以降低死鎖的機會。
  2. 在程序中以批量的方式處理數據時,如果事先對數據進行排序,保證每個線程按固定順序處理記錄,也可以大大降低死鎖可能。
  3. 在事務中,如果要更新記錄,應該申請足夠級別的鎖(排它鎖),而不是先申請共享鎖,更新時在申請排他鎖。當用戶申請排他鎖時,其他事務可能已經獲得了相同記錄的共享鎖,從而造成鎖等待,甚至死鎖





免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2026 CODEPRJ.COM