日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

数据结构与索引-- mySql索引诡异事件

發布時間:2023/12/4 编程问答 59 豆豆
生活随笔 收集整理的這篇文章主要介紹了 数据结构与索引-- mySql索引诡异事件 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

什么時候使用B+樹索引

  • 并不是所有查詢條件下出現的列都需要添加索引。對于什么時候添加索引,我們通過經驗判斷,訪問表中很少一部分行時候,使用B+樹索引才有意義。
  • 對于性別字段,地區字段,類型字段,他們取值范圍很少,即選擇性低。如下sql
select * from moment where status = 1;
  • 對于性別,狀態,可取值范圍局限性非常大。對于上述SQL得到的結果可能是該表50% 的數據(假設2中狀態),這時候,添加B+樹索引完全沒有必要。相反,如果某個字段取值范圍不固定,幾乎沒有重復,即高選擇性,此時使用B+樹索引是最合適的,例如nickName 昵稱字段,基本上一個應用中都不給你重復出現。
  • 如上,當訪問選擇性高的字段并從表中取出很少一部分行時候,對這個字段添加B+樹索引是非常有必要的,但是如果出現了訪問字段是高選擇性的,但是取出的行數據占表中大部分數據時候,MySql數據庫可能就不會使用B+樹索引了,我們看如下一個案例。
show index from New_UserBaseInfo;

  • 表New_userBaseInfo大約有450W條數據。encodePhone字段上有一個索引,這時候,我們查找 ‘89F5F342F1ABE260F4F3D728174CF379’ 這個加密手機號時候,得到如下執行計劃:
explain select * from New_UserBaseInfo where encodePhone = '89F5F342F1ABE260F4F3D728174CF379'

  • 可以看到 使用了idx_encodePhone這個索引,也符合我們前面提到的套選擇性,選取表中很少行的原色,但是入座執行下面的這條語句:
explain select * from New_UserBaseInfo where encodePhone > '89F5F342F1ABE260F4F3D728174CF379'

  • 可以看到possible_keys依然是idx_encodePhone,但是實際上優化器使用的索引key是null, 而且type是ALL,說他他匹配到了對應的索引,但是他并沒有使用索引去查下,還是全表查詢。way???
  • 因為這不符合我們之前說的規則,雖然encodePhone 這個字段的值是高選擇性,但是我們取出的行數據中占了表中的一大部分數據。可以看到rows顯示的是44W+ 數據,包括99%的數據了。因此查詢優化器并沒有使用索引
  • 也許會有疑問,查找加密手機號大于 89F5F342F1ABE260F4F3D728174CF379 的字段,這種情況幾乎不太可能出現。的確如此,但是我們考慮New_UserBaseInfo上 的lastRegisterTime 字段(注冊時間),改字段日期類型,字段上有一個idx_lastRegisterTime 非唯一索引,看如下兩條查詢語句:
explain select * from New_UserBaseInfo where lastRegisterTime < '2019-06-24';

explain select * from New_UserBaseInfo where lastRegisterTime < '2019-06-25';

  • 查找用戶注冊時間小于某個時間的sql語句。前后兩條SQL只相差1天時間,2條SQL語句的執行計劃竟然不一樣。在第二條SQL執行的時候,雖然同樣使用的idx_lastRegisterTime索引,但是優化器卻沒有使用這個索引,而是對全表掃描。

  • MySQL數據庫的查詢優化器會通過explain的rows字段預估查詢可能的到的行,如果大于某個值,則B+樹會選擇全表掃描。這個值大概是在20% 左右數據總量的時候會觸發。即當我們取出的數據占比超過全數據量的20% 的時候,優化器不會使用索引,而是全表掃描。

  • 但是rows中預估的數據并不是絕對準確的,可以看大優化器判斷日期小于2019-06-24 的數據是:757912,但是實際值:462377

  • 實際值少了大概38%,這可能對查詢優化器的選擇產生一定的影響,如果對比強制使用索引和使用優化器選擇的全表掃描來查詢注冊日期小于2019-06-25的數據,最終發現如下:

select * from New_UserBaseInfo force index(idx_lastRegisterTime)where lastRegisterTime < '2019-06-25';select * from New_UserBaseInfo where lastRegisterTime < '2019-06-25';
  • 查詢時間分別是1.45s,5.8s,第一句SQL強制使用idx_lastRegisterTime 索引,所用的時間是4.15s,根據優化器選擇的全表掃方式,執行第二SQL確5.8s,因此優化器的選擇并不完全是正確的,有時候需要自己去判斷。

