mysql、postgresql、hbase、tidb數(shù)據(jù)庫存儲方案對比
| MySQL Innodb | PostgreSql | HBase | TiDB | |
| 存儲模型 |
1、采用page + buffer pool + redo log的方案,內(nèi)存中的buffer pool緩存 了磁盤中的頁面,通過哈希表判斷訪問的頁是否在內(nèi)存中,buffer pool中還維 護了一個頁面的LRU鏈表,分為new和old兩個區(qū)域,同時還維護了free list和 flush list兩個鏈表,分別用于存放空閑頁和臟頁,臟頁同時存在于LRU和flush list中。 2、redo log放在重做日志緩沖中,事務提交時,只需其對應的redo log被寫 入磁盤即可,不要求臟頁刷盤,由于redo log是順序?qū)懀K頁刷盤是隨機寫, 因此提高了寫入性能。每條redo log都有一個lsn號,頁面中也保存了最新lsn, 重做時根據(jù)lsn判斷是否應用redo log。flush list中的臟頁和redo log逐一對 應。 3、采用undo log機制,數(shù)據(jù)更新時,頁面保存最新版數(shù)據(jù),舊數(shù)據(jù)放入undo log中; 4、采用checkpoint機制,將臟頁定時寫入磁盤(double write),臟頁寫入 磁盤后,其redo log就不再需要,因此最近一次checkpoint前的redo log可 以被覆蓋重用。 5、采用change buffer機制,將對那些不在buffer pool中的頁面的操作保存 下來,等change buffer滿或有select查詢該頁時,再將這些操作merge到 buffer pool中的頁面上。 |
1、采用page + buffer pool + redo log的方案,內(nèi)存中的buffer pool緩存 了磁盤中的頁面,通過哈希表判斷訪問的頁是否在內(nèi)存中,采用clock-sweep 算法來進行緩存頁的淘汰。使用free list管理空閑頁面,buffer pool使用動態(tài) 的內(nèi)存管理方式,可以根據(jù)實際需要增加或減少。
2、采用redo log機制,事務提交時只需其對應的redo log被寫入磁盤即可,
不要求臟頁刷盤,由于redo log是順序?qū)懀K頁刷盤是隨機寫,因此提高了
寫入性能。根據(jù)lsn號判斷是否需要應用redo log。
3、沒有undo日志,mvcc的舊數(shù)據(jù)原地殘留在數(shù)據(jù)頁中,新數(shù)據(jù)在heap中 重新分配,有HOT(heap only tuple)機制,即舊數(shù)據(jù)指向新數(shù)據(jù),索引指向 記錄的指針不變,通過舊數(shù)據(jù)找到新數(shù)據(jù);若非HOT場景,則更新數(shù)據(jù)需要 更新所有索引,即使沒有更新索引字段。有vacuum進程定期去釋放不再使 用的行和索引key占用的空間,vacuum后臺進程對IO資源占用比較多,會導 致TPS和延時不穩(wěn)定。 4、采用checkoutpoint機制,將臟頁按一定觸發(fā)條件寫入磁盤。會遍歷 buffer pool中所有的數(shù)據(jù)緩存頁,然后將臟頁取出并按照其對應的磁盤位 置進行排序,然后依次刷新到磁盤,臟頁刷盤后wal日志就可以被刪除或者 回收。
|
1、采用基于key-value的列存儲方案,記錄中無需保存所有字段,只需保存不為null的字段。 2、采用類似lsm-tree的存儲方案,對數(shù)據(jù)的增刪改通過log的方式順序存儲在內(nèi)存MemStore中(使用跳表數(shù)據(jù)結構),當 MemStore中的數(shù)據(jù)量達到一定閾值時,數(shù)據(jù)會被刷寫到磁盤中,即為HFile文件,MemStore刷盤的過程不阻塞系統(tǒng)的讀寫。后臺線程按一定規(guī)則將小HFile文件合并為大文件,有minor合并和major壓縮合并兩種方式,minor將多個小文件合并為少數(shù)的大文件,major將若干個HFile重寫為一個新HFile。 3、自動分區(qū),HBase中擴展和負載均衡的基本單元為region,當region的大小達到一定閾值時,會分裂成兩個新的region,行數(shù)據(jù)根據(jù)RowKey的范圍可以分為多個region,region由region server管理,每個region server負責管理多個region,可以通過動態(tài)增加region server的數(shù)量來動態(tài)擴容。若其中一臺region server宕機,其負責的region會被轉(zhuǎn)移到其他region server上,通過WAL日志來恢復數(shù)據(jù)。 4、數(shù)據(jù)存儲在HFile中, HFile存儲在HDFS中。HFile中存儲的是經(jīng)過排序的鍵值映射結構,且鍵值是連續(xù)遞增的,因此無需為新插入的鍵預留位置。 5、具有WAL日志(HLog),HBase 的寫入主要分三步:1.先寫 WAL 日志;2.再寫入 memstore;3.最后寫 Sync wal, 當region server宕機,可通過HLog恢復出MemStore中尚未刷盤的數(shù)據(jù)。 6、使用Zookeeper管理集群,只有一個主節(jié)點,主節(jié)點負責region管理和遷移,負責全局region的負載均衡,還負責元數(shù)據(jù)管理。 7、使用布隆過濾器,它保存在HFile的metaData中,使用布隆過濾器可以判斷數(shù)據(jù)是否在某個數(shù)據(jù)塊中,減少HFile中數(shù)據(jù)塊的無效訪問。 8、Region里邊會有多個Store,每個Store其實就是一個列族的數(shù)據(jù)(所以我們可以說HBase是基于列族存儲的),Store里邊有MemStore和HFile。
|
1、TiDB是HTAP數(shù)據(jù)庫,分為TiKV和TiFlush兩個存儲引擎,分別儲存OLTP和OLAP類型的數(shù)據(jù)。TiKV底層使用RocksDB來持久化KV數(shù)據(jù),TiKV管理數(shù)據(jù)的邏輯分片(region),將實際對數(shù)據(jù)的操作發(fā)給RocksDB處理。 2、采用基于key-value的列存儲方案,每行數(shù)據(jù)按如下形式編碼: 每一行記錄對應一個RowID,對于行ID,TiDB 做了一個小優(yōu)化,如果某個表有整數(shù)型的主鍵,TiDB 會使用主鍵的值當做這一行數(shù)據(jù)的行 ID。 3、每行數(shù)據(jù)有唯一的RowKey,RowKey包含了表Id和行Id,這樣就確保了同一張表的所有數(shù)據(jù)放在一起,同一行中的所有列放在一起。 4、將 SQL 查詢映射為對 KV 的查詢,再通過 KV 接口獲取對應的數(shù)據(jù),最后執(zhí)行各種計算。 5、每一個TiKV實例都管理了若干region,region中包含了一定范圍RowKey的行數(shù)據(jù),TiDB集群中有若干TiKV實例,region在這些實例中相互備份,使用raft協(xié)議進行同步數(shù)據(jù),在每一個TiKV實例中,region管理的數(shù)據(jù)在底層使用RocksDB來實現(xiàn)KV數(shù)據(jù)的持久化。 6、具有TiFlush OLAP存儲引擎,TiFlush中的數(shù)據(jù)需要從TiKV中同步過來。由于TiKV 本身沒有 Table 的概念,TiDB 需要將 Table 數(shù)據(jù)按照 Schema 編碼為 Key-Value 的形式后寫入相應TiKV Region,然后通過 Multi-Raft 協(xié)議同步到 TiFlash,再由 TiFlash 根據(jù) Schema 進行解碼拆列和持久化等操作。 |
| 索引方案 |
1、主鍵采用B+樹,主鍵即數(shù)據(jù),葉子節(jié)點存儲數(shù)據(jù)本身; 2、采用稀疏索引,只能索引到數(shù)據(jù)所在頁面,需要在頁面中通過二分法查找 到具體數(shù)據(jù),數(shù)據(jù)在頁面中邏輯上依主鍵升序排列,物理上無序; 3、主鍵B+樹中,數(shù)據(jù)頁面按照索引字段順序連接在一個雙向鏈表中,但在 磁盤中可能不是順序分布; 4、還支持自適應hash索引,以及全文索引。 |
1、數(shù)據(jù)行在heap中存儲,在邏輯上和物理上都沒有任何特定順序,b樹 索引采用nbtree。 2、支持b樹索引,哈希索引,倒排索引等。 |
1、支持輔助索引,實現(xiàn)方式是在HBase中創(chuàng)建一個新的表,該表的Rowkey是輔助索引的值,值為原表的Rowkey。 2、hbase的索引有:1、全局索引;2、覆蓋索引;3、本地索引。全局索引只能查詢row key中包含的字段,用前綴過濾,否則索引表不會生效,如果查詢語句中的條件字段或返回字段不是索引字段,就會觸發(fā)全表掃描。 |
1、TiDB 同時支持主鍵和二級索引(包括唯一索引和非唯一索引)。與表數(shù)據(jù)映射方案類似,TiDB 為表中每個索引分配了一個索引 ID,用 2、無論是表數(shù)據(jù)還是索引數(shù)據(jù)的 Key 編碼方案,一個表內(nèi)所有的行都有相同的 Key 前綴,一個索引的所有數(shù)據(jù)也都有相同的前綴。這樣具有相同的前綴的數(shù)據(jù),在 TiKV 的 Key 空間內(nèi),是排列在一起的。因此只要小心地設計后綴部分的編碼方案,保證編碼前和編碼后的比較關系不變,就可以將表數(shù)據(jù)或者索引數(shù)據(jù)有序地保存在 TiKV 中。采用這種編碼后,一個表的所有行數(shù)據(jù)會按照
|
| 事務方案 |
1、 支持讀已提交、可重復讀、串行化隔離級別,默認可重復讀級別; 2、可重復讀級別通過間隙鎖防止幻讀的發(fā)生,幻讀:由于其他事務的提交,當 前事務的前置條件已不再滿足,再次使用前置條件查詢時得到的是新的結果; 3、通過redo log實現(xiàn)持久性,通過undo log實現(xiàn)原子性,通過鎖實現(xiàn)一致 性,通過mvcc實現(xiàn)隔離性;undo log和數(shù)據(jù)頁一樣也會產(chǎn)生redo log,也會 被刷臟,故障恢復時會由redo log恢復出undo log,然后通過undo log將事 務未提交但已刷臟的頁回滾。 4、通過活躍事務快照和記錄中的事務id來判斷的可見性,讀已提交級別是每 次查詢時生成一個快照,可重復讀是在第一次查詢時生成快照,后面查詢不 再生成。 5、取消事務時,需要應用undo log中的日志,將數(shù)據(jù)恢復。 |
1、支持讀已提交、可重復讀、串行化隔離級別,默認可重復讀級別。 2、mvcc的新舊數(shù)據(jù)都保存在heap中,通過活躍事務快照、clog、xmin和 xmax判斷記錄的可見性。讀已提交級別是每次查詢時生成一個快照,可重復 讀是在第一次查詢時生成快照,后面查詢不再生成。 3、事務回滾時,只需將該事務在clog中的狀態(tài)置為abort即可,不用回滾數(shù)據(jù) 頁的修改,由后臺線程回收舊數(shù)據(jù)。
|
1、僅支持行級事務,且目前只支持單行級別的 read uncommitted 和 read committed 隔離級別。 2、采用MVCC機制,HBase的增刪改記錄都有「版本」,默認以時間戳的方式實現(xiàn)。如果查詢時沒有指定版本號,默認返回最新數(shù)據(jù)。 3、寫寫并發(fā)控制,在寫入(或更新)之前先獲取行鎖,寫入完成后釋放。支持批量寫入(或批量更新),使用兩階段鎖協(xié)議,首先獲取所有待寫入(更新)行記錄的行鎖,寫入完成之后再統(tǒng)一釋放所有行記錄的行鎖。 4、讀寫并發(fā)控制,利用數(shù)據(jù)的多版本,當讀請求到來時,若讀取的行正在被另一個事務改寫,則該讀請求將讀取改行數(shù)據(jù)的歷史版本。 |
|
| 缺點 |
1、當有大量數(shù)據(jù)插入或修改時,索引節(jié)點會頻繁拆分和合并,將會導致大量的隨機 磁盤IO,影響寫入性能,不適合OLAP場景。 2、checkpoint刷臟機制,當redo log或buffer pool滿了,會停止接受插入和 更新,就要根據(jù)LRU算法將臟頁面刷盤,這時會導致隨機IO。 3、采用undo段設計,回滾時需要重做每一個undo log,回滾較慢。 4、長事務會使undo log無法及時釋放,占用大量空間,且老事務需要遍歷整個 版本鏈才能找到需要的版本。 |
1、更新數(shù)據(jù)時,需要將舊數(shù)據(jù)copy一份,容易引起數(shù)據(jù)頁膨脹。 2、不支持cluster索引,即主鍵相鄰的兩條記錄在磁盤上也是相鄰的,無法 享受到cluster 索引帶來的性能優(yōu)勢。 3、在快照隔離級別和串行化隔離級別下,兩個事務更新同一行,則只有一個 事務可以成功,另一個事務阻塞,并待第一個事務提交后回滾。當大量事務 更新熱點行時,會造成大量事務回滾。
|
1、讀數(shù)據(jù)性能較慢,需要依次遍歷HFile直到找到所需數(shù)據(jù)。 2、需要后臺線程不斷合并增量數(shù)據(jù),合并的頻率和數(shù)量可能會影響磁盤IO。 |
1、RocksDB 存在著數(shù)十倍的寫入放大效應。在寫入量大的應用場景中,這種放大效可能會觸發(fā) IO 帶寬和 CPU 計算能力的瓶頸影響在線寫入性能。除了寫入性能之外,寫入放大還會放大 SSD 盤的寫入磨損,影響 SSD 盤的壽命。不過TiDB基于RocksDB 自研的KV存儲引擎Titan部分緩解了這個問題。 |
| 優(yōu)點 |
1、讀寫性能比較均衡。 2、修改元組時不需要拷貝整行,只需要將被修改列的舊值存入回滾段。 3、垃圾回收的影響更小,清理舊版本不涉及數(shù)據(jù)頁,只需要將最老活躍事務之 前的事務undo log清除。
|
1、heap表插入、修改和刪除數(shù)據(jù)采用追加的方式,速度較快,適合OLAP 場景。 2、事務回滾速度較快,只需要將事務在clog中的狀態(tài)置為abort即可。 3、修改數(shù)據(jù)時,無需修改索引,因為索引指向數(shù)據(jù)在頁面中的邏輯指針, 邏輯指針才指向數(shù)據(jù)的物理地址,所以只用修改頁面內(nèi)邏輯指針的指向即可。
|
1、使用日志文件和內(nèi)存將隨機寫轉(zhuǎn)換成順序?qū)懀鼙WC穩(wěn)定的數(shù)據(jù)插入速率,即以磁盤傳輸速率工作。 2、存儲數(shù)據(jù)的布局較優(yōu),查詢一個鍵需要的磁盤尋道次數(shù)的成本是透明的:假如有5個存儲文件,則一個訪問最多需要5次磁盤尋道。 |
1、純分布式架構,擁有良好的擴展性 |
總結:
數(shù)據(jù)庫由計算引擎 + 存儲引擎組成,一款數(shù)據(jù)庫是什么類型(如關系型、KV、圖數(shù)據(jù)庫等)不取決于存儲引擎,而是計算引擎決定的,比如關系型數(shù)據(jù)庫的數(shù)據(jù)可以用KV形式保存(TiDB,底層用RocksDb存儲,采用LSM機制更新),也可以用B+樹保存(innodb),甚至如果不考慮列拼接的性能也可以使用列式存儲;圖數(shù)據(jù)庫可以采用圖原生的設計,也可以基于傳統(tǒng)關系型存儲,使用點表和邊表保存數(shù)據(jù)。計算引擎將數(shù)據(jù)從這些存儲引擎中讀出來后,按業(yè)務場景進行SQL計算或NoSQL計算,從而成為了關系型數(shù)據(jù)、圖數(shù)據(jù)庫、KV、向量、時序等,而某種存儲引擎可能在某種場景下能工作的更好,比如行式存儲引擎不適合olap場景。因此有些數(shù)據(jù)庫只是在基于已有的存儲方案上,封裝了一層計算層中間件,而有些數(shù)據(jù)庫則是將其他數(shù)據(jù)庫的計算層和另外的數(shù)據(jù)庫的存儲層進行組合。


浙公網(wǎng)安備 33010602011771號