數據庫管理系統將數據存儲在磁盤、磁帶以及其他的裸設備上,雖然這些設備的訪問速度相比內存慢很多,但其非易失性和大容量的特點使他們成為數據存儲的不二之選。
本文主要討論大型數據庫產品的磁盤存儲內部結構,這對於深入理解數據庫各種數據結構具有至關重要的作用。
數據庫磁盤存儲的體系結構


以上兩圖分別展示了存儲器分級結構以及磁盤內部物理結構,不是本文重點,不贅述。需要強調的是:一次完整的輸入輸出(IO)操作的時間=磁盤軸旋轉時間(旋轉延遲)+磁盤臂移動時間(尋道時間)+數據傳輸時間。三者所需時間的平均經驗值為:0.004秒、0.008秒和0.0005秒。所以,一次完整的IO時間的經驗值是0.0125秒,即1/80秒。
對於大型數據庫而言,即便是這極短暫的0.0125秒,頻繁的IO操作會將這微不足道的時間累積得非常可觀,因此磁盤存儲的優化對於數據庫效率的提升是非常必要和重要的。不同的數據庫產品的磁盤存儲內部實現是不同的,本文只討論Oracle、DB2大型數據庫產品,二者細節上雖有不同,但結構大體上是一樣的。
Oracle和DB2數據庫的存儲模型如圖:

可以看出,數據庫中的數據都存儲在表空間中。表空間即是管理將邏輯數據庫設計映射到操作系統物理存儲中的一個數據庫對象,用於指明數據的物理位置。關於表空間,以后再討論。
Oracle數據庫磁盤存儲的邏輯結構為:一個數據庫(Database)對應多個表空間(Tablespace),一個表空間對應多個段(Segment),一個段對應多個區(Extent),一個區對應多個數據塊(Data Block),真正的數據就保存在數據塊中。這里有以下幾點需要說明:
1.Oracle中一個數據塊的大小默認是2KB(支持2KB,4KB,8KB,16KB,32KB),而DB2中則默認是4KB(支持4KB,8KB,16KB,32KB);
2.Oracle中有段(Segment)的概念,而DB2中沒有這一概念,表空間直接是各個容器(數據文件)中的區(Extent)組成,不過也還是有一個很弱化的Extent組概念。下面提到的關於Segment的內容則全部是針對Oracle的;
3.Oracle中的數據塊稱為Oracle Block,而DB2中則直接稱為Data Page(數據頁)。
4.Oracle中的Extent稱為區,而DB2中則稱為擴展數據塊。為方便閱讀,本文中統稱為區。

Oracle磁盤存儲邏輯結構

DB2磁盤存儲邏輯結構
要注意:這里的表空間,段,區,數據塊(頁)全部都是數據庫中的邏輯概念,並不是物理存在的,那么數據庫磁盤存儲的邏輯結構如何映射到操作系統磁盤存儲的物理結構中呢?
先來看看表空間的物理映射。表空間在操作系統上是由容器(Container)承載的,對於系統管理表空間(SMS)【注意:Oracle不存在系統管理表空間,其表空間全部都是數據庫管理的】,其唯一的容器是文件目錄;而對於數據庫管理表空間,其容器則是文件或者裸設備(比如磁帶、磁盤櫃)。這里我們不討論系統管理表空間,只關注數據庫管理表空間下的數據庫磁盤存儲。由於數據庫對於文件和裸設備這兩種容器操作上是同等對待的,所以以文件容器為例進行討論。文件容器本身是一個由DMS表空間使用的預分配大小的文件,一個表空間可以有多個文件容器,即有多個數據文件。也就是說:一個邏輯上的表空間映射為多個物理上的數據文件。
再來討論數據塊(頁)的物理映射。我們知道,物理結構上,操作系統中的文件是由多個操作系統的塊(Block)組成的(Linux,Unix系統的塊大小為512B,而Windows系統的塊大小為1KB),而上面說了,數據庫中的數據是以數據塊(頁)為單位存放的,,那么數據庫中的數據塊(頁)和操作系統的塊是什么關系呢?事實上,若干個操作系統塊組成一個數據庫的數據塊(頁)。對應區(extent)和段(segment)的映射並沒有物理單位與之對應,而是在一個數據文件中會包含多個段,每個段里又包含多個區(每個段中的區的個數不一定是一樣的;DB2中數據文件中直接以區為單位,沒有段的概念),每個區包含若干數據塊(每個區中數據塊的數量也不一定一樣)。另外,和表空間一樣,段也是可以跨文件的,即一個段可以由不同文件中的區所組成。
數據庫磁盤存儲的邏輯/物理映射圖解如下(DB2中沒有Segment這一層):

關於這些邏輯單位的具體討論以后再說。現在只需要了解大體的體系結構。通過上面的介紹可以看到,數據庫管理的表空間自成一體,具有平台無關性,儼然是一個小型的獨立文件系統了。
數據庫磁盤存儲的內部實現
了解了磁盤存儲的體系結構,再來看一看數據庫系統中,數據具體是如何進行存儲的。
當創建一張表(Table)的時候,會為其指定表空間,一旦表成功創建,數據庫系統就要為表提供磁盤空間。
Oracle數據庫會自動為一張表分配一個Segment(段),這個Segment稱為Data Segment(數據段)【注:一張表只能被分配一個數據段。Oracle一共有四種類型的段,分別是Data Segment(數據段),Index Segment(索引段),Rollback Segment(回滾段)和Temp Segment(臨時段)】。當為表分配的數據段全部寫滿的時候,數據庫管理系統會為這個數據段增加新的區(Extent),也就是說,數據段空間分配完后並不是需要多少空間就為段增加多少空間,也不是直接在區中增加數據塊,而是一次性增加一個Extent(這樣做避免了頻繁的Segment擴容),Extent是空間分配的最小單位,而且Extent在表空間中的各個容器上是均衡分配的。另外,數據塊(頁)是最小的存儲單位,也即最小的I/O單位,所有數據都是按塊(頁)存儲,讀出的時候也是直接將整個數據塊(頁)讀入內存中的。至於DB2,其方案與Oracle基本相同,所不同的是沒有Segment分配的問題。
我們以DB2為例,看看數據存儲具體是怎樣的。【參考牛新庄:深入解析DB2】
一個DMS表空間可以有多個容器(文件或裸設備),DB2將Extent均衡的寫到各個容器上。即當需要請求一個Extent時,數據庫管理器這個Extent分配到下一個容器上。這種方案保證了各容器的均衡利用,提高了並行訪問效率。如左圖:

表空間容器均衡寫

