CMU Database Systems - Storage and BufferPool

Database Storage

存儲分爲volatile和non-volatile,越快的越貴越小數據庫

那麼因此要解決的第一個問題就是,若是儘可能在有限的成本下,讓讀寫更快些緩存

意思就是,儘可能讀寫volatile存儲,可是volatile比較頗有限,因此須要合理的在兩種存儲上去swap數據結構

 

可是技術是在飛速的進步的,因此如今有Non-volatile memory併發

因此最近流行內存數據庫,由於當前的memory和磁盤間的IO瓶頸已經消除,因此當前的瓶頸是CPU cache和Memory以前的問題性能

這個問題會在下一門課裏面說優化

因此繼續前面的問題,怎麼解決disk和memory之間的IO瓶頸編碼

一個直覺的想法就是,交給操做系統去作,使用虛擬內存,Virtual Memoryspa

mmap能夠產生內存文件,把磁盤文件的內容map到內存的地址空間,這樣有個問題就是若是有多個併發寫,須要同步機制,系統也提供右圖這些同步指令操作系統

可是數據庫管理系統每每但願作的更精細,由於操做系統是個通用方案,必定達不到性能最優設計

 

下面咱們來看第二個問題,DBMS如何將數據庫的數據放到磁盤文件上? 

這裏有個選擇,DBMS是否要用系統的文件系統,仍是拿一塊raw storage本身管理,如今通常的選擇是仍是使用文件系統,畢竟方便

既然用文件系統,那麼DBMS就須要把數據庫數據存成一個或多個文件

這裏有個概念,Page,文件是由一堆page組成的

page其實就是固定大小的數據塊,那爲何要有這層抽象?

這個和咱們使用的存儲有關,當前用的磁盤,除了慢,還有個特色是對順序讀寫比較友好,由於隨機讀須要磁頭不斷的機械移動的,這個想一想也很慢

因此文件系統和磁盤間的IO,須要儘可能批量讀,讀寫數據的最小單位稱爲數據塊,通常是4K,爲何是4K,應該是由於比較經濟

而數據庫的page是基於文件系統的,因此設計成4k的倍數會比較合理

數據庫會本身維護一個page id到實際存儲地址(文件+offset)的映射

那麼如何在磁盤文件上管理page?

有三種方式,最多見的是Heap FIle

 

HeapFile就是用來放page的文件,固然咱們能夠經過文件名+offset,訪問某個page

同時咱們須要能夠遍歷全部的page,知道哪些page有free space能夠用來存放tuples

因此這裏heap file也有兩種實現方式,

 

繼續看看Page的構造是怎麼樣的?

能夠看到在page中的header,存儲了一些元數據,若是須要self-contained,就須要包含scheam,編碼信息等

 

那麼data,是如何組織的了?

其實有兩種方式存儲數據庫的數據,

Tuple-oriented和Log-structured

Tuple-oriented主要的存儲方式是,slotted pages

這個方法關鍵就是加入了slot array來索引各個tuple,這樣就能夠兼容變長的tuples,否則怎麼知道每一個tuple從哪裏開始,刪除tuple也更簡單

 

若是是Log-structured,寫數據會比較簡單

但讀數據就比較麻煩了,須要replay出數據,由於你只記錄了log嗎

提供讀性能的方式有兩種,儘可能減小replay的數據,就是打snapshot或建index

比較經常使用的就是按期的作compaction,好比HBase, Cassandra,LevelDB,RocksDB

Compaction分爲兩種,按層逐級compact,或是universal

最後,tuple自己的存儲結構是怎麼樣的?

一樣Tuple也有一個header,裏面包含元數據,好比這個tuple可見性,BitMap表示哪些是NULL
注意這裏通常是不會包含schema,由於在每一個tuple都包含沒有必要,一個table的schema是固定的,單獨存就好

 

Denormalized Tuple Data

這是一種針對查詢的優化,

Denormalized,都知道關係模型有範式,冗餘數據必定是會打破範式的,因此是de-

兩個表join,把須要的字段冗餘到一張表中,稱爲pre join,讀的時候會比較快,單純從當前page就能夠完成,可是寫就麻煩了,由於打破範式了嗎

總結一下上面的說的,以下圖

這裏page管理用的是direction的方式,因此讀取page2,

首先要把direction page加載到buffer裏面,這樣讀到page2的地址而後再去讀出page2,而後Execute engine需求去解析page

 

Page中就是tuple的集合,tuple是a sequence of bytes,但若是咱們要使用這些數據,首先要把這些bytes轉化爲相應類型的數據

