Log-Structured Merge-Tree,簡稱 LSM。java
以 Mysql、postgresql 爲表明的傳統 RDBMS 都是基於 b-tree 的 page-orented 存儲引擎。現代計算機的最大處理瓶頸在磁盤的讀寫上,數據存儲沒法繞開磁盤的讀寫,純內存型數據庫除外,但因爲內存存儲的不穩定性,咱們通常只將內存型的存儲做爲緩存系統。sql
爲提高數據庫系統的寫性能,咱們發現磁盤的順序寫性能遠遠大於隨機寫性能,甚至性能高於內存的隨機寫。因此在不少偏向寫性能的數據庫系統中,以犧牲一部分讀性能和增大寫放大的狀況下引入了 LSM 數據結構。數據庫
咱們從頭開始設計一個數據庫引擎。數據模型很簡單,咱們選最簡單的 Key-Value 結構,一條數據只有一個 Key 和一個 Value。操做只有 get 和 put,以下:c#
get(key); put(key, value);
從最簡單的開始,每一個數據庫一個data.db
文件,咱們像寫日誌同樣,將每條記錄 append 到文件結尾。緩存
key1,value1 key2,value2 key3,value3 key10,value10 key8,value8
這樣咱們已經完成了 80%了,而後須要完成讀功能。如上數據文件,若須要查詢 key2 數據,咱們只能從文件開頭開始遍歷,當直到讀取到 key2 數據:數據結構
for (row in rows) { if (row.key == "key1") { return row; } }
好了,一個簡單的數據庫就完成了。架構
什麼?完成了?是的,完成了,雖說拿出去會被砍死,但誰也不可否認它已經完成了一個數據庫系統的最基本功能。app
這樣的遍歷是十分耗費性能的。那麼怎麼提升讀取性能呢?建立一個內存索引「Index」便可,最簡單的方式,在內存中維護一個 Map,存儲每一個 key 對應的文件內容偏移量。這樣讀取一條記錄就只須要一次內存操做加上一次磁盤操做就能夠了。異步
b-tree 是因何出現的?想想上面的 Map 結構的索引有什麼缺點?Map 索引解決了隨機單點讀的性能問題,但沒法解決 Rang 查詢,好比須要查詢 key 在 key1 和 key200 之間的數據。因而,就有了 b-tree,b 樹是有序的結構樹,能夠很簡單的進行 Rang 查詢。post
b-tree 將全部數據都索引在內存中,當數據無限增加時,將沒法在內存中存放這麼大的索引文件。
咱們來看看 LSM 的實現。
SSTable:LSM 的磁盤文件,稱做SSTable(Sorted String Table)。望文得意,LSM 存儲在磁盤中的文件,數據也是按 Key 排序存儲的,這樣就能夠解決上面講到的數據量大了以後沒法將數據所有索引到內存中的問題。若是磁盤文件也是有序的,那麼內存索引能夠採起」稀疏索引「(Sparse Index),能夠每一段記錄一個索引,將數據邏輯上分紅多個block
,稀疏索引只須要記錄每一個block
的偏移量,每條數據經過遍歷block
實現。這樣索引量將大大減少。
Memtable:LSM 的內存結構叫作Memtable。Memtable是一個有序結構,一樣能夠採用樹結構,能夠用跳錶
。LSM 寫數據時,只須要寫入內存中的Memtable,當Memtable到達必定量以後,會異步刷入磁盤,就是上面的SSTable。
immutable Memtable:在數據從內存Memtable刷入SSTable時,爲避免讀寫鎖致使的性能問題,LSM 會在內存中 copy 一份immutable Memtable表,顧名思義,這個數據結構不可改變,新寫入的數據只會寫入新的Memtable,immutable Memtable供刷盤線程讀取,查詢數據的請求也能夠訪問這個數據結構,這樣若是數據在內存中,就不須要訪問磁盤,能夠提供數據查詢的效率。
WAL:write ahead log,預寫日誌,關於 WAL,能夠參考我以前的文章《你常據說的 WAL 究竟是什麼》。在 LSM 中,在數據刷入磁盤前,爲防止異常致使數據丟失,LSM 會先將數據寫入 WAL,而後寫入 SSTable,系統重啓時,LSM 會從 WAL 中回溯 SSTable,當寫完一個 SSTable 時,LSM 會清理掉過時的 WAL 日誌,防止 WAL 過量。
LSM 的寫包括四個流程:
爲保證順序寫磁盤,LSM 不會去直接刪除數據,而是經過寫一條 delete 標識來表示數據被刪除,數據只有在被 Compact 時纔會被真正刪除。
LSM 讀取數據將從memtable、imutable、sstable依次讀取,直到讀取到數據或讀完全部層次的數據結構返回無數據。因此當數據不存在時,須要依次讀取各層文件。LSM 能夠經過引入布隆過濾器來先判斷一個數據是否存在,避免無效的掃文件。
LSM 的合併策略是 LSM 很重要的一個部分,咱們將放在下一篇文章中單獨講解。
LSM 結構的應用十分普遍,諸如Bigtable,HBase,LevelDB,SQLite4, Tarantool , RocksDB,WiredTiger ,Apache Cassandra,InfluxDB 底層都使用了 LSM。只好的文章,咱們將詳細講解 LSM 在 leveldb 或 Cassandra 中的實現。
推薦:
Mysql 大表問題和解決
Mysql 主鍵問題
列式存儲
時間序列數據庫(TSDB)初識與選擇
十分鐘瞭解 Apache Druid
Apache Druid 底層存儲設計
Apache Druid 的集羣設計與工做流程