順序讀,隨機讀與預讀取

  • 之前介紹的規則中,索引使用原則,高選擇,取出表中少部分數據。但是為什么只能是少部分數據?這就和InnoDB的順序讀和隨機讀取有關系
    • 順序讀:是指定順序的讀取磁盤上的快(Block);隨機讀(Random Read)是指訪問的快不是連續的,需要磁盤的磁頭不斷移動。當前傳統機械磁盤的瓶頸之一就是隨機讀取的速度較低。
    • 在網上找的資料:同時對比RAID(磁盤陣列)開啟write back 和write Through的性能差異。測試磁盤是由4塊15000轉的硬盤組成的RADI 10.測試文件大小2GB,塊大小64KB。
    • Write-through:CPU向cache寫入數據時,同時向memory(后端存儲)也寫一份,使cache和memory的數據保持一致
    • Write-back:cpu更新cache時,只是把更新的cache區標記一下,并不同步更新memory(后端存儲)。只是在cache區要被新進入的數據取代時,才更新memory(后端存儲)。
write backwrite Through
順序讀193.7665.333
隨機讀82.11716.218
  • 可以看到,不管是否開啟RAID卡的Write Back功能,磁盤的隨機讀性能都遠遠小于順序讀的性能。而上表中也說明了Write Back相對于Write Through 的性能提升。
  • 在數據庫中,順序讀是指根據索引的葉節點數據就能順序的讀取所需要的行數據。這個順序只是邏輯上的順序讀取,在物理磁盤上,行對應的數據可能還是隨機分布在磁盤上的不同地址。但是相對來說,物理磁盤上的數據還是比較順序的,因為B+樹的構建是根據區來管理的,區是64個連續的頁。如根據主鍵進行讀取,或者通過輔助索引的葉節點就能讀取到數據。
  • 隨機讀,一般指訪問輔助索引葉節點不能完全得到的結果,需要根據輔助索引葉節點中的主鍵去找時機行數據。一般說來,輔助索引和主鍵所在的數據段是不同的,因此訪問是隨機的方式
  • 之前的sql lastRegisterTime < ‘2019-06-25’ 這條就是典型的隨機讀取。而正是因為讀取的方式是隨機的,并且隨機讀的性能會遠低于順序讀取,因此優化器才會選擇全表掃描的方式,而不是走 idx_lastRegisterTime 這個索引。
預讀取
  • InnoDB存儲引擎為了提高讀性能,引入了預讀取技術。預讀取是通過一次IO請求將多個頁面預讀取緩沖池中,并且估計預讀取的多個頁馬上會被訪問。傳統的IO請求每次只讀取一個頁,在傳統機械硬盤較低的IOPS下。預讀取技術可以大大提高讀取性能。

  • InnoDB有兩個預讀取的方法,隨機預讀取(Random read ahead)和線性預讀取(linear read ahead)

    • 隨機預讀取:指定一個區(64個連續的頁)中的13個頁面也在緩沖區中,并且在LRU列表的前端(即頁是被頻繁訪問),則InnoDB存儲引擎會將這個區中神域的所有頁預讀到緩沖區。
    • 線性預讀取基于緩沖池中的頁的訪問模式,而不是數量。如果一個區中的24個頁都被順序訪問了,則InnoDB存儲引擎會讀取下一個區的所有頁。
    • LRU頁解析:Innodb為了加快對磁盤中數據的操作,在操作磁盤上的數據時,會先把數據存放到一塊名為Buffer Pool的內存緩沖池中,但是內存的大小遠小于磁盤的大小,因此需要一種機制來淘汰非熱點數據,保證內存中存在的數據是較為頻繁訪問的數據。LRU是這種管理場景下最常用的算法,類似Redis中的LRU淘汰算法:
      • 新數據插入到鏈表頭部;
      • 每當緩存命中(即緩存數據被訪問),則將數據移到鏈表頭部;
      • 當鏈表滿的時候,將鏈表尾部的數據丟棄。
  • InnoDB1.0.4 開始,縮進訪問的預讀取被取消了,而線性預讀取還是保留了,并且加入了innodb_read_ahead_threshold參數,改參數標識一個區中的多少個頁面被順序訪問時候,InnoDB存儲引擎才開啟預讀取,即預讀下一個區中所有頁。默認值是56,當一個區中56個頁都被訪問過,則預讀下一個區的所有項。

show VARIABLES like 'innodb_read_ahead_threshold'

  • 固態硬盤的情況,固態硬盤沒有讀寫磁頭,讀取不需要旋轉,因此隨機讀取性能得到質的提高。因為固態硬盤現在并沒有全面普及,所InnoDB存儲引擎中沒有見到對固態硬盤相關的一些優化。

