簡單談談Mysql索引與redis跳錶
摘要
面試時,交流有關mysql索引問題時,發現有些人能夠濤濤不絕的說出B+樹和B樹,平衡二叉樹的區別,卻說不出B+樹和hash索引的區別。這種一看就知道是死記硬背,沒有理解索引的本質。本文旨在剖析這背後的原理,歡迎留言探討
問題
如果對以下問題感到困惑或一知半解,請繼續看下去,相信本文一定會對你有幫助
- mysql 索引如何實現
- mysql 索引結構B+樹與hash有何區別。分別適用於什麼場景
- 資料庫的索引還能有其他實現嗎
- redis跳錶是如何實現的
- 跳錶和B+樹,LSM樹有和區別呢
解析
首先為什麼要把mysql索引和redis跳錶放在一起討論呢,因為他們解決的都是同一種問題,用於解決資料集合的查詢問題,即根據指定的key,快速查到它所在的位置(或者對應的value)
當你站在這個角度去思考問題時,還會不知道B+樹索引和hash索引的區別嗎
資料集合的查詢問題
現在我們將問題領域邊界劃分清楚了,就是為了解決資料集合的查詢問題。這一塊需要考慮哪些問題呢
- 需要支援哪些查詢方式,單key/多key/範圍查詢,
- 插入/刪除效率
- 查詢效率(即時間複雜度)
- 儲存大小(空間複雜度)
我們看下幾種常用的查詢結構
hash
hash是key,value形式,通過一個雜湊函式,能夠根據key快速找到value
B+樹
B+樹是在平衡二叉樹基礎上演變過來,為什麼我們在演算法課上沒學到B+樹和跳錶這種結構呢。因為他們都是從工程實踐中得到,在理論的基礎上進行了妥協。
B+樹首先是有序結構,為了不至於樹的高度太高,影響查詢效率,在葉子節點上儲存的不是單個數據,而是一頁資料,提高了查詢效率,而為了更好的支援範圍查詢,B+樹在葉子節點冗餘了非葉子節點資料,為了支援翻頁,葉子節點之間通過指標連線。
跳錶
跳錶是在連結串列的基礎上進行擴充套件的,為的是實現redis的sorted set資料結構。 level0: 是儲存原始資料的,是一個有序連結串列,每個節點都在鏈上 level0+: 通過指標串聯起節點,是原始資料的一個子集,level等級越高,串聯的資料越少,這樣可以顯著提高查詢效率,
總結
資料結構 | 實現原理 | key查詢方式 | 查詢效率 | 儲存大小 | 插入、刪除效率 |
---|---|---|---|---|---|
Hash | 雜湊表 | 支援單key | 接近O(1) | 小,除了資料沒有額外的儲存 | O(1) |
B+樹 | 平衡二叉樹擴充套件而來 | 單key,範圍,分頁 | O(Log(n) | 除了資料,還多了左右指標,以及葉子節點指標 | O(Log(n),需要調整樹的結構,演算法比較複雜 |
跳錶 | 有序連結串列擴充套件而來 | 單key,分頁 | O(Log(n) | 除了資料,還多了指標,但是每個節點的指標小於<2,所以比B+樹佔用空間小 | O(Log(n),只用處理連結串列,演算法比較簡單 |
對LSM結構感興趣的可以看下cassandra vs mongo (1)儲存引擎
cassandra vs mongo (1)儲存引擎
概括
儲存引擎:
B-Tree
快取管理
快取管理的核心在於置換演算法,置換演算法常見的有FIFO(First In First Out),LRU(Least Recently Used)。關係型資料庫在LRU的基礎上,進行了改進,主要使用LIRS(Low Inter-reference Recency Set)
將快取分為兩級,第一次採用LRU,最近被使用到的資料會進第一級,如果資料在較短時間內被訪問了兩次或以上,則成為熱點資料,進入第二級。避免了進行全表掃描的時候,可能會將快取中的大量熱點資料替換掉。
LSM
Log-Structured Merge Tree:結構化合並樹,核心思想就是不將資料立即從記憶體中寫入到磁碟,而是先儲存在記憶體中,積累了一定量後再刷到磁碟中
LSM VS B-Tree
LSM在B-Tree的基礎上為了獲取更好的寫效能而犧牲了部分的讀效能,同時利用其它的實現來彌補讀效能,比如boom-filter.
1.寫
B樹的寫入,是首先找到對應的塊位置,然後將新資料插入。隨著寫入越來越多,為了維護B樹結構,節點得分裂。這樣插入資料的隨機寫概率就會增大,效能會減弱。
LSM 則是在記憶體中形成小的排好序的樹,然後flush到磁碟的時候不斷的做merge.因為寫入都是記憶體寫,不寫磁碟,所以寫會很高效。
2.讀
B樹從根節點開始二分查詢直到葉子節點,每次讀取一個節點,如果對應的頁面不在記憶體中,則讀取磁碟,快取資料。
LSM樹整個結構不是有序的,所以不知道資料在什麼地方,需要從每個小的有序結構中做二分查詢,找到了就返回,找不到就繼續找下一個有序結構。所以說LSM犧牲了讀效能。但是LSM之所以能夠作為大規模資料儲存系統在於讀效能可以通過其他方式來提高,比如讀取效能更多的依賴於記憶體/快取命中率而不是磁碟讀取。
Cassandra
Cassandra是一個寫效能優於讀效能的NoSql資料庫,寫效能好一個原因在於選擇了LSM儲存引擎。
Mongo
MMAPv1
Mongo 3.2以前預設使用MMAPv1儲存引擎,是基於B-Tree型別的。
邊界(padding)
MMAPv1 儲存引擎使用一個叫做”記錄分配”的過程來為document儲存分配磁碟空間。MongoDB與Cassandra不同的是,需要去更新原有的document。如果原有的document空間不足,則需要將這個document移動到新的位置,更新對應的index。這樣就會導致一些不必要的更新,和資料碎片。
為了避免出現上述情況,就有了邊界的概念,就是為document預分配空間。但是這樣就有可能造成資源的浪費。mongo 按照64M,128M,256M…2G的2的冥次方遞增策略預分配,最大2G。在資料量小的情況下問題並不明顯,但是當達到2G時,磁碟佔用量大的問題就出來了。
同樣這一點和關係型資料庫也不一樣,關係型資料庫對於長記錄資料會分開儲存。
鎖
MMAPv1使用collection級別的鎖,即一個collecion增,刪,改一次只能有一個。在併發操作時,就會造成等待。
WiredTiger
3.2及其以後的預設儲存引擎,同樣是基於B-Tree的。採用了lock-free,風險指標等併發技術,使得在多核機器上工作的更好。
鎖級別為document。並且引入了compression,減少了磁碟佔用。
總結
以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,謝謝大家對我們的支援。