The two most important days in your life are the day you were born and the day you find out why. -Mark Twain

#22 資料庫的資料實體儲存單位 Page - 3

在來延續上一篇文章 (#21  資料庫的資料實體儲存單位 Page - 2) 的內容.在上一篇的文章中看到了如果沒有 page 的概念時,資料庫引擎有那些可行的方法來在硬碟上管理資料,從上一篇文章中,你看感受到空白空間的處理與以及資料的定位並不是很方便.於是在階層管理的方便性上多加了一層 Page.

在一般市面上常見的資料庫產品中,Page 的大小都大約在幾Kb之間,類似於作業系統的儲存單位大小.所以,一個資料庫可能會包含一個或多個資料庫檔案,而每一個資料庫檔案都會包含多個 Page.

我們再看看如何用 Page 來管理資料.如上一篇文章的情況,資料有可變長度與固定長度兩種.固定長度的情況是比較單純好處理,可以參考下圖:


上圖是某一個 Page 的示意圖,由於每筆資料都是固定長度,所以每一個 Page 都可以儲存相同數量的資料筆數 (除了最後一個 page 可能因為資料筆數無法除盡,所以不一定有相同筆數).在每個 Page 的最後面會留下一些小空間用來做為目錄式的記錄,所以可以從這小空間中可以知道這一個 Page 一共儲存了多少筆數的資料,同時也可以知道每一個位置是不是都有資料.以上圖為例,我們透過 delete 語法刪除了一筆資料,而這筆資料剛好是位在這個 Page 的第二個位置,所以可以看到在目錄式記錄上第二個位置是 False (boolean),用一個 bit 就可以表達完成.所以,當有新資料要寫入時,資料庫引擎就可以讀取每個 Page 上的這目錄就可以知道那一個 Page 有多少的空間是可以寫入新資料的.因此,這種實作方式對 storage manager 的開發者來說相當簡單,而且空白空間也易於維護.

另一個情況是可變長度的資料.因為每筆資料的長度不見得會一樣,所以每個 Page 能放多少筆的 record 完全得視實際上每個 record 的長度來決定.在 Page 上安排位置時,在 manifest 那段小空間裡所放的內容就會有點差異了,如下圖:


由於資料非固定長度,所以在 manifest 上每一個位置都會有一個 pointer 指定到那個資料的位置,同時也會指出來空白空間是從什麼位置開始.上圖的狀態等於是資料經過了一些 update/delete 指令後才會出現的情況,因為每筆資料之間還是有空白空間的存在,但那些空白空間不會被資料庫引擎所使用,因為管理上實在太麻煩了,所以當有一筆新資料要寫入到這個 Page 時,資料庫引擎只會找這個 Page 上的空白空間 (灰色區塊),只要有足夠的空白空間,那麼新資料就會被寫進來,同時更新 manifest 的相關 pointer 內容.

以上的內容是資料庫引擎對在一個 Page 內針對固定長度與可變長度資料的存取安排,所以你就可以想像的到當一個資料庫運行了一段時間之後,零零碎碎的空白空間一定是散落在資料庫檔案的各角落,而那些零零碎碎的空白空間是不會被資料庫引擎所使用.所以,資料庫的產品通常都會一個功能把這些零零碎碎的空白空間給消除,也就是讓資料靠的更緊密些,讓儲存空間可以在容量上得到發揮更好的效果.

Share:

#21 資料庫的資料實體儲存單位 Page - 2

延續上一篇文章 (#19 資料庫的資料實體儲存單位 Page) 的內容,在這篇文章裡,我們來談談有關資料庫的實體儲存結構.所謂的實體儲存結構是指資料放在硬碟中的情況.

首先,我們先來看看一個很簡單的結構,資料長度是固定的.這種情況在前面的文章有提過.因為長度都是固定的,所以每筆資料的長度便可以固定,這樣好處是在於方便計算也方便存取.

另一種情況是資料長度不是固定的,這種情況在前面文章也有提過.因為長度不是固定的,因此必須要想一個方法把不同欄位的資料做一個區隔,這樣在讀取資料時才知道什麼情況下是資料欄位的終點.在實體資料儲存時,有兩個方式可以來達成這種目的.第一,在每個資料欄位之間都用一個特殊符號隔開來,長相如下:


上圖是用一個 # 來當特殊符號,這是很笨的例子,但只是方便說明用.在資料寫入到硬碟時,資料欄位之間會透過特殊符號做為間隔,而在讀取資料時,資料庫引擎也知道該如何處理資料了,而這樣的好處就是非常直覺且簡單,並不會浪費多餘的硬碟空間,缺點就是會浪費較多的資料讀取時間,例如我只要讀取表格裡欄位五的資料,那麼資料庫引擎就必須從欄位一讀取過四個特殊符號才能取得欄位五的資料.從前面文章中,你應該有學到一個精神就是當你對時間不滿意時,那就犧牲點空間去換取時間.所以可以把上述的儲存方式最一點小改良如下圖:


在整筆資料的最前面加上一個導覽式的目錄,裡面存放著每個資料欄位開始的位址起點,所以每筆資料有五個欄位,因此目錄裡就有五個 pointer,而每個 pointer 的內容就是一個硬碟位址用來記得資料的起點.所以當資料庫引擎要讀取欄位五的資料時就不用從欄位一開始讀取了,可以透過目錄裡的第五個 pointer 就可以直接找到欄位五的位址.

以上所說的方法都是很直覺且自然的,但我們來想像一下如果進行資料更新或刪除時會發生什麼事情?

先針對資料更新來討論,如果把欄位三的內容更新成較小的內容時,此時影響不大,只是欄位三後面會有空白空間,而我們該討論的是該不該保留這個空白空間.因為這裡討論的是每個資料欄位都有一個 pointer 會對應到資料,所以若我們保留空白空間的話,可以讓資料少了些移動,但也可能會浪費許多細小的空白空間.如果此時的情況是欄位三的內容被更新成比較大的內容時,例如更長的字串,接著就面臨到沒有足夠的空間問題了,解決方法會有很多種,也許比較簡單的是把整筆資料搬移到足夠大的空間,好讓欄位三可以容納下新的內容,同時前面的五個 pointer 的內容也需要一起改變.如果是刪除呢? 那就更單純了,只要把整筆資料在刪除或是做一個已刪除的 flag 即可.所以,從這裡我們可以看到,其實在更新時所面到的挑戰是比刪除的挑戰還要大很多.而且不論是那一種更新 (內容變多或變小),都會遇到空白空間管理的問題.

所以我們可以發現,當資料庫的資料筆數越來越多時,讓資料庫引擎直接去對每一筆資料做管理時,其實是有點不方便的.因此,我們需要在這中間多一層實體儲存的管理單位,而這管理單位就是我們前面文章中所介紹的 Page.這種感覺就有點像一個國家不可能直接管理到每一個家庭,中間一定會有省,市之類的層級來做為分類,以便於管理上的應用.而 Page 也就是這樣的感覺.

Share: