1. 程式人生 > 實用技巧 >MySQL知識點彙集

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+樹葉子節點中有指標連線,這樣是為了範圍查詢的的時候可以直接往下查詢,而不用從第一層索引開始重頭開始,減少回表;

假如一個節點是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): 寫鎖
    • 允許持有排它鎖的事務讀寫事務,阻止其他事務獲取該資源的共享鎖與排它鎖;
    • 不能獲取任何鎖,不代表不能讀

某個事務獲取資料的排它鎖,其他事務不能獲取該資料的任何鎖,並不代表其他事務不能無鎖讀取該資料;