LSM Tree(log-structured merge-tree)是一種文件組織結構的數據結構,目前在不少數據庫中都有使用到,如SQLite、LevelDB、HBase在Mongodb中也有一個LSM引擎;
在傳統的關系型數據庫中使用的是B-/B+ tree作為索引的數據結構,B tree的查詢性能很高,為O(log n)復雜度,但其寫性能並達不到O(log n),而在傳統數據庫中每次插入、刪除數據都要更新索引,每次更新索引都會有一次磁盤IO,頻繁寫時其性能較低;
LSM Tree查詢性能達不到理論的O(log n),但效率並不慢,且其避免了頻繁寫時的磁盤IO,使得非常適用於KV與日志型數據庫;
LSM快的原因
磁盤、內存的順序讀寫性能遠高於隨機讀寫性能,LSM通過消除更新操作(改、刪)在其結構中數據無法改、刪改,只能夠順序的新增追加,從而達到避免了隨機寫的性能問題;
寫的情況解決了,但此時還必須解決隨機讀的性能問題,或者說怎么能夠避免隨機讀;在目前順序追加的兩個場景中通過其特性消除了隨機讀的問題:
1、在WAL(write-ahead log)中場景中其數據是被整體訪問的不存在隨機讀問題;
2、在Kafka中其沒有隨機讀,因為其有明確的offset,有了offset就可通過seek讀取指定數據,明確的物理偏移量;
LSM Tree要解決的是不需要讀取全部數據、無需物理偏移量的讀場景下的高性能讀的問題;
寫數據
外部數據是無序的,但LSM Tree所有寫操作為順序寫,直接無差別的追加並不能雖然實現了順序寫但不能保證數據的有序;在LSM Tree中會在內存中使用一個有序結構(memtable)如(AVL樹、紅黑樹等),寫數據時都寫入其有序樹中,始終保持數據的有序性,當寫入數據達到閾值時觸發有序樹的flush刷盤操作,將數據有序的順序寫入到磁盤中,生成segment,有序樹開始新的周期。
SSTable
LSM Tree持久化后的數據稱為SSTable(Sorted Strings Table),SSTable為按Key排序后的數據,每個SSTable可能包含多個文件成為segment,其內部數據一樣為有序結構,segment為immutable(不可修改);
讀取數據
先從mentable有序樹中查找數據,如未找到數據則從SSTable中讀取指定數據,從最新segment開始依序掃描segment,在每個segment中其內部都是有序數據,可使用二分查找算法進行查詢,可在O(log n)時間內得到結果;
二分查找有兩種情況:一次性把數據全部讀到內存、每次二分時讀取數據;在segment非常大時兩者性能都不夠理想,可在內存內部維護一個稀疏索引(sparse index)【稀疏索引是指將有序數據切分為固定大小的塊,僅對每塊開頭一條數據做索引】,引入稀疏索引后,可在索引表中用二分查找定位key 在哪小塊數據中,后僅從磁盤中讀取一塊數據即可獲得查詢結果,此時加載數據量僅是整個 segment 的一小部分,IO大大降低。
查找的數據存在時不需全盤掃描,但如讀取的數據不存在SSTable中時,此時需要掃描所有segment才能得到最終結果,但此時性能會非常差;
修改、刪除數據
在LSM Tree的SSTabe中數據是不可修改的,也就是不可修改、刪除;此處的修改數據並不是傳統意義上的找到某條記錄並修改它,只是追加一條新的數據記錄當讀取數據是自然會只讀到新數據從而忽略掉老的數據;刪除數據同理,其刪除邏輯為:追加一條數據其值為墓碑,就替換掉了老數據;當SSTable執行合並數據邏輯時,這些“刪除”、“修改”的數據將會被真正的刪除掉;
性能優化
這時可使用布隆過濾器(bloom filter)進行優化,寫入數據之前在布隆過濾器中標記,在讀取時可斷定某條數據是否存在。布隆過濾器有假陽性的概率,但其認為一條數據沒出現過,那該數據一定沒出現過。其認為一條數據出現過,那么該數據很有可能出現過;
文件合並(Compaction)
系統運行越久SSTable的segment數據會越多,讀取時的IO次數越多,性能會降低,此時需要控制segment的數量;LSM Tree會定期執行segment合並操作,將多個segment合並為一個大的segment,清理舊segment,由於segment的有序特性,使用並歸排序就能快速的對數據進行合並,時間復雜度接近O(n);
memtable持久化
為避免memtable有序樹數據還未持久化為SSTable文件時系統就崩潰,可在將數據寫入到memtable時同時將數據寫入到WAL日志中,當程序崩潰時可通過此文件恢復數據;持久化SSTable時需要對memtable與WAL日志做統一的清空處理;
LSM Tree基本流程
1、寫數據:將數據緩存到內存有序樹結構中(memtable),以此同時更新稀疏索引、布隆過濾器;
2、memtable達到預設的閾值,將該有序樹數據flush刷盤到磁盤中,生成有序數據文件segment,此文件為immutable性質,不可修改;
3、讀取數據時,如存在布隆過濾器,先用其驗證如不存在數據就不存在,否則先從mentable有序樹中查找數據如找到數據,依次從新到老順序查詢每個segment,查詢segment使用二分查找對應稀疏索引,知道對應數據offset范圍,讀取磁盤范圍內數據,再次二分查找獲取數據;
4、LSM Tree定期執行compaction操作,將多個segment文件合並為更大的文件;