主要的類型以下,

須要特別關注的,

浮點數和定點數

定點數就是小數點是固定的,因此咱們用int分別存儲小數點先後的數字就能夠實現,定點數是能夠作到精確計算的,可是侷限也很明顯,只能表示固定精度

 

浮點數就比較複雜了,由於小數是連續的,無限的,而計算機實現是離散的,有限的

因此要在計算機裏面表示浮點小數,就須要用trick的方法去近似,定義出的標準就是IEEE-754,浮點運算是近似的,非精確的

 

 VARCHAR,BLOB

因爲tuple大小不能超過page,好比對於varchar,若是大於page,須要把多的存放到overflow page裏面

而對於blob這樣的類型,乾脆就須要存放到外部文件中,這裏注意對於存放到外部文件的數據,是不保證transaction等語義的

 

那麼如今有個關鍵的問題,數據庫的元數據是存儲在什麼地方的?

Catalogs,Catalogs的信息能夠從Information_schema表中讀取到

不一樣的庫,對於元數據讀取有不一樣的shortcuts,

 

 最後再看下,行列存的區別,

行存,row storage,稱爲n-ary storage model

對於左圖的OLTP的需求,行存很適合,插入和更新比較簡單,整行的查詢

但對於右圖,OLAP的需求,行存會比較慢,BI需求每每須要掃描大量的行,但只是用其中的部分字段

 

列存,column store,decmposition storage model 

相對於行存,列存是把一列的數據集中存儲在一個page中,

這樣上面的例子,就只須要讀包含這兩個列的page,其餘page就不用讀了

列存關鍵的問題是如何恢復成行?

這裏給的方法也很簡單,若是列中的每一個value都是等長的,那直接根據length除就知道是第幾行的
或者,就是在每一個列裏面記錄下tupleid

 

BufferPool

bufferPool是一種cache機制,讀磁盤慢,因此把讀到的page緩存在bufferPool的frame裏面

而且用Page Table來記錄,到底哪些page在bufferpool中;page table中還會記錄meta,好比dirty flag,這個page被改過,不能直接drop掉,須要寫回磁盤;Pin,這個page正在被讀,不能被swap out

上面的鎖的形狀,表示latch,
我要讀page2,table中miss,那麼先用latch鎖定一個slot,而後等page2被load到bufferPool的frame的時候,link上,這個過程當中別人不能來修改或讀取這個slot

下圖,表示在數據庫領域,lock和latch的區別,
lock是應用層面的,對邏輯內容的互斥,好比行,表,庫,事務
latch是應用不可見的,內部數據結構的互斥

同時,若是一個數據庫只有一個bufferPool,由於全部和磁盤間的數據交換都要經過他,很容易爭搶,解決的方式,

咱們能夠用多個bufferPool,按不一樣的用途,維度區分開

BufferPool在cache的時候有些優化

Pre-Fetching

預取,這個就是dbms本身作cache的好處,你讓os作cache,它無法去知道你下面可能要讀什麼

 

Scan Sharing

簡單的說,就是Query之間能夠共享已經cache的page

 

Buffer Pool ByPass

防止scan操做會污染buffer pool,因此單獨開塊內存去cache query級別的緩存

 

OS Page Cache

OS在文件系統操做的時候,自己會有page cache

既然dbms在buffer pool已經本身管理了page cache,那麼os的這封cache顯的有些多餘,因此通常數據庫都會用direct IO,把OS的page cache給關掉

不關掉有個好處,好比db進程重啓了,但這個時候os的page cache還在,能夠避免冷啓動

 

 

Buffer Replacement Policy

LRU,這個每一個page都有個最後訪問的時間戳,淘汰最老的,但這個須要按時間排序;
Clock,對LRU的近似,更簡單

LRU和Clock對於sequential scan都支持很差,scan很容易就會把以前的buffer給沖掉

因此能夠用LRU-K,記錄下history,算訪問interval,這樣scan這種只訪問一次的,就很容易被淘汰掉

更特化的策略,好比priority hints,dbms知道哪些page比較重要,常常訪問,打上標籤

 

Dirty Page

page被修改過,就不能直接drop掉,主要flush回disk;

若是每次等eviction的時候再去flush髒頁,會讓eviction的過程很是的慢,因此通常會有個後臺進程按期批量的去刷髒頁

 

最後dbms除了有buffer pool來cache tuples和indexes,還有其餘的一些memory pool,

相關文章
相關標籤/搜索