+---------------------+
| collection |
| |
| * top |
| segment-5 |
| * mid |
| * base |
| segment-0...4 |
| * clean |
| |
+---------------------+
然后,Persister
会调用LowerLevelUpdate
将base
区域的segment stack
传递给用户/默认的持久化回调进行持久化
合并操作,返回一个合并后的snapshot
,然后将该snapshot
存储在lowerLevelSnapshot
。
当用户配置了CachePersisted
时,在持久化完成时,会将base
区域的segment stack
移动到clean
区域:
+---------------------+
| collection |
| |
| * top |
| * mid |
| * base |
| * clean |
| segment-0...4 |
+---------------------+
| lower-level-storage |
| |
| mutations from... |
| segment-0...4 |
+---------------------+
否则会被清除:
+---------------------+
| collection |
| |
| * top |
| * mid |
| * base |
| * clean |
| |
+---------------------+
| lower-level-storage |
| |
| mutations from... |
| segment-0...4 |
+---------------------+
默认持久化
如果使用默认的持久方式时,使用的默认持久化回调为:
func(higher Snapshot) (Snapshot, error) {
var ss Snapshot
ss, err = s.Persist(higher, persistOptions)
if err != nil {
return nil, err
if storeSnapshotInit != nil {
storeSnapshotInit.Close()
storeSnapshotInit = nil
return ss, err
其核心函数为persist
它主要做几件事:
将base
区域的数据与之前的snapshot
进行合并、压缩;
以特定格式将snapshot
写入新的持久化文件。
默认持久化采用的写文件的函数为persistBasicSegment
。
持久化文件格式
moss
持久化文件的命名格式为data-xxxxxx-.moss
,中间部分为文件序号,序号最大的文件为最新一份持久化文件。
文件开头4K字节为Header
部分,存储一些元数据。
文件结尾为4K字节(可能超过4K字节,但是4K字节对其存储)的Footer
部分,存储每个SegmentLoc
在文件中的偏移量。SegmentLoc
记录了每个segment
持久化后的信息。
moss
也支持自定义持久化的方式,只要将实现CollectionOptions
中LowerLevelInit/LowerLevelUpdate
这些对象/回调
即可。其原生持久化也是依赖这些接口来实现的。自定义实现持久化时,就不需要使用OpenStoreCollection
来创建collection
了,
直接实现对应接口,使用NewCollection
来创建collection
即可。
moss
作为一个嵌入式cache引擎,嵌入到用户软件中,作为内存型的K-V存储,支持自定义持久化回调,可以方便的将数据
持久化到默认文件或者自定义的database中。但是其多层的线段栈结构也有很大的局限性:
每次操作的batch
都会形成一个segment
,因此,每次batch
的操作必须操作很多数据才能将segment
合并的代价摊薄,
同时每个batch
操作时,每个Key都只能操作一次,因此还可能需要使用map
结构来去重
因此,适用的使用场景还是相当的有限。
持久化的方式使用异步的方式,就丧失了crash consistency
。而且其实现,使得用户难捕捉到持久化失败时详细的错
误,很难精确处理每条数据持久化的错误,因此其就丧失了称为一款存储引擎的资格,充其量只能称为一个不太注重持久化
的cache
,如memcahced
。
moss design