etcd-raft-儲存分析
etcd raft介紹
etcd raft是目前使用最廣泛的raft庫,如果想深入瞭解raft請直接閱讀論文 “In Search of an Understandable Consensus Algorithm”( https:// raft.github.io/raft.pdf ), etcd raft在etcd, Kubernetes, Docker Swarm, Cloud Foundry Diego, CockroachDB, TiDB, Project Calico, Flannel等分散式系統中都有應用,在生成環境得到了驗證。 傳統raft庫的實現都是單體設計(整合了儲存層、訊息序列化、網路層等), etcd raft繼承了簡約的設計理念,只實現了最核心的raft演算法, 這樣更加的靈活。etcd將網路、日誌儲存、快照等功能分開,透過獨立的模組實現,使用者可以在需要時呼叫。etcd自身實現了自己的一套raft配套庫:etcd-wal(用於儲存日誌),snap(用於儲存快照),MemoryStorage(用於儲存當前日誌、快照、狀態等資訊以供raft核心程式使用)。
etcd wal介紹
WAL是write ahead log的縮寫,etcd使用wal模組來完成raft日誌的持久化儲存,etcd對wal的所有實現都放在wal目錄中。
wal資料結構
type WAL struct {
lg *zap.Logger
dir string
// the living directory of the underlay files
// dirFile is a fd for the wal directory for syncing on Rename
dirFile *os.File
metadata []byte
// metadata recorded at the head of each WAL
state raftpb.HardState
// hardstate recorded at the head of WAL
start walpb.Snapshot
// snapshot to start reading
decoder *decoder
// decoder to decode records
readClose func() error
// closer for decode reader
mu sync.Mutex
enti uint64
// index of the last entry saved to the wal
encoder *encoder
// encoder to encode records
locks []*fileutil.LockedFile
// the locked files the WAL holds (the name is increasing)
fp *filePipeline
}
上述為wal的資料結構,透過用wal.go檔案中的Create()方法來獲取wal的例項。wal首先會建立一個臨時目錄並初始化相關變數,並建立和初始化第一個wal檔案,等所有的操作都初始化完成後直接更改臨時目錄的名字完成wal例項的初始化。
檔案組織
wal的所有日誌放在一個指定目錄下,日誌的檔名以 .wal 作為結尾,格式為-.wal,seq和index的格式都為%016x,例如:0000000000000001-0000000000000001.wal。index代表這個檔案中第一條raft日誌的index,seq是這個檔案的序列號(依次遞增)。
每個檔案的大小預設為64M,當檔案大於64M時,wal會自動生成新的日誌檔案用於儲存日誌。每個日誌檔案都會使用flock鎖定檔案,引數為LOCK_EX,這是一把獨有鎖,同一時間只能有一個程式可以操作這個日誌檔案,所以當wal佔有這個檔案時,透過程式是無法刪除這個檔案的。
日誌邏輯組織
wal日誌可以儲存多種型別的資料,具體如下。
- crcType 每個新的日誌檔案的第一條記錄都會是crcType型別的記錄,crcType也只會在每個日誌檔案的開始時寫入,用於記錄上一個檔案最後的crc是多少
- metadataType 每個新的日誌檔案中metadataType緊跟在crcType記錄後面,每個日誌檔案只會出現一次
- stateType 這種日誌型別會在兩種情況下加入:
- 自動切分日誌檔案時,新的日誌檔案中,緊跟在metadataType後面會存入一條stateType的日誌
- 當raft核心程式ready中返回hard state時也會儲存該型別的日誌
- snapshotType wal日誌中只會儲存snapshot的term和index,具體的資料儲存在專門的snapshot中,每次儲存快照都會在wal日誌中儲存一個wal的快照。當儲存快照時,會將快照之前index的日誌檔案都釋放掉。wal中儲存的snapshot主要用於檢查快照是否正確。
日誌讀寫
wal透過封裝的encoder和decoder模組來實現日誌讀寫。
寫日誌
encoder模組把會增量的計算crc和資料一起寫入到wal檔案中。 下面為encoder資料結構
type encoder struct {
mu sync.Mutex
bw *ioutil.PageWriter
crc hash.Hash32
buf []byte
//快取空間,預設為1M,降低資料分配的壓力
uint64buf []byte
}
wal透過encoder實現寫日誌,在這個模組中會完成crc的計算。wal為了更好的管理資料,日誌中的每條資料都會以8位元組對齊(wal會自動對齊位元組)。 日誌寫入的流程如下。
圖中的crc是增量計算,以之前的所有日誌資料為增量基礎。 wal只關注寫入日誌,不會校驗日誌的index是否重複,但是如果重啟這個Node的話,系統會自動過濾掉中間混雜的日誌。
日誌切分
wal實現了日誌自動切分,當日志資料大於預設的64M時就會生成新的檔案寫入日誌,日誌的切分透過wal.go檔案中的cut方法來實現。cut方法只會在呼叫wal中的Save方法才會觸發呼叫,新檔案的第一條記錄就是上一個wal檔案最後的crc。
日誌compact
wal沒有實現日誌的自動compact,系統只提供了MemoryStorage的日誌compact方法(需要使用者主動呼叫)。
file_pipeline模組
wal新建新的檔案時都是先新建一個tmp檔案,當所有操作都完成後再重新命名這個檔案。wal使用file_pipeline這個模組在後臺啟動一個協程時刻準備一個臨時檔案以供使用,從而避免臨時建立檔案的開銷。
etcd snap介紹
etcd raft自帶了 go.etcd.io/etcd/etcdserver/api/snap模組來實現快照的儲存。
檔案組織
在snap模組中一個快照用一個字尾名為.snap的檔案儲存,檔案格式為-.snap, term和index分別代表快照日誌所處的term和index。 每個快照具體儲存結構如下圖:
詳細介紹
系統可以有多個快照,snap模組使用Snapshotter結構統一管理快照。
type Snapshotter struct {
lg *zap.Logger
dir string
}
上面snapshotter的結構程式碼,snapshotter主要用於儲存和讀取快照。
快照具體儲存的內容需要使用者來指定,例如在raft的官方例子中直接將當時的kv資料Marshal之後儲存到快照中。
func (s *kvstore) getSnapshot() ([]byte, error) {
s.mu.RLock()
defer s.mu.RUnlock()
return
json.Marshal(s.kvStore)
}
何時打快照
在etcd-raft中使用者可以選擇何時打快照,在etcd的官方案例中打快照的方法是maybeTriggerSnapshot(),這個方法在節點的Ready()方法返回時呼叫,當前提交的index值與上一次大快照的index值大於10000時會打新的快照。
etcd MemoryStorage介紹
MemoryStorage用於儲存raft節點臨時的資料,包括entrys、快照等。使用者將資料儲存到memoryStorage中,raft節點也會使用這些資料。包括entrys的傳遞、快照的傳送等都是從memoryStorage中傳送。
// MemoryStorage implements the Storage interface backed by an
// in-memory array.
type MemoryStorage struct {
// Protects access to all fields. Most methods of MemoryStorage are
// run on the raft goroutine, but Append() is run on an application
// goroutine.
sync.Mutex
hardState pb.HardState
snapshot pb.Snapshot
// ents[i] has raft log position i+snapshot.Metadata.Index
ents []pb.Entry
}
memoryStorage會儲存最新的entrys(包括哪些沒有commit)、快照和狀態,使用者在收到其它節點傳送的相關資料時需要將資料儲存到memorystorage中。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31559758/viewspace-2681225/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- swift 物件儲存地址分析Swift物件
- 指標儲存地址分析指標
- Laravel 儲存 (Storage) 原始碼分析Laravel原始碼
- 塊儲存 檔案儲存 物件儲存物件
- 【Redis】redis各型別資料儲存分析Redis型別
- PG wal 日誌的物理儲存分析
- Druid:實時分析資料儲存UI
- 簡單分析oracle的資料儲存Oracle
- 使用DBMS_STATS儲存分析結果
- 行式儲存 列式儲存
- 儲存—物件儲存_Minio物件
- 自動儲存、靜態儲存和動態儲存
- 【RocketMQ原始碼分析】深入訊息儲存(2)MQ原始碼
- 【RocketMQ原始碼分析】深入訊息儲存(3)MQ原始碼
- 雲端儲存系統監控服務分析
- MyISAM 儲存引擎,Innodb 儲存引擎儲存引擎
- 資料儲存--檔案儲存
- 儲存
- 物件儲存 vs 檔案儲存 vs 塊儲存,選哪個?物件
- kubernetes/k8s CSI分析-容器儲存介面分析K8S
- 儲存過程與儲存函式儲存過程儲存函式
- PostgreSQL儲存智慧-空間聚集儲存SQL
- Redis儲存結構以及儲存格式Redis
- RocketMQ中Broker的訊息儲存原始碼分析MQ原始碼
- EMC儲存Raid故障資料分析報告AI
- 通過MySQL儲存原理來分析排序和鎖MySql排序
- Kafka原始碼分析(三) - Server端 - 訊息儲存Kafka原始碼Server
- 50億海量資料如何高效儲存和分析?
- 從原始碼分析RocketMq訊息的儲存原理原始碼MQ
- MySQL資料分析儲存引擎示例講解HKEAMySql儲存引擎
- 深度分析C#中Array的儲存結構C#
- 聚焦資料時代新儲存需求,浪潮儲存的新儲存之道
- 雲原生儲存詳解:容器儲存與 K8s 儲存卷K8S
- mysql儲存函過程和儲存函式都屬於儲存程式MySql儲存函式
- 物件儲存,未來儲存新潮流物件
- Android儲存(2)– 介面卡儲存Android
- Flutter持久化儲存之檔案儲存Flutter持久化
- 容器附加儲存(CAS)是雲原生儲存