单机存储系统
單機存儲系統就是單機存儲引擎的一種封裝,而單機存儲引擎分為:
- 哈希存儲引擎
- B樹存儲引擎
- LMS樹存儲引擎
哈希存儲引擎
Bitcask是一個基于哈希表結構的鍵值存儲結構。
它的特點是寫時追加,也就是說它每次在文件中只會追加數據而不會修改,所以文件大小超過限制時,會新建一個活躍數據文件,而達到大小限制的文件就叫作老數據文件。
Bitcask的數據結構:
內存中哈希索引表的數據結構:
- 讀取操作:用內存中的哈希索引表的數據結構,通過主鍵找到對應的文件編號,value長度和位置,就可以在活躍數據文件找到對應item(每一個item都是Bitcask數據結構),得到value值。
- 刪除:原item的value中存入刪除標記。
- 新增:直接添加一條item條目。
- 更新:原item不變,跟新增操作一樣。
定期合并:Bitcask系統中的記錄刪除后者更新后都會讓原來的數據變為垃圾文件,所以要進行定期合并,對同一個key只保留最新的一個。
快速恢復:哈希索引表存在內存中,一旦斷電,重建這個哈希索引表需要掃一遍磁盤中的數據文件,為了加快重建速度,Bitcask通過索引文件來提高重建hash表的速度。索引文件其實就是內存中的哈希索引表轉儲到磁盤生成的結果文件。
B樹存儲引擎
Mysql的Innodb是按照頁來組織數據的,也就是說內存中存儲的是頁,對應B+樹的一個節點。
- 查詢:從B+樹的根節點開始進行二分查找知道找到葉子節點,而且每次讀取一個節點,如果頁不在內存中,則需要從磁盤中讀取出來并把對應的頁緩存起來。
- 修改:首先需要記錄提交日志,然后再修改內存中的B+樹。內存中修改的頁超過一定比率,就會將頁面刷新到磁盤中持久化。
緩存區管理
基本的LRU:將近期訪問的item存到一個鏈表中,每次新訪問的item加到鏈表頭部,同時淘汰掉鏈表尾部item(即最近最少訪問到的item被淘汰掉)。
存在的問題1:如每次訪問item都要更新一次LRU鏈表,都需要對整個LRU加鎖
存在的問題2:若果一次查詢中掃描了大量的item數據,則會導致緩存池LRU鏈表中的大部分或者全部數據被替換掉,從而污染緩存池。
改進的LRU:
對問題1:犧牲精度來減小鎖粒度
分段LRU鏈表(如Mysql):將LRU鏈表分為前后兩部分,如果訪問的item在前部分則不進行任何操作,即不用將該item移動到 頭部。只有在后半部分時才加鎖,移動到頭部。
計時LRU鏈表(如memcached):鏈表的每個節點item都存儲一個最近訪問時間,每次訪問該item時,只有當距離上次訪問時間 操作某個設定值才會移動該item到鏈表頭部。
塊LRU鏈表(如OceanBase):鏈表的每個節點不是單獨的一個item數據記錄而是以塊比如2MB的內存塊。每次移動或者淘汰都 以塊為基本單位。每個塊保存一個訪問計數和最近訪問時間,每次訪問塊中的任何一個item都會將該塊的訪問計數加1,當該塊的訪問計數達到某個設定值(比如所有塊的平均訪問次數)時就更新該塊的最近訪問時間。按塊的最近訪問時間來淘汰塊。
對問題2:分級緩存
LIRS算法(如MySQL InnoDB):LIRS將數據分為兩部分:LIR(Low Inner-reference Recency)和HIR(High Inner-reference Recency),其中,LIR中的 數據是熱點,在較短的時間內被訪問了至少兩次。LIRS可以看成是一種分級思想:第一級是HIR,第二級是LIR,數據先進入到第一級,當數據在較短的時間內被訪問兩次時成為熱點數據則進入LIR,HIR和LIR內部都采用LRU策略。這樣,LIR中的數據比較穩定。類似的,如可以實現兩級cache,cache元素先進入第一級cache,當訪問頻率達到一定值(比如2)時升級到第二級,第一級和第二級均內部采用LRU進行替換。
LSM樹存儲引擎
LSM的基本思想就是將的數據的修改增量保存在內存中,當內存達到大小限制后將這些增量數據批量寫入磁盤,讀取時需要合并磁盤中的歷史數據和內存中的增量數據。
- 當應用寫入一條記錄時,LevelDB會首先將修改操作寫入到操作日志文件,成功后再將修改操作應用到MemTable,這樣就完成了寫入操作。
- 當MemTable占用的內存達到一個上限值后,需要將內存的數據轉儲到外存文件中。LevelDB會將原先的MemTable凍結成為不可變MemTable,并生成一個新的MemTable。新到來的數據被記入新的操作日志文件和新生成的MemTable中。
- LevelDB后臺線程會將不可變MemTable的數據排序后轉儲到磁盤,形成一個新的SSTable文件,這個操作稱為minor Compaction。
SSTable中的文件是按照記錄的主鍵排序的,每個文件有最小的主鍵和最大的主鍵。 - 當某個層級下的SSTable文件數目超過一定設置值后,levelDB會從這個層級中選擇SSTable文件,將其和高一層級的SSTable文件合并,這就是major compaction。
- LevelDB的清單文件記錄了所有元數據,包括屬于哪個層級、文件名稱、最小主鍵和最大主鍵,它會隨著Compaction進行而重新生成新的清單文件,所以需要當前文件來記錄了當前使用的清單文件名。
LevelDB寫入操作很簡單,但是讀取操作比較復雜,需要先查看內存中的MemTable,然后在不可變MemTable中查找,接下來再從SSTable文件中讀取。
總結
- 上一篇: 网易2019实习生Java编程题
- 下一篇: 分布式系统之异常