從數據庫存儲,文件結構談到B樹,散列


   昨天俱樂部內部辦了一個講座,關於常規數據庫系統實現,聽了之后有點混亂,於是花了很多時間特地查了一些資料,基本上自己感覺自己是明白了。特地寫下來。

      文章開頭說明三點,

  • 第一點,本文針對常規數據庫,是為了區別空間數據庫;
  • 第二點,本文不追求解釋清楚各個細節,而是着重介紹整體的脈絡,說白了就是,本文不細究怎么做,而是探討為什么要這么做。舉個例子,我會去探討為什么要設計B樹,而不會去說明B樹是怎么操作的;
  • 第三點,設計數據庫存儲結構的目的在於簡化和協助對數據的訪問。

    

  1.  概述

  計算機內部存儲是分層次的,簡單來說有內存和磁盤,內存空間小但是運算快,而磁盤空間大但是很慢。數據庫系統可能很大,有幾百萬條記錄,這樣一來數據庫就必須存儲在磁盤里面了,每次需要訪問數據庫里面的數據的時候我們就需要通過I/0接口訪問磁盤,然后將數據讀出來。我們要理解一點,就是說從I/O接口讀取數據是很慢很慢的,我們要盡量減少讀取次數。往往我們讀取的數據是由局部性的,所以一般每次中磁盤里面讀取數據都是一下子讀取一塊數據而不僅僅是我們需要的那一條數據,這樣做是因為我們以后要訪問的數據很可能就在當前訪問數據的附近,所以干脆全讀出來以減少讀取次數。那個“塊”我們稱之為磁盤塊,每個磁盤塊是由幾個扇區組成的,一般4K左右吧(我就不繼續解釋扇區的概念啦)。

      好了,我們的目標就在於加速對數據的訪問。我們前面提到,加速訪問的關鍵就在於怎么高效讀取數據,這有兩個方面。第一,我們可以想辦法加速讀取I/O的速度;第二,我們要想辦法減少讀取I/O的次數。

  2. 加速I/O讀取速度

 

      這里我不去深入到物理結構那個層次。

      前面有提到現在的數據庫可能很大,這就需要用很多磁盤來存儲,但是我們不能天真的以為磁盤就不會壞掉,所以我們首先需要確保數據的可靠性。畢竟,如果數據都損壞了,還談何快速訪問呢?

      確保可靠性的直觀想法就是備份(哈哈,插一句,記得很多同學都互嘲學計算機的孩子都養成了隨手備份的“惡習”)。這樣一來,一個邏輯上的磁盤是由兩個物理磁盤所構成的,並且每個寫操作都需要在兩個磁盤上執行。這樣做的好處就在於,除非兩個磁盤同時壞掉,否則我們的數據就是安全的,顯然我們提高了可靠性。

      現在回到我們一開始說的怎么加速I/O讀取的速度這個問題。既然數據有備份,那么我們讀取數據的時候為什么不可以同時也從備份數據中讀取呢,這樣,速度勢必翻倍。這也就是通過並行來提升性能,涉及到數據拆分了。具體怎么做一般有比特級拆分和塊級拆分兩種常見辦法。

      有關於加速的話題就暫且談到這里了,具體細節大家可以繼續研究 冗余獨立磁盤陣列(RAID)。總結一下就是通過冗余(備份)來提高可靠性,通過並行提高性能

      3. 減少I/O的訪問次數

      花開兩朵各表一枝。下面就要進入最核心的部分了,也就是怎么設計以減少I/O的訪問次數。

      為了說明清楚這個問題,我們必須知道記錄在文件中是怎么組織起來的,因為關系就是記錄的集合嘛。常見的記錄組織有三種方式,堆文件組織,順序文件組織以及散列文件組織(就不說明散列啦,本文)。

   3.1 堆文件組織

      對於堆文件組織,記錄是按照它們到達的順序存放的,也就是說沒有什么結構只能順序訪問了。對於這種結構,顯然效率是個問題,因為如果我要查詢包含某個鍵值的記錄,我就需要順序的讀取(注意,這里的順序是指按照記錄存儲的先后順序)把整個關系表讀取出來,線性查找,很麻煩吧。而且刪除的時候我們需要先線性查找到那個記錄,然后標記刪除再寫回磁盤。注意一點被刪除的那個磁盤空間是不會再被使用的,這點有點影響效率。當然了,也不是一無是處,插入還是很方便的,直接插到末尾就好了,這個性質很適合批量加載數據。

  3.2 順序文件組織

      如果記錄之間是有順序的(記錄根據“搜索碼”排序),那么這個時候我們需要查詢某個鍵值的時候就可以很快找到了。搜索碼一般是主鍵,當然了,也可以不是。為了減少順序文件處理中的I/O訪問次數,我們在物理上盡可能按照搜索碼順序存儲記錄。插入的時候,先搜索到處於插入記錄之前的那個記錄,然后如果這個記錄所在的塊還有空間那就直接插入,否則就需要將該記錄插入到新的“溢出塊”中了。無論哪種情況,都需要調整指針,使得可以按照搜索碼順序吧記錄鏈接在一起。刪除的時候,也是先找到記錄,然后標記刪除,同時用將所有被刪除的記錄串起來形成“空閑列表”以便與空間管理。下面着重說一下怎么查找。(這里假定是順序索引)

      首先我們知道既然記錄是順序排列的,那么就可以二分查找了。這里面有個前提,大家知道二分查找必須要求記錄是可以任意訪問的,也就是說我們必須滿足記錄在物理空間上連續才可以二分,也就是說如果出現上面提到的“溢出塊”就不能二分了,乖乖線性查找吧。我們先不考慮“溢出塊”的情況

      即便二分查找,那么我們查找一次的需要訪問I/O log(b) 次,其中b是指所有記錄存儲所占用的磁盤塊個數。也許你會很慶幸效率很不錯嘛,但是我們來算一下,假設一個磁盤塊(假設4KB)可以存儲10條記錄,那么十萬條記錄就要占用一萬個磁盤塊,也就是需要訪問I/O十四次,一次假設30ms,那也是四百多毫秒了,是不是很恐怖!

  3.2.1 建立索引

     我們回頭來看剛剛的這種做法,我們等於是讀取了整個記錄表然后再二分查找,那比如說我只想找到學號為1111的那個學生,我其實只需要讀取全部的學號,然后二分查找就可以了,至於這個學生的其他信息暫時用不到啊!所以,我們建立了附加結構--索引。索引包括搜索碼和指向該記錄的指針,指針包括了記錄所在磁盤塊的標識以及塊內偏移。很顯然,索引是比記錄要小的多的。我們再計算一下,假設一個磁盤塊可以存儲100個索引記錄,那么也就是需要占用1000個磁盤塊,二分查找需要十次訪問I/O,效率好多了吧,當然了三百毫秒也還是太長!(這里假設每個記錄都有獨特的所以碼)

  我們接着來分析。剛剛建立的索引是為每個搜索碼都建立對應的索引,也就是稠密索引。實際上我們大可不必如此,我們只需要為某些搜索碼建立索引(稱之為稀疏索引),然后再順序查找就好了。我們知道,相對於I/O訪問操作,我們將磁盤塊數據放進內存后對其掃描的時間是可以忽略的,所以我們一般為每個磁盤塊建立索引。再回到剛剛的計算,十萬個記錄需要占用一萬個磁盤塊,也就是需要一萬個索引,那我們需要用一百個磁盤塊來存儲,二分查找也就是需要7次的I/O訪問,這樣需要兩百多毫秒,雖然還是很長,但是最起碼有進步了。

 