輔助索引的優化

  • 輔助索引的葉子節點包含主鍵,但是輔助索引的葉子節點不包含完整的行信息,因此,InnoDB存儲引擎總是會先從輔助索引的葉節點判斷是否能得到所需要的數據。用如下案例解釋:
drop table if EXISTS tcreate table t(a int not null, b varchar(20), PRIMARY key(a), key(b));insert into t select 1, 'k';insert into t select 2, 'do';insert into t select 3, 'dr';insert into t select 4, 'an'; select * from t;
  • 如上我們插入的數據,我們執行如下查詢語句:

  • 順序如下:

  • 加入我們插入的數據如下:

insert into t select 1, 'a';insert into t select 2, 'b';insert into t select 3, 'c';insert into t select 4, 'd'; select * from t;
  • 查詢結果如下:

  • 我們可以看到,他的排序規則是按照b的順序排列的,并不是根據主鍵a的順序排列,這也就是我們上面提到的,因為輔助索引中包含了主鍵的值,因此訪問b列上的輔助索引就能得到a的值,這樣就可以得到表中所有數據的值。 通常情況,一個輔助索引頁中,能存放的數據比主鍵索引頁上存放的數據多,因此優化器選擇了輔助索引 ,如果我們解釋這句查詢語句得到如下結果:

  • 可以看到,優化器最終選擇b索引,如果想得到對列a的排序結果,還需要對他進行Order By 操作,這樣優化器才會走主鍵,避免在查詢b列后又發生對a的排序操作。如下圖:

  • 或者可以強制使用主鍵索引

聯合索引

  • 聯合索引是指對表上的多個列做索引。之前說的情況,都對表上的某個列進行索引。聯合索引類似:
alter table t add key idx_a_b(a,b);
  • 什么時候該使用聯合索引?在這個問題之前我們應該弄清楚聯合索引內部的結構,本質上,聯合索引還是 B+樹,不同的是聯合索引的鍵值數量不是1個,二手大于等于2個。我們用簡單的兩個key的情況說明問題,如上a,b兩個key,我們用如下圖表示:

  • 如上圖中看到多個key情況的B+樹,和我們之前討論的單個鍵值沒有什么區別,鍵值都是排序的,通過葉子節點可以邏輯上順序的讀出所有數據,就上面的例子來說:(1,1)(1,2)(2,1)(2,4)(3,1)(3,2)數據按照(a,b)的順序存放。
  • 例如對于查詢 select * from table where a= xxx and b = xxx,這種情況顯然可以用(a,b)聯合索引。對應單個a的查詢 select * from table where a= xxx也可以使用(a,b)聯合索引。但是對于select * from table where b= xxx單個b的查詢不可以用這個B+樹索引。可以看到葉子節點上b的值 1,2,1,4,1,2,顯然不是按排序的,因此對于b列的查詢使用不到(a,b)的聯合索引。
  • 聯合索引的第二個好處:可以對第二個鍵值進行排序,例如,很多情況,我們都只查詢某個用戶訂單信息,并按照時間排序,取出最近一段時間的購買記錄,這個時候使用聯合索引可以避免多一次的排序操作。因為索引本身的葉子節點已經排序了。如下測試案例:
create table buy_log(userid int unsigned not null, buy_date date);insert into buy_log values (1,'2021-01-19'), (2,'2021-01-19'), (3,'2021-01-19'), (1,'2021-02-19'), (3,'2021-02-19'), (1,'2021-03-19'), (1,'2021-04-19');ALTER table buy_log add key(userid); alter table buy_log add key(userid, buy_date);
  • 如上建立兩個索引,都包含userid字段,對userid進行查詢,看優化器的選擇,如下:

  • 如上,possible_keys中有兩個索引,分別是單個userid和userid,buy_date的聯合索引。優化選擇的是userid,因為改葉節點包含單個鍵值,因此一個頁能存放的記錄更多,接著,看一下的查詢,我們假定要取出userid= 1的最近三次購買記錄,并分析使用單個索引和符合索引區別:

  • 同樣的都可以用兩個索引,但是這次優化器選擇了符合索引,因為這個聯合索引中buy_date已經排序好了,如果我們強制使用userid的單個索引,會有如下結果:

  • 如上extra信息中,看到Using filesort,filesort指排序,但是不是文件中完成,我們可以對比執行:


  • 如上看到增加了排序操作,但是如果使用userid, buy_date的聯合索引userid_2,就不會有這一次額外的操作,如下:


上一篇:數據結構與索引-- B+樹索引
下一篇:mysql技術分享-- 視圖是什么

總結

以上是生活随笔為你收集整理的数据结构与索引-- mySql索引诡异事件的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。