MySQL知識點彙集
索引的本質
索引是幫助MySQL高效獲取資料的排好序的資料結構;
比如現在一百個數,那麼如何快速獲取中間值,常見的可以用二叉樹,來快速定位資料,減少查詢的次數;
索引的選擇
常見的資料結構有:
https://www.cs.usfca.edu/~galles/visualization/Algorithms.html 這個網頁可以幫助理解各種樹
-
二叉樹
- 如果次數有一萬個數為(1,2,3....),那麼二叉樹會有一萬層,更像一個連結串列結構。這種結構很顯然是不便於快速定位資料的。因為如果要查詢10000那麼需要10000次io才能檢索到索引;
-
紅黑樹
- 紅黑樹是一種平衡二叉樹,較於二叉樹會減少層次數量,但是隨著資料量增加,紅黑樹的層級也會增加;
-
Hash表
- Hash表只能針對於單資料查詢,如select * from user where id=888;這種查詢會先hash(888)算出索引地址,然後拿到資料的地址。可以極快的查詢到資料;
- 但是如果是範圍查詢,那麼hash便無法支援;
以上,所以MySQL索引最好保證樹的高度(14層),以此減少io的次數(14次),來達到快速查詢的目的;
-
B樹
- 比較矮胖,即縮小高度,將每層級的資料增多。
- 每個節點都有data,這樣查詢的時候一次性獲取一個層級的節點放到記憶體,在記憶體中可以快速查詢;
- 因為獲取一個層級節點到記憶體是有大小限制的,即如果節點過大,那麼獲取的數量會減少;
- innodb_page_size的預設值是16kb, 即一個層級的節點最好16kb.
MySQL最終選擇的是B+樹
- B+樹(B樹的變種)
- 較於B樹,B+樹的非葉子節點不儲存data。這樣一次io可以獲取更多的節點;
- 較於B樹,B+樹非葉子節點會冗餘索引,即每層會有重複的,這樣做事為了查詢更快;
- 較於B樹,B+樹葉子節點中有指標連線,這樣是為了範圍查詢的的時候可以直接往下查詢,而不用從第一層索引開始重頭開始,減少回表;
- 較於B樹,B+樹的非葉子節點不儲存data。這樣一次io可以獲取更多的節點;
假如一個節點是16KB,且主鍵是Bigint(佔用記憶體8B) ,主鍵間儲存的記憶體地址假設佔用記憶體B,那麼一層可以儲存1170個節點;依次類推,這樣的高度為3的B+樹大概可以儲存2000W資料;
儲存引擎
MySQL儲存引擎是形容表的;
MyISAM儲存引擎
檔案儲存格式為三個:
- frm 表結構
- MYD 資料
- MYI 索引
採用B+樹 樹的子節點儲存的是資料的地址,獲取到資料地址後再去MYD檔案查詢具體資料;
這種查詢方式的索引也叫做非聚集索引
INNODB儲存引擎
檔案儲存格式為兩個:
- frm 表結構
- IBD 資料+索引
採用B+樹 樹的子節點儲存的就是資料,減少了回表。根據索引可以直接查詢到資料;
這種查詢方式的索引叫做聚集索引 也叫聚簇索引
為什麼INNODB要求必須有主鍵且推薦採用自增主鍵,而MyISAM卻不需要?
- 因為InnoDB要求資料有序,如果插入的是非自增主鍵,隨機插入現有索引頁中間,可能會造成資料移動,增加開銷。
- MyISAM 插入資料就是,如果中間資料頁有空位就直接插入,沒有就在現有資料頁後面插入,不涉及資料移動。
聯合索引是怎麼樣的?
-
如上圖,可以看到圖是聯合索引(col1,col2,col3)
-
因為不是主鍵索引,所以葉子節點不儲存data, 那麼在非聚集索引中,即使複合列索引,非葉節點也儲存三個值
-
非葉子節點也是儲存三列,不過是先按照第一欄位排序,相同按第二欄位排序,依次類推
mysql(innodb)是索引組織表,葉子節點是存的表的主鍵
-
按照col1,col2,col3的順序依次查詢後可以找到主鍵id。再根據主鍵id進行索引查詢;
-
如果where條件只有col2,那麼很顯然我們無法定位一個具體的索引區間,這種原則就是最左側列匹配原則
理解以上聯合索引後,我們可以很清晰的理解索引的匹配原則;
MySQL 索引匹配原則
在我們瞭解到MySQL底層資料結構B+樹後,這些規則其實很好理解;
索引匹配原則如下:
- 等值匹配
- 最左側列匹配
- 最左字首匹配
- 範圍查詢
- 等值匹配+範圍查詢
假設現在有一張表student_score,表中有5個欄位,id,class, name,course,score,其中id為主鍵,有四條資料
- id:1 class:1班 name:張三 course:數學 score 90
- id:2 class:1班 name:張三 course:語文 score 50
- id:3 class:3班 name:李四 course:數學 score 50
- id:4 class:3班 name:李四 course:語文 score 20
並且我們以class、name、course建立了聯合索引;
等值匹配
假設現在有一條sql如下
select * from student_score where class = '1班' and name = '張三' and course = '數學'
這條語句中就會用到所有的索引欄位,首先找class,然後查詢name,最後查詢course
最左側列匹配
假設有一條sql如下
select * from student_score where course = '數學'
該SQL語句就不會用到索引,因為B+樹中,索引的使用是從左到右的,不能跳過左邊的,直接查詢右邊的,我們可以用explain來看一下
確定這裡是沒有用到索引的,這條SQL可以改成
select * from student_score where class = '1班' and name = '張三'
這樣就可以用到索引了
因此在建立索引的時候,我們需要考慮表中的欄位,到底哪些欄位是最常被用於查詢的
最左字首匹配
這條原則適用於模糊匹配的時候,也就是需要用到like的時候,假設有一條SQL如下
select * from student_score where class like '1%'
這裡是可以用到索引的,印在B+索引樹中,資料都是按照欄位來排序的,比如這裡有聯合索引 key(class、name、course) ,那麼資料會按照class排序
但是如果你把SQL寫成了下面這樣,就用不到索引了,因為匹配不到最左字首
select * from student_score where class like '%班'
範圍查詢
我們可以通過> 、 <這種範圍比較來查詢資料
select * from student_score where class > '1班' and class < '3班'
通過explain,可以看到,這條sql是用到了索引的
但是如果你把sql改成下面這樣的,就不會用到索引了
select * from student_score where name > '張三'
因為該聯合索引的B+樹中只能根據class欄位來進行範圍查詢,也就是聯合索引中最左側的欄位
等值匹配+範圍查詢
假設有一條SQL如下
select * from student_score where class = '1班' and name > '張三' and course > '數學'
通過explain分析後,可以看到,這裡也會用到索引
併發事務帶來的問題
-
髒讀(Dirty read): 當一個事務正在訪問資料並且對資料進行了修改,而這種修改還沒有提交 到資料庫中,這時另外一個事務也訪問了這個資料,然後使用了這個資料。因為這個資料是還沒 有提交的資料,那麼另外一個事務讀到的這個資料是“髒資料”,依據“髒資料”所做的操作可能是 不正確的。
-
丟失修改(Lost to modify): 指在一個事務讀取一個數據時,另外一個事務也訪問了該資料, 那麼在第一個事務中修改了這個資料後,第二個事務也修改了這個資料
-
指在一個事務內多次讀同一資料。在這個事務還沒有結束 時,另一個事務也訪問該資料。那麼,在第一個事務中的兩次讀資料之間,由於第二個事務的修 改導致第一個事務兩次讀取的資料可能不太一樣
-
幻讀與不可重複讀類似。它發生在一個事務(T1)讀取了幾行資料,接 著另一個併發事務(T2)插入了一些資料時。在隨後的查詢中,第一個事務(T1)就會發現多了 一些原本不存在的記錄,就好像發生了幻覺一樣,所以稱為幻讀。
Mysql隔離級別
- READ-UNCOMMITTED(讀取未提交): 最低的隔離級別,允許讀取尚未提交的資料變更,可能會導 致髒讀、幻讀或不可重複讀。
- READ-COMMITTED(讀取已提交): 允許讀取併發事務已經提交的資料,可以阻止髒讀,但是幻讀 或不可重複讀仍有可能發生。
- REPEATABLE-READ(可重複讀): 對同一欄位的多次讀取結果都是一致的,除非資料是被本身事務 自己所修改,可以阻止髒讀和不可重複讀,但幻讀仍有可能發生。
- SERIALIZABLE(可序列化): 最高的隔離級別,完全服從ACID的隔離級別。所有的事務依次逐個 執行,這樣事務之間就完全不可能產生干擾,也就是說,該級別可以防止髒讀、不可重複讀以及 幻讀。
MySQL 鎖
表鎖 & 行鎖
- 只有明確指定主鍵,才會執行行鎖,否則執行表鎖;
- 上面意思就是隻有可以找到具體的列才會執行行鎖;
- 若匹配不到資料則無鎖;
- 如資料表有id=1,2,3三條資料,select * form user where id=4,此時是沒有鎖的;
鎖演算法
行鎖演算法
-
Record Lock 普通行鎖
- 鍵值在條件範圍內
- 記錄存在
-
Gap Lock 間隙所
- 鎖定一個範圍,不包括記錄本身
- 對於鍵值不存在條件範圍內,叫做間隙(GAP),引擎就會對這個間隙加鎖;
- 比如: select * from user where id > 49 for update 那麼遵循左開右閉原則,會增加(49, max]的間隙鎖
-
Next-key Lock 行&間隙
-
鎖定一個範圍,包含記錄本身
-
在鍵值範圍條件內,同時鍵值又不存在條件範圍內;
-
在可重複讀隔離級別下才會生效;
-
表鎖演算法
-
意向鎖(升級機制)
- 當一個事務帶著表鎖去訪問一個加了行鎖的資源,那麼此時,這個行鎖會升級成意向鎖,將表鎖住;
此時有兩條資料,Id=10, name=jjj和Id=20, name=kkk
# 事務A(行鎖)
select * from user where id = 10 for update;
# 事務B(表鎖)
select * from user where name = 'kkk' for update;
此時執行事務B是拿不到鎖的,因為在事務B是帶著表鎖去的,此時事務A的鎖會升級成表鎖(意向鎖);
-
自增鎖
- 如果一個事務正在往表裡插入自增記錄,其他事務都必須等待;
鎖的實現
共享鎖 & 排它鎖
是行鎖與表鎖的一個具體實現
- 共享鎖:
- 允許一個事務去讀一行,阻止其他事務去獲取該行的排它鎖;
- 一般理解: 能讀,不能寫
- 排它鎖(X): 寫鎖
- 允許持有排它鎖的事務讀寫事務,阻止其他事務獲取該資源的共享鎖與排它鎖;
- 不能獲取任何鎖,不代表不能讀
某個事務獲取資料的排它鎖,其他事務不能獲取該資料的任何鎖,並不代表其他事務不能無鎖讀取該資料;