稠密索引

1350882338_6785

稀疏索引

1350887159_6771

 

多級索引

 

  為了解決這個問題,我們想一開始對待順序文件那樣對待索引文件,也就是對索引文件建立索引,為了便於識別我們將索引文件的索引稱為外層索引,原先的順序文件的索引稱為內層索引。外層索引很小了,我們假設它已經在內存里面了,那么我們先在外層索引中搜索,然后只需要讀取一次索引塊就可以了,這樣就是一次I/O操作。當然了,如果記錄實在太多,我們還可以建立三級甚至多級索引,利用多級索引當然是要比二分搜索快得多啦。

  然而,我們要冷靜!因為我們所有的討論建立在一個致命的基礎上,也就是沒有“溢出塊”。而實際上,隨着文件的增大,插入刪除是很多的,所有“溢出塊”還是會不可避免的產生,而一旦存在溢出塊就很麻煩了,因為我們只能順序線性查找了,效率大打折扣。雖然我們可以通過文件重組的方式來消除“溢出塊”,但是重組是很占用時間的事情,所以我們不希望頻繁重組。在這種情況下呢,被我們隱藏在后台很久很久的B樹就橫空出現了。設計B樹的主要考慮是為了在數據插入或者刪除的情況下仍然可以保持效率。當然了,后人將B樹稍作改進,也就形成現在使用最為廣泛的所有結構---B+樹。至於B+樹的具體操作,我們就不多提了,大家可以看看其他一些資料。

  3.2.2 從多級索引到B+樹

     B+樹的非葉節點實際上就是一個多級(稀疏)索引,B+樹是一種平衡樹,也正是這種平衡屬性確保了其具備良好的查找刪除插入性能。B+樹的每個節點大小一般等於磁盤塊的大小,而葉節點一般會存儲實際記錄的磁盤塊而不是指向記錄的指針。這樣一來的話呢,I/O的訪問次數就等於樹的高度了,而且最關鍵的一點,我們也不怕溢出塊了。

  我們可以大概計算一下B+樹的性能。我們假設搜索碼32字節,然后磁盤指針8字節,這樣一個磁盤塊可以裝得下10個索引記錄。B+樹要求非葉節點至少要”半滿“,在我們這種情況下也就是每個非葉節點至少有50個索引記錄。假設我們有一千萬個記錄,那么也就是一百萬個磁盤塊,也就是一百萬個葉子節點。樹的高度也就是log(50)(1000 000),大概為4,也就是四次I/O操作!四次I/O訪問就可以鎖定一千萬條記錄里面的一個記錄,你敢信?????哈哈

  我們回過頭來看看B+樹,為什么它非要確保非葉節點”半滿“呢,或者說白了我們為什么不用平衡二叉樹?平衡二叉樹的操作可是要簡單不止一點點啊!我們也按照上面的方法計算一下,一百萬個葉子節點的平衡二叉樹大概多高--二十。也就是需要二十多次的I/O訪問。這下子明白了吧,B+樹這個奇怪要求就是為了使得樹”矮胖“一點,這樣需要的I/O次數才少!

      索引還有很多種方式啦,除了上面說的順序索引之外還有散列索引,位圖索引等等形式,我就不一一介紹了。哈哈,說到這里,應該把整個脈絡梳理的差不多了,先這樣呢,該吃飯去了。

4. 參考

  Database.System.Concepts(6th.Edition.2010)].Abraham.Silberschatz

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM