從數據庫到固態硬盤的讀取過程

應用程序到數據庫到操作系統到固態硬盤

在我研究了從應用程序發送的簡單 SQL 查詢是如何最終存儲到磁盤的過程中,我發現術語“頁(page)”和“塊(block)”可能是軟件工程中最多用的概念之一。有數據庫頁(database page),操作系統虛擬內存頁(virtual memory page),文件系統塊(file system block),固態硬盤頁(SSD page),兩種類型的固態硬盤塊(SSD block),其中一個稱爲邏輯塊(logical block),對應於文件系統,另一個是更大的單元,稱爲擦除塊(erase unit),其中包含多個頁。所有這些單元都可以具有不同的大小,有些匹配,有些不匹配。

在本文中,我將詳細介紹一個SELECT語句以及在執行過程中不同層次的輸入/輸出(I/O)是如何一直執行到磁盤的底層。

這裏是我們將要解釋的完整圖示:

但首先,基礎知識

當您在數據庫中創建一個表時,會在磁盤上創建一個文件,並將數據佈局在固定大小的數據庫頁中。數據在頁中的佈局方式取決於引擎是行存儲還是列存儲。將頁視爲一種結構,它具有標頭和數據,數據部分是行所在的位置。

數據庫頁可以是8KB(Postgres)或16KB(例如MySQL InnoDB)或更大。表被存儲爲文件中的頁數組,其中頁面索引+大小告訴數據庫確切的偏移量和讀取量。例如,假設數據庫頁大小爲8KB,要從磁盤上讀取第7頁,您需要尋找偏移量7 * 8192 + 1,並且您將讀取8192字節的長度。

行及其所有列依次存儲在頁中。如果行無法適應頁中剩餘的空間,則會分配一個新頁面,並將該行放入新頁面中。

當從磁盤讀取頁面並放入緩衝池時,我們可以免費獲取該頁面中的任何行(和列)。無論您是否相信,這可能是數據庫優化和數據建模中最重要的認識。

有了這些基礎知識,讓我們執行此查詢,查詢表爲“STUDENTS”,其中包含一個類型爲“serial”的ID字段(單調遞增)。該表有20,000行,分佈在20個頁面中。我們所需的行(1008行)位於頁面1(第二個頁面)。緩存中沒有任何數據。爲了保持簡單,我不打算在這裏包含索引,只是爲了保持簡單,雖然會比較慢。爲簡潔起見,我只畫出前三個頁面。

SELECT NAME FROM STUDENTS WHERE ID = 1008;

無論我們是使用主索引作爲表的聚簇索引,還是在沒有主索引的情況下使用堆表(如在PostgreSQL中),我認爲這裏的模型是相似的。儘管ID字段沒有建立索引,在InnoDB的情況下,您可以將另一個字段視爲主鍵,它會強制我們進行全索引掃描,其中我們讀取主索引的葉子頁面;而在PostgreSQL中,則會對錶進行順序掃描。我們假設每個頁面大小爲8KB。

數據庫

數據庫解析並理解查詢,由於ID字段沒有建立索引,所以它進入了全表掃描模式,以獲取值爲1008的行。

數據庫從頁面0開始,並檢查頁面0是否在緩衝池中。緩衝池是所有數據庫進程之間共享的內存空間,用於存儲頁面,也可以在其中進行寫入操作。數據庫沒有找到頁面0,因此它向表文件發送讀取頁面0的請求。

文件的偏移量是0*8192,我們要讀取8192字節。頁面被讀取並放置在共享緩衝區內存中。頁面包含行1到1000,數據庫解析頁面,反序列化並在內存中查找每行的ID值,但沒有找到。因此,我們繼續讀取頁面1。

讀取頁面1時,文件的偏移位置爲1*8192,我們要讀取8192字節。頁面1被放置在共享緩衝區中,與頁面0一起存儲。數據庫在頁面中查找行1008,並找到並返回給用戶。

現在讓我們進一步細分,看看數據庫向操作系統發出讀取請求時會發生什麼。

文件系統

文件系統以塊或邏輯塊的單位從磁盤讀取和寫入數據。這些單位的大小可以從512字節到4KB不等,其中4KB是最常見的大小。

操作系統接收到讀取文件偏移量爲0,讀取8192字節的請求,並使用文件系統索引節點或"inode"將請求的字節映射到文件系統塊。每個邏輯文件系統塊地址(LBA)映射到存儲設備中的特定物理塊,我們將在後面學到這一點。如果您讀取單個字節,實際上會從磁盤中讀取整個塊。

當請求從偏移量0開始讀取8192字節時,操作系統執行以下檢查:

  1. 文件的偏移量0到8192字節之間的塊是哪些?
  2. 假設文件系統塊大小爲4KB,則得到兩個塊。
  3. 文件系統查找文件的索引節點以查找邏輯塊地址(LBA),假設這些塊是100和101。
  4. 操作系統然後在文件系統頁緩存中查找塊100和101,以查看先前的讀取是否已將它們獲取並放置在內存中。
  5. 文件系統頁緩存位於內存中,存儲塊地址和主內存中虛擬內存頁的地址。
  6. 操作系統中的虛擬內存頁通常是4KB,與塊大小相匹配。因此,一個塊可以適配到一個內存頁中。
  7. 假設操作系統在頁緩存中未找到塊100和101,因此準備從磁盤中讀取。