表空間容器,Extent,數據頁和表空間之間的關系
新建一個稱為HUMANRES的DMS表空間,其Extent大小為2個Page(即數據塊),每個Page大小為4KB。表空間有4個容器,每個容器內有少量已分配的Extent:表空間中DEPARTMENT和EMPLOYEE表中都占用了7個Page,按照上面介紹的分配規則,這兩個表中的數據都會寫到四個不同的容器中(7個Page需要分配4個Extent,每個Extent依次分配到各個容器中),另外,一個Extent只能由一個表所寫,即便表數據不能完全利用Extent中的空間,Extent中的空閑空間也只能空着,不能繼續寫入其他表中的數據(這是由Extent是空間最小分配單位決定的)。如右圖。
數據塊(頁)存儲
下面進一步深入,分析一下數據庫磁盤存儲最小單元數據塊(Data Block or Data Page)內的具體結構是怎樣的。對於Oracle和DB2,二者的數據塊結構是不同的。以下分別討論。至於其他的數據庫產品,不在討論之列。
Oracle數據庫的數據塊(Data Block)結構及相關特性
(參照:http://docs.oracle.com/cd/B28359_01/server.111/b28318/logical.htm)
數據塊格式

公共和變量塊頭( Common and Variable Header)
DB2數據庫的數據頁(Data Page)結構及相關特性
數據頁格式

在標准表中,數據在邏輯上按數據頁的列表進行組織。這些數據頁根據表空間的Extent大小在邏輯上分組到一起。這個Extent組類似於Oracle中的Segment,但Extent組沒有Segment那樣的強制概念。
每個數據頁都具有相同的格式。每一頁的最前面都是頁頭,后面跟着槽(Slot)目錄,然后是可用空間和記錄。
頁頭(Header)
用於存放BPS HEADER和一些頁的信息字段,其中BPS HEADER占48字節,整個頁頭大概占91個字節。
槽目錄(Slot Directory)
槽目錄類似Oracle的數據塊中的行目錄,槽目錄中的每個條目都與該頁中的一個記錄相對應。槽目錄中的條目代表記錄開始位置在數據頁中的字節偏移。值為 -1 的條目與已刪除的記錄相對應。
可用空間(Free Space)
DB2可用空間嚴格來說包括通常意義上的常規可用空間和嵌入的可用空間。其中常規可用空間可用直接存儲數據,而嵌入的可用空間通常是記錄被刪除后產生的碎片空間,不能直接使用,只有當頁重組后合並到常規可用空間后才能被使用,這一點和Oracle類似。
記錄(Record)
已經存儲了數據的空間,類似於Oracle中的行數據。即表中的行存放於頁面中成為記錄。根據數據頁大小以及記錄大小的不同,每個數據頁中包含的記錄數(即行數據數)也會有所變化。大多數頁僅包含用戶記錄(即普通的數據庫數據)。但是,少數頁包含由數據服務器用於管理表的特殊內部記錄。例如,在標准表中,每 500 個數據頁就有一個可用空間控制記錄(FSCR)。這些記錄用於映射后續每 500 個數據頁(直到下一個 FSCR 為止)中可供新記錄使用的可用空間。另外,在DB2 V9之前,每個數據頁最多可以存放255條記錄,而DB2 V9之后,每個數據頁理論上可以存放65000條記錄(與RID有關,暫不討論),但實際上受限於數據頁大小,每個數據頁大概能存放2300多條記錄。
行鏈接和行遷移
DB2是不允許行(記錄)跨頁的,即不允許行鏈接。但是允許行遷移(DB2中又稱為行溢出),
當表中存在變長數據類型時,容易發生行溢出現象,行溢出有時也叫行遷移,它表示當我們更新變長數據類型的字段時,由於更新的數據長度比原數據長,以至於當前的數據頁無法存放整行數據時,就需要把這行數據存放到一個新的數據頁,並在原來的數據頁內存放該行新位置的指針以指向新行位置,被移動的數據的RID(RID以后討論)保持不變。顯然,如果大規模出現行遷移的現象,那么必然會對數據庫訪問的效率產生嚴重影響(I/O大幅增加),如果表中發現大量行遷移現象,建議進行重組(REORG)操作,重新規划數據存儲位置消除行溢出。

對應DB2數據頁還需要強調的一點是:DB2的數據頁和Oracle數據塊一樣,也有PCTFREE和PCTUSED的概念,其含義與作用也大致相同,不贅述。
另外可以看到,Oracle的數據塊結構和DB2的數據頁結構是非常相似的。還有一點,二者的行數據都是從高位開始向低位寫,而行目錄則是由低位向高位寫,為什么要這樣呢?因為數據的插入是一個兩頭寫的過程,既要將數據寫入到可用空間中成為行數據或記錄,又要更新頭部后面的行目錄或槽目錄,如果同側寫不利於空間的擴展。下圖是一個直觀圖:

數據行結構
現在對數據塊(頁)的具體結構已經有了一個大概的了解,那么表中的一行數據是怎樣以行數據(記錄)的形式保存在數據塊(頁)中的呢?這個問題看似簡單,實際上,一個數據行(記錄,即上圖中的Row)的結構是非常復雜的,這里以DB2中數據行的結構為例。如圖:

各參數解釋如下:

可以看到,當元組(即表中的行)中存在變長列的時候,不管該列位於什么位置,在數據行(記錄)中都被挪到最后面存儲,而定長列則順序存儲。
【注:表的屬性列在定義的時候指定了數據類型,有的數據類型是變長的,比如varchar,其大小隨實際值的變化而變化,有的列數據類型的定長的,比如int為32位,物理該列上的實際值是多少,都占用4個字節。一旦列中出現變長數據類型的列,則該元組為變長元組。】
下圖是狀態位A的每一位的含義(1個字節=8位),狀態位B是未使用的。

下面看看一個定長的元組(行)在數據行(記錄)中具體是如何存放的:

有些復雜,將此圖與前面貼的結構圖對照着看。【注意:所有數據在數據頁的數據行(記錄)中都是以十六進制存放的,且為逆序存放】
下面是變長的元組(行)在數據行(記錄)中具體存放方式,與定長元組的存放方式大同小異:

對於含大對象數據類型的元組,其大對象數據並不與數據行存放在一起,而是與數據行分開放置於數據庫的不同頁中,在該元組(行)的數據行中存放一個指向該大對象的指針。如圖:

以上就是數據庫系統磁盤存儲內部結構的大致內容了
https://blog.csdn.net/u011537073/article/details/49157903 ,如有侵權,請聯系 linjie.rd@gmail.com 刪除
