MyISAM
和MEMORY支持表鎖
Innodb
既支持行鎖,也支持表鎖,默認行鎖
檢查table_locks_waited
和table_locks_immediate
狀態變量分析html
table_locks_waited 的值越高,則說明存在嚴重的表級鎖的爭用狀況數據庫
MySQL的表鎖有兩種模式服務器
鎖模式的兼容以下表session
是否兼容 | 請求none | 請求讀鎖 | 請求寫鎖 |
---|---|---|---|
當前處於讀鎖 | 是 | 是 | 否 |
當前處於寫鎖 | 是 | 否 | 否 |
可見,對MyISAM表的讀操做,不會阻塞其餘用戶對同一表的讀請求,但會阻塞對同一表的寫請求;數據結構
對MyISAM表的寫操做,則會阻塞其餘用戶對同一表的讀和寫請求;併發
MyISAM表的讀和寫操做之間,以及寫和寫操做之間是串行的!(當某一線程得到對一個表的寫鎖後,只有持有鎖的線程能夠對錶進行更新操做.其餘線程的讀、寫操做都會等待,直到鎖被釋放爲止)性能
對於 MyISAM 引擎優化
select
前,會自動給涉及的全部表加 讀 不須要用戶直接顯式用lock table
命令spa
對於給MyISAM顯式加鎖,通常是爲了在必定程度上模擬事務操做,實現對某一個時間點多個表一致性讀取線程
total
subtotal
假設咱們須要檢查這兩個表的金額合計是否相符,可能就須要執行以下兩條SQL
圖片上傳失敗...(image-3017e3-1547370332969)
這時,若是不先給這兩個表加鎖,就可能產生錯誤的結果;
由於第一條語句執行過程當中,order_detail
表可能已經發生了改變.
所以,正確寫法應該以下
圖片上傳失敗...(image-8081d7-1547370332969)
session1 | session2 |
---|---|
得到表 film_text 的讀鎖 lock table film_text read; |
|
可select * from film_text | 可select * from film_text |
不能查詢沒有鎖定的表 :select * from film | 可查詢/更新未鎖定的表: select * from film |
插入或更新鎖定表會提示錯誤 update...from film_text | 更新鎖定表會等待 update...from film_text |
釋放鎖 unlock tables | 等待 |
| 得到鎖,更新成功 |
當使用lock tables
時,不只須要一次鎖定用到的全部表
且同一表在SQL語句中出現多少次,就要經過與SQL語句中別名鎖多少次
lock table actor read複製代碼
會提示錯誤
select a.first_name.....複製代碼
須要對別名分別鎖定
lock table actor as a read,actor as b read;複製代碼
在必定條件下,MyISAM
也支持併發插入和讀取
控制其併發插入的行爲,其值分別能夠爲
能夠利用MyISAM
的併發插入特性,來解決應用中對同表查詢和插入的鎖爭用
例如,將concurrent_insert
系統變量設爲2,老是容許併發插入;
同時,經過按期在系統空閒時段執行OPTIONMIZE TABLE語句來整理空間碎片,收到因刪除記錄而產生的中間空洞
刪除操做
不會重整整個表,只是把 行 標記爲刪除,在表中留下空洞
MyISAM傾向於在可能時填滿這些空洞,插入時就會重用這些空間,無空洞則把新行插到表尾
MyISAM
的讀和寫鎖互斥,讀操做串行的
MyISAM
表的讀鎖,同時另外一個進程也請求同表的寫鎖,MySQL如何處理呢?這是由於MySQL認爲寫請求通常比讀請求重要
這也正是MyISAM
表不適合有大量更新 / 查詢
操做應用的緣由
大量的更新操做會形成查詢操做很難得到讀鎖,從而可能永遠阻塞
幸虧,咱們能夠經過一些設置來調節MyISAM
的調度行爲
low-priority-updates
SET LOW_PRIORITY_UPDATES=1
LOW_PRIORITY
屬性雖然上面3種方法都是要麼更新優先,要麼查詢優先,但仍是能夠用其來解決查詢相對重要的應用(如用戶登陸系統)中,讀鎖等待嚴重的問題
另外,MySQL也提供了一種折中的辦法來調節讀寫衝突;
即給系統參數max_write_lock_count
設置一個合適的值;
當一個表的讀鎖達到這個值後,MySQL便暫時將寫請求的優先級下降,給讀進程必定得到鎖的機會
* * *
InnoDB與MyISAM的最大不一樣有兩點
行級鎖和表級鎖原本就有許多不一樣之處,另外,事務的引入也帶來了一些新問題
一組SQL語句組成的邏輯處理單元
相對於串行處理來講,併發事務處理能大大增長數據庫資源的利用率,提升數據庫系統的事務吞吐量,從而能夠支持能夠支持更多的用戶
但併發事務處理也會帶來一些問題,主要包括如下幾種狀況
在併發事務的問題中,「更新丟失」一般應該是徹底避免的;
但防止更新丟失,並不能單靠數據庫事務控制器來解決,須要應用程序對要更新的數據加必要的鎖來解決,所以,防止更新丟失應該是應用的責任
「髒讀」、「不可重複讀」和「幻讀」,其實都是數據庫讀一致性
問題,必須由數據庫提供必定的事務隔離機制來解決
數據庫實現事務隔離的方式,基本能夠分爲如下兩種
加鎖
,防止其餘事務對數據進行修改 不加任何鎖
,經過必定機制生成一個數據請求時間點的一致性數據快照
,並用這個快照來提供必定級別(語句級或事務級)的一致性讀取.數據庫的事務隔離級別越嚴格,併發反作用越小,但付出的代價也越大
由於事務隔離實質上就是使事務在必定程度上「串行化」進行,這顯然與「併發」矛盾
爲了解決「隔離」與「併發」的矛盾,ANSI SQL定義了4種隔離級別
隔離級別/讀數據一致性及容許的併發反作用 | 讀數據一致性 | 髒讀 | 不可重複讀 | 幻讀 |
---|---|---|---|---|
未提交讀(Read uncommitted) | 最低級別,只能保證不讀取物理上損壞的數據 | 是 | 是 | 是 |
已提交度(Read committed) | 語句級 | 否 | 是 | 是 |
可重複讀(Repeatable read) | 事務級 | 否 | 否 | 是 |
可序列化(Serializable) | 最高級別,事務級 | 否 | 否 | 否 |
若是發現爭用比較嚴重,如
Innodb_row_lock_waits
和Innodb_row_lock_time_avg
的值比較高
進一步觀察發生鎖衝突的表,數據行等,並分析鎖爭用的緣由
默認狀況每15秒會向日志中記錄監控的內容;
若是長時間打開會致使.err文件變得很是巨大;
因此確認緣由後,要刪除監控表關閉監視器,或者經過使用--console選項來啓動服務器以關閉寫日誌功能
InnoDB支持如下兩種類型的行鎖
MySQL InnoDB默認行級鎖
行級鎖都是基於索引的,若一條SQL語句用不到索引是不會使用行級鎖的,會使用表級鎖把整張表鎖住
爲了容許行/表鎖共存,實現多粒度鎖機制,InnoDB還有兩種內部使用的意向鎖(Intention Locks)
這兩種意向鎖都是表鎖
當前鎖/是否兼容/請求鎖 | X | IX | S | IS |
---|---|---|---|---|
X | 衝突 | 衝突 | 衝突 | 衝突 |
IX | 衝突 | 兼容 | 衝突 | 兼容 |
S | 衝突 | 衝突 | 兼容 | 兼容 |
IS | 衝突 | 兼容 | 兼容 | 兼容 |
若是一個事務請求的鎖模式與當前鎖兼容,InnoDB就請求的鎖授予該事務;
反之,若是二者二者不兼容,該事務就要等待鎖釋放
意向鎖是InnoDB自動加的,不需用戶干預.
對於UPDATE、DELETE和INSERT語句,InnoDB會自動給涉及及數據集加排他鎖(X);
對於普通SELECT語句,InnoDB不會加任何鎖.
對於SELECT語句,能夠經過如下語句顯式地給記錄加讀/寫鎖
共享鎖語句主要用在須要數據依存關係時確認某行記錄是否存在;
並確保沒有人對這個記錄UPDATE或DELETE.
但若是當前事務也須要對該記錄進行更新,則頗有可能形成死鎖;
對於鎖定行記錄後須要進行更新操做的應用,應該使用排他鎖語句.
session_1 | session_2 |
---|---|
set autocommit=0,select * from actor where id =1 | set autocommit=0,select * from actor where id =1 |
當前seesion對id爲1的記錄加入共享鎖 select * from actor where id =1 lock in share mode | |
| 其餘seesion仍然能夠查詢,並對該記錄加入 select * from actor where id =1 lock in share mode |
當前session對鎖定的記錄進行更新,等待鎖 update。。。where id=1 | |
| 當前session對鎖定記錄進行更新,則會致使死鎖退出 update。。。where id=1 |
| 得到鎖,更新成功 |
session_1 | session_2 |
---|---|
set autocommit=0,select * from actor where id =1 | set autocommit=0,select * from actor where id =1 |
當前seesion對id爲1的記錄加入for update 共享鎖 select * from actor where id =1 for update | |
| 可查詢該記錄select from actor where id =1,可是不能再記錄共享鎖,會等待得到鎖select from actor where id =1 for update |
更新後釋放鎖 update。。。 commit | |
| 其餘session,得到鎖,獲得其餘seesion提交的記錄 |
行鎖是經過給索引上的索引項加鎖來實現
若是沒有索引,InnoDB將經過隱藏的聚簇索引來對記錄加鎖
行鎖實現特色意味着:
若是不經過索引條件檢索數據,那麼Innodb將對錶的全部記錄加鎖,和表鎖同樣
當咱們用範圍條件而不是相等條件檢索數據,並請求共享或排他鎖時,InnoDB會給符合條件的已有數據的索引項加鎖;
對於鍵值在條件範圍內但並不存在的記錄,叫作「間隙(GAP)」,InnoDB也會對這個「間隙」加鎖,這種鎖機制就是所謂的間隙鎖(Next-Key鎖).
舉例來講,假如emp表中只有101條記錄,其empid的值分別是1,2,...,100,101,下面的SQL:
InnoDB 不只會對符合條件的 empid 值爲 101 的記錄加鎖;
也會對 empid
大於101
(這些記錄並不存在)的「間隙」加鎖
相等條件來訪問更新數據
,避免使用範圍條件. 對於InnoDB,在絕大部分狀況下都應該使用行鎖
由於事務
,行鎖
每每是咱們選擇InnoDB的理由
但在個別特殊事務中,也能夠考慮使用表鎖
固然,應用中這兩種事務不能太多,不然,就應該考慮使用MyISAM
在InnoDB下 ,使用表鎖要注意
LOCK TALBES
雖然能夠給InnoDB
加表鎖InnoDB
引擎層管理的,而是由其上一層MySQL Server負責;autocommit=0、innodb_table_lock=1(默認設置)
,InnoDB 引擎層才知道MySQL加的表鎖,MySQL Server才能感知InnoDB加的行鎖;LOCK TALBES
對InnoDB
鎖時要注意,要將autocommit
設爲0,不然MySQL不會給表加鎖UNLOCK TALBES
釋放表鎖,由於它會隱式地提交事務LOCK TALBES
加的表鎖,必須用UNLOCK TABLES釋放表鎖,正確的方式見以下語句 MyISAM表鎖是deadlock free的,這是由於MyISAM老是一次性得到所需的所有鎖,要麼所有知足,要麼等待,所以不會出現死鎖
但在InnoDB中,除單個SQL組成的事務外,鎖是逐步得到的,這就決定了InnoDB發生死鎖是可能的
發生死鎖後,InnoDB通常都能自動檢測到,並使一個事務釋放鎖並退回,另外一個事務得到鎖,繼續完成事務
一般來講,死鎖都是應用設計的問題,經過調整業務流程、數據庫對象設計、事務大小、以及訪問數據庫的SQL語句,絕大部分均可以免
下面就經過實例來介紹幾種死鎖的經常使用方法。
相同的順序
訪問表 事先對數據排序
,保證每一個線程按固定的順序來處理記錄 直接申請排他鎖,而不該該先申請共享鎖
可重複讀
下,若是兩個線程同時對相同條件記錄用SELECT...ROR UPDATE
加排他寫鎖SELECT...FOR UPDATE
若是出現死鎖,能夠用SHOW INNODB STATUS命令來肯定最後一個死鎖產生的緣由和改進措施。
共享讀鎖和排他寫鎖
之間,以及排他寫鎖之間
互斥,即讀寫串行 MyISAM
容許查詢/插入併發,可利用這一點來解決應用中對同一表查詢/插入的鎖爭用問題 MyISAM
默認的鎖調度機制是寫優先,這並不必定適合全部應用,用戶能夠經過設置LOW_PRIPORITY_UPDATES
參數或在INSERT、UPDATE、DELETE語句中指定LOW_PRIORITY
選項來調節讀寫鎖的爭用 MyISAM
表可能會出現嚴重的鎖等待,能夠考慮採用InnoDB表來減小鎖衝突 在瞭解InnoDB的鎖特性後,用戶能夠經過設計和SQL調整等措施減小鎖衝突和死鎖
本文由博客一文多發平臺 OpenWrite 發佈!