MYSQL是在大小公司中使用率極高的開源的關係型數據庫,以其良好的易用性和在分佈式場景下的高性能而著稱,也是全部新手在數據庫入門時的產品首選。最近由於聽了公司的一位師兄關於MYSQL InnoDB鎖的講座,收穫不少,因此將MYSQL鎖相關的必備知識在此進行梳理。這些知識不只能夠幫助面試,也能夠在平常開發進行性能優化或死鎖問題排查時派上用場。固然,最重要的是,在對數據進行上鎖時,就可以梳理出相應的上鎖流程,從而避免真正走到故障時再去排查。html
本文主要包括mysql
MYSQL主要分爲客戶端和服務端,其中客戶端負責對服務端進行鏈接,服務端主要包含兩個部分,其中存儲引擎層(Storage Engines)決定數據在磁盤上具體的存儲形式,典型的存儲引擎包括InnoDb和MyISAM,而目前MYSQL甚至支持混合存儲引擎,便可能一張表一半存儲在InnoDb上,一半存儲在MyISAM。面試
除此之外的其它服務端組建則不關心數據用什麼形式存儲,主要負責執行具體的SQL語句,sql
這裏簡單比較一下InnoDB和MyISAM這兩個存儲引擎。
InnoDB的特性以下:數據庫
MyISAM的特性以下:segmentfault
本文主要基於InnoDB對鎖的特性進行介紹。緩存
一個查詢請求在整個MYSQL服務端的鏈路以下:性能優化
那麼SQL語句在通過解析器和優化器時是什麼樣的一個鏈路呢?
一個標準的Select SQL語句包含如下幾個部分:服務器
select t1.column1 as column1, t2,column2 as column2... from TABLE t1, TABLE t2 ... WHERE condition1 GROUP BY condition2 HAVING condition3 ORDER BY column1 LIMIT N
而這條語句的標準邏輯執行順序以下:架構
這裏有一點須要注意,select語句是在group by和having以後執行,所以select中as出來的列名在group by和having中是不能夠引用的,可是order by中是能夠引用的。
可是真正的的執行順序和標準邏輯執行順序並不必定相同,由於優化器會對SQL的執行順序進行變動,從而儘量提升SQL的執行效率。好比:
select * from table1 t1 join table2 t2 on t1.id = t2.id where t1.count > 10 and t2.count > 100
標準的執行順序會先將表格t1和t2進行join操做,再對join後的結果針對where語句進行篩選。而優化器可能會變化一下執行順序,先根據where t1.count > 10 and t2.count > 100
篩選出t1表和t2表中符合條件的數據,再執行join。
那麼有沒有辦法看到SQL在真實執行的時候的執行計劃呢?這就須要Explain
語法。
Explain關鍵字的使用方法很簡單,只要將其加在具體的SELECT語句以前就能夠,Explain也只能解析SELECT語句。經過Explain關鍵字能夠觀察表的索引是否合理,語句的真實執行順序是否符合預期。Explain執行後生成的數據以下:
列名 | 含義 |
---|---|
id | SELECT語句的SQL_ID,它是指這個語句在查詢中的第n條語句,若是兩個id相同,則表明按照順序執行從上到下執行,id值越大,優先級越高,越先被執行 |
select_type | SELECT語句類型, 如SIMPLE是指不使用UNION或子查詢 |
table | 輸出行所屬的表格,derivex是指從第x步生成的衍生表 |
type | 訪問類型,說明表是如何關聯的 |
possible keys | 可選擇的索引 |
key | 真正選擇的索引。要想強制MySQL使用或忽視possible_keys列中的索引,在查詢中使用FORCE INDEX、USE INDEX或者IGNORE INDEX。 |
key_len | 選中索引的長度,顯示的是索引字段的最大可能長度,是根據表定義得來,而非表內檢索 |
ref | 哪些列或常量被用來查找索引列上的值 |
rows | 預估須要掃描的行數 |
filtered | 預計多少比例的行數會被過濾出來 |
其中訪問類型(type)按照從好到壞包括
select * from user_info where id = 2
SELECT * FROM user_info, order_info WHERE user_info.id = order_info.user_id
SELECT * FROM user_info, order_info WHERE user_info.id = order_info.user_id AND order_info.user_id = 5
value IN (SELECT primary_key FROM single_table WHERE some_expr)
在知道這些以後,使用Explain分析語句時能夠按照以下思路進行分析:
這裏建議看一下參考文章中的Explain實戰例子文章來加深經過Explain進行優化的思路
ATOMICY原子性:事務要麼所有執行,要麼所有不執行
CONSISTENCY一致性:事務執行前和執行後數據狀態應當一致
ISOLATION隔離性:事務之間不會相互影響
DURABILITY持久性:事務執行完成後結果不會丟失,所以須要可以對數據進行恢復
隱式事務:在autocommit爲true的狀況下,默認每一條語句都會開啓一個事務執行,執行完畢後提交事務。所以不在事務上下文中執行select * from user where id = 1 for update
語句在語句執行完後就會釋放排他鎖,這在大多數狀況下都是不合理的。
顯式事務:每一個事務以start transaction開啓,以commit或rollback結束。Spring中使用@Transactional或是transactionTemplate包圍的代碼段
事務總共有4個隔離級別:
髒讀:一個事務中未提交的語句會被另外一個事務察覺
不可重複讀:一個事務中提交的update語句會被另外一個事務察覺
幻讀:一個事務中提交的insert語句會被另外一個事務察覺
鎖主要分爲表鎖和行鎖。顧名思義,表鎖就是指對整張表進行上鎖,而行鎖則是指針對一行數據進行上鎖。表鎖一般在服務器層面實現,而行鎖每每在存儲引擎層實現。行鎖並非只對數據行上鎖,還能夠對索引/索引區間進行上鎖,即強調的是粒度更小的鎖。
鎖能夠分爲如下四類:
意向鎖和意向鎖之間是兼容的,而意向鎖和行鎖之間也是兼容。意向鎖主要是對錶鎖的優化。假如如今有一個事務須要對錶a加排他鎖,若是沒有意向鎖,就須要對全表進行掃描,直到找到第一個共享/排他鎖。而經過判斷是否有意向鎖,能夠極大的提升鎖互斥判斷的性能。加意向鎖是在全部鎖(行鎖/表鎖)以前進行判斷和執行的。
行鎖具體有三種實現:
只在可重複度REPEATABLE READ或以上的隔離級別下的特定操做纔會取得gap lock或nextkey lock。
讀已提交REPEATABLE COMMIT級別下只有record lock
MYSQL默認爲RR
所以當判斷語句如何加行鎖時,須要根據事務隔離級別+是否使用主鍵/惟一鍵/索引進行判斷。
加鎖順序本質上和索引的查詢順序是一致的
這裏有一種最糟糕的狀況,即若是where條件中的字段不是主鍵/索引/惟一索引,則會先對所有索引上排他鎖,在找到符合條件的記錄後,解鎖不知足條件的鎖。
MYSQL架構
Explain關鍵字
MYSQL性能優化神器Explain
Explain實戰例子
詳解 MySql InnoDB 中意向鎖的做用
幻讀