GaussDB(DWS)基本IO框架

      網友投稿 1073 2025-03-31

      概述

      數據庫的IO結構一直是影響性能的關鍵部分,數據庫的操作最終還是要體現在最后的物理文件上。我們平常總是聽到各種緩存,這都是為了減小IO開銷而設計的。

      對于數據庫來說,因為要保證一致性的問題,那么IO模型的設計就更為復雜,既要保證減少IO的讀寫,又要保證突然斷電時,已提交事務的內容不能丟失等意外情況。此外,采用不同的索引結構設計的IO開銷也不一樣,理解IO模型對數據庫的調優也有一定的指導作用。

      本篇博文分別從讀取和寫入兩個方面講解了GaussDB(DWS)中的行存,列存基本IO模型。

      行存IO基本框架

      存儲結構

      在了解行存的整個IO框架之前,首先需要對行存的文件結構有一定的了解。那么我們平常操作的表都是怎么樣存儲在我們的文件系統中呢?

      就跟我們平常用的身份證號一樣,在GaussDB(DWS)中,也有對應的ID號來標識各個對象:

      OID(Object identifiers):對象的唯一標識。

      每個表存在對應數據庫的文件夾中,用relfilenode標識。

      通過各種ID,我們就可以查看每個表對應的文件了。

      例如表row1,可以直接查詢對應的文件:

      test=# select pg_relation_filepath('row1'); pg_relation_filepath ---------------------- base/16385/55984 (1 row)

      對于行存來說,我們常有的印象是以行為單位進行讀取和寫入的,但是這個對于IO開銷就過于嚴重了,因此GaussDB(DWS)采用和操作系統的思路,以頁為基本單位進行讀取和寫入。

      每個表的讀取寫入以頁(文件塊)為基本單位,頁的大小是一個BLCKSZ,默認8KB,其結構如下:

      Tuple保存了當前一行的數據,分為Header和Data兩塊,頭部保存元組的相關信息(列數,事務信息,是否有Toast表等)。

      每個Tuple最大為2kb,若Data過大無法壓縮至2KB,則采用額外的Toast表存儲,此時Tuple內的Data保存Toast表的相關信息。

      GaussDB 行存框架:

      行存無論在讀取還是寫入上,都采用了大量的緩存用來減少IO開銷。

      在外存方面,針對數據庫的特點,也單獨設計了外存管理器。整個框架如下:

      這里面涉及到幾個比較大的內核機制:

      本地緩存:

      這里面的本地緩存介紹了三個比較常用的緩存結構,這里直接引用了官方的英文解釋。

      temp_buffers: Sets the maximum number of temporary buffers used by each database session. These are session-local buffers used only for access to temporary tables.

      work_mem: Specifies the amount of memory to be used by internal sort operations and hash tables before writing to temporary disk files.

      maintenance_work_mem: Specifies the maximum amount of memory to be used by maintenance operations, such as VACUUM, CREATE INDEX, and ALTER TABLE ADD FOREIGN KEY.

      共享內存:可由整個Gaussdb共享

      包括shared_buffer和wal_buffer, 分別用來存放Page和Clog,Wal Segment。

      WalWriter,BgWriter:

      主要是將共享內存的內容落盤,WalWriter一般是在事務提交時就需要落盤,但是有時候可以放棄一定的事務一致性原則,從而讓WalWriter異步落盤加快速度。BgWriter負責將shared_buffer中的內容落盤。

      外存管理:

      負責上層與外存之間的文件交互。

      IO管理框架:讀取

      讀取的過程相對簡單,就是從物理文件先裝到shared_buffer中,然后從shared_buffers返回相關的結果。

      shared_buffers中就是以Page為單位進行存儲的,因為每個Page的大小是固定的,所以shared_buffers能存放的page個數也就是確定的。這里面就需要考慮一個問題,因為這個資源是共享的,如果一個線程讀取了大量的文件,這樣勢必會使得其他線程的緩存命中率下降。

      GaussDB在這里引入了Ringbuffer的機制,可以限制一個線程所使用的shared_buffers的大小,從而解決掉這個問題。

      IO管理框架:寫入

      寫入操作是增加的新的元組,Update操作相當于先Delete, Insert。

      INSERT

      增加一個新的元組和對應的line pointer的標記。

      UPDATE

      刪除舊的元組,可以看見Tuple1的HEAD標記了Delete, Data在圖中標記為了Delete只是為了說明這個Tuple被刪除了。然后增加新的元組

      將舊元組標記為Dead,然后插入新的元組,由Vacuum負責清理。當然,這里面Data變為DELETE只是用來描述刪除的是此Tuple,實際上Data當中的值是不變的。

      寫入的整體邏輯:

      GaussDB行存在寫入時,將元組信息先寫入到shared_buffers,然后用bgwriter刷入磁盤,這樣在事務提交時就可以避免磁盤的IO開銷,提升性能,為了保證一致性和恢復,使用wal日志和checkpoints可以實現日志先落盤(也可以異步)和redo等操作。

      列存的IO管理框架

      列存的存儲單元

      列存的存儲結構并沒有像orc或parquet的結構方式,采用了一種真正的以列存儲的結構方式。主要的特性如下:

      列存的存儲單元為CU(CStore Unit)

      CU的大小為8k對齊

      適合大批量導入的場景

      同一列的CU存在一個新文件中,大于1GB時,切換到新文件中。

      列存用一個CUDesc的行存表描述CU的相關信息,可以理解成為一個Toast表。

      CUDesc:行存表,記錄CU的相關信息, 主要屬性如下:

      col_id,cu_id: 第col_id列,第cu_id個CU

      min, max, row_count, size

      cu_mode: information mask(RLE,LZ4,Delta表等)

      cu_pointer:指向每一個CU,記錄delete bitmap

      magic:和CU頭部的magic相同,校驗使用

      CU結構

      和傳統的文件結構類似,也是以頭和壓縮后的數據為主要結構。

      列存索引

      這里介紹兩個索引,C-Btree和Psort索引。這也是各大數據庫常用的兩個索引,主要涉及的是IO相關的內容。

      C-Btree

      列存的Btree原理上并不是基于普通的Btree,而更像是一種B+樹。

      索引結構和行存無差別,同樣以行存形式存儲

      C-Btree可以提升點查效率

      存儲key->ctid(cu_id, offset)

      過程:

      根據B-tree索引找到ctid集合

      對集合進行批量排序(減少IO開銷)

      在CUDesc找到對應的cu_id,根據offset找到數據

      舉例,等值查詢 n=49, 范圍查詢 23

      PSort

      PSort是一個聚簇索引,對索引進行排序,然后將排序后的索引和行號存入一個新的表,用單獨的列存表存儲。

      簡單示意如下,圖片來源:https://www.modb.pro/db/108155

      IO管理框架:讀取

      列存的讀取和行存一樣,也是設計了緩存,這樣在重復查詢的時候可以顯著減少IO開銷,提高性能。

      讀取過程:

      根據where條件,做MIN/MAX過濾的謂詞條件

      加載CUDesc

      MIN/MAX過濾

      讀取CU到CU Cache中

      GaussDB(DWS)基本IO框架

      解析并填充

      CacheMgr: 用來緩存CU到內存中,可以提高重復查詢的性能。

      CU的物理文件:

      1. CStore_1.0: 當前基本不怎么實用

      2. CStore_2.0: 重整了CU的文件結構,避免列數過多導致文件結構復雜。

      IO管理框架:寫入

      列存的插入要分兩種情況,少量的插入和大量的插入,列存主要是對大批量數據設計的,因此為了彌補小量插入的打包CU性能開銷,設計了一個delta行存表,用來記錄插入結果,可以減少膨脹和提升性能,最后定期的整理。

      寫入框架如下

      列存的刪除比較簡單,如果是delta表,先從delta表中刪除滿足謂詞條件的記錄,然后在CUDesc表中更新待刪除CU的delete_bitmap。

      總結

      以上的相關內容只是整個IO框架的大體描述。可以看出因為涉及的場景不同,列存和行存的模型還是有區別的。尤其是列存的寫入不通過緩存,直接進入磁盤,這點與行存有著很大的區別,行存因為首先寫入shared_buffers,就需要很多額外的機制來保證一致性問題。而列存則相對簡單,誰好誰好都不是絕對的,這與使用場景有很深的關系。

      這其中還有很多細小的點可以仔細分析,以后的博客也會針對其中某些內容深入講解,比如說,OS緩存和GaussDB(DWS)之間的緩存交互,以及OS緩存和磁盤之間的交互邏輯,這對于一致性的實現是很重要的。

      希望本篇博客能夠幫助到各位讀者!

      EI企業智能 Gauss AP 數據倉庫服務 GaussDB(DWS)

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:怎么弄多一個頁面(怎么多加一張頁面)
      下一篇:wps2019怎么設置自動換行?wps2019自動換行設置教程
      相關文章
      亚洲人成电影在线观看网| 亚洲av一综合av一区| 亚洲成无码人在线观看| 亚洲一区日韩高清中文字幕亚洲| 涩涩色中文综合亚洲| 亚洲精品人成网在线播放影院| 亚洲欧洲国产经精品香蕉网| 老司机亚洲精品影院| 亚洲国产老鸭窝一区二区三区 | 亚洲色四在线视频观看| 久久久久亚洲精品影视| 亚洲AV无码国产精品麻豆天美 | 学生妹亚洲一区二区| 一本色道久久88亚洲精品综合 | 亚洲人成色777777老人头| 一本天堂ⅴ无码亚洲道久久| 亚洲国产精品无码久久| 亚洲第一第二第三第四第五第六| 亚洲成a∧人片在线观看无码 | 亚洲va在线va天堂va888www| 亚洲视频在线观看免费| 亚洲日韩区在线电影| 亚洲精品视频久久| 亚洲午夜国产精品| 亚洲情A成黄在线观看动漫软件| 亚洲色偷偷综合亚洲av78| 亚洲精品无码高潮喷水A片软| 亚洲国产成人精品无码区花野真一 | 亚洲动漫精品无码av天堂| 亚洲a在线视频视频| 亚洲国产高清在线精品一区| 国产精品久久亚洲不卡动漫| 亚洲精品无码国产片| 亚洲国产精品丝袜在线观看| 国产成人麻豆亚洲综合无码精品 | 亚洲人成网站看在线播放| 亚洲日韩一区二区三区| 国产亚洲精彩视频| 久久亚洲中文字幕精品一区| 亚洲AV无码欧洲AV无码网站| 亚洲系列中文字幕|