注意,索引節點包含有關文件的其他元數據,例如權限信息。

存儲

我們瞭解到讀取頁0相當於偏移量0和長度8192,它被轉換爲文件系統塊100和101,每個塊的大小爲4KB。操作系統檢查了緩存,但找不到這些塊,因此從磁盤中讀取。

假設使用NVMe固態硬盤,操作系統通過NVMe驅動向存儲設備發送讀取命令。讀取命令有許多參數,但最重要的是起始LBA(邏輯塊地址),第二個參數是要讀取的塊數。這意味着驅動程序發出了一個讀取命令,傳遞了(100, 0)。長度爲0表示在NVM命令集中讀取1個塊。

現在有趣的地方來了。在NVMe中,“塊”的大小可能與文件系統塊大小不同。例如,在這裏我們假設NVMe塊與文件系統塊大小相同,都是4KB。如果它們不同,操作系統需要更改讀取參數。例如,如果NVMe塊大小爲2KB,則文件系統塊大小將包含2個NVMe塊。因此,讀取命令將爲101, 3。

固態硬盤(SSD)被分成頁面,這是最小的讀寫單元。SSD的NAND頁面目前的大小爲16KB。頁面被分組成更大的單元,通常也稱爲塊,以擦除單元的方式。要寫入SSD頁面,頁面必須處於擦除狀態,而單獨擦除頁面是不可能的,必須擦除整個擦除單元。

現在,我們的NVMe邏輯塊地址映射到這個頁面中的一個偏移量。因此,在這種情況下,由於我們的NVMe邏輯塊大小爲4KB,4個塊適合於SSD NAND頁面。

SSD實際上並不使用邏輯塊地址,它只知道頁面的物理位置。因此,需要進行從邏輯塊地址到物理頁面偏移量的轉換。由於SSD頁面可以大於塊,多個塊可以映射到不同偏移量的同一頁面上。

NVMe控制器接收到讀取LBA 100和LBA 101的命令,NVMe驅動器的一個特點是,這些邏輯塊地址(這兩個塊)被轉換爲物理頁面和偏移量,例如頁面99和偏移量0x0001和0x1002。接下來,NVMe控制器檢查本地SSD DRAM緩存,以查看頁面99是否在緩存中。是的,這裏有一個SSD緩存。如果頁面99不在緩存中,則整個頁面(整個16KB)被完全獲取到緩存中並放置在緩存中。

一旦頁面完全被獲取到緩存中(整個16KB),相應的塊會從頁面中提取並返回給操作系統主機。在這種情況下,只返回了前8KB。

你可能會問爲什麼不讓操作系統直接訪問頁面的物理地址?爲什麼需要進行這種轉換?原因是磁盤有時需要移動數據,當操作系統指向物理位置時,移動數據變得困難。

具有諷刺意味的是,可以將數據的移動卸載到主機上,但這不會增加應用程序的複雜性成本。沒有免費的午餐。這是一個深入研究的課題,你可以決定深入探索。

回到文件系統

操作系統獲取代表兩個塊(100、101)的8KB數據,並將其放入兩個內存頁面中。然後,它更新文件系統頁面緩存,以便下一次請求讀取100或101時可以從主機內存中獲取。

回到數據庫

然後,操作系統將控制權返回給數據庫應用程序,如果你還記得,它發出了讀取偏移量爲0、長度爲8192的請求,對應於頁面0。數據庫將原始字節放入共享緩衝池內存(與文件系統緩存不同)。頁面0現在對於從中提取數據的任何其他查詢都是“熱點”,在刷新到磁盤之前,頁面0可以接收寫入操作。

當然,下一個頁面,即頁面1,也經歷了同樣的過程,直到找到行ID爲1008的行。

總結

要從數據庫中讀取一行數據,你必須讀取包含該行的頁面。爲了讀取數據庫頁面,數據庫會向文件發出正確偏移量和長度對應於頁面的讀取請求。操作系統將這些字節映射到文件系統塊地址(或LBAs),對比文件系統頁面緩存,看是否存在具有這些塊的內存頁面。否則,會向存儲控制器發送讀取命令。設備將邏輯塊轉換爲物理地址,並將頁面加載到緩存中,並將所請求的字節返回給操作系統主機。主機將塊放入文件系統頁面緩存,並返回給數據庫,數據庫將頁面放入共享緩衝池中並開始處理,並將所請求的單行返回給用戶。

如果你使用索引會怎樣?類似的情況,索引是存儲在磁盤上的B+樹結構,也具有頁面。在遍歷樹時,你將獲取頁面,因此概念是相同的。

如果你喜歡我的文章,點贊,關注,轉發!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章