LevelDb之manifest文件
从这篇文章开始想写写leveldb的小知识。先了解manifest文件,因为这个文件关系到实例的读取。至于这个文件的内部结构以及如何来的,等会再聊。
if instances[Name], err = leveldb.OpenFile(Dir, instance); err != nil {
return errors.New(fmt.Sprintf(`leveldbs: err while open %s : %s`, Dir, err.Error()))
}
这段代码在manifest文件损坏的情况下会出现报错,报错内容:
ERROR leveldb: manifest corrupted: missing [file=MANIFEST-*******]
报错原因:leveldb在存储过程中因为一些其他原因导致程序突然的down掉可能导致manifest文件损害或者丢失。这样就只能手动repair
下了,打不开一个instance
就没有办法执行get
命令。repair
的业务方代码可以这样写,这里以Golang
代码来说明:
if instances[cfgIns.Name], err = leveldb.OpenFile(Dir, instance); err != nil {
if instances[cfgIns.Name], err = leveldb.RecoverFile(Dir, instance); err != nil {
return errors.New(fmt.Sprintf(`err while recoverfile%s : %s`, Dir, err.Error()))
}
}
RecoverFile
这个API函数的源码是:
func RecoverFile(path string, o *opt.Options) (db *DB, err error) {
stor, err := storage.OpenFile(path)
if err != nil {
return
}
db, err = Recover(stor, o)
if err != nil {
stor.Close()
} else {
db.closer = stor
}
return
}
manifest文件修复还是蛮简单的,说明Google的大神早就想到了这些东西。通过上面的例子先感受下manifest文件的存在,下面我们学习下manifest文件结构和文件的由来。文件结构如图:

再配合Golang中定义的struct看看:
type session struct {
stNextFileNum uint64 // current unused file number
stJournalNum uint64 // current journal file number; need external synchronization
stPrevJournalNum uint64 // prev journal file number; no longer used; for compatibility with older version of leveldb
stSeqNum uint64 // last mem compacted seq; need external synchronization
stTempFileNum uint64
stor storage.Storage
storLock util.Releaser
o *cachedOptions
icmp *iComparer
tops *tOps
manifest *journal.Writer
manifestWriter storage.Writer
manifestFile storage.File
stCompPtrs []iKey // compaction pointers; need external synchronization
stVersion *version // current version
vmu sync.Mutex
}
manifest
文件存放versionset
(版本集合信息)。versionset
是一个链表,每个节点就是一个version
,version
中主要包含有.sstable
文件大小,文件编号,有序值的最大值和最小值等等信息。
type tSet struct {
level int
table *tFile
}
type version struct {
s *session
tables []tFiles
cLevel int
cScore float64
cSeek unsafe.Pointer
ref int
next *version
}
type tFile struct {
file storage.File
seekLeft int32
size uint64
imin, imax iKey
}
好吧,manifest文件里面存放的东西大致上就这些了,下面聊聊manifest文件的创建和修改过程。这个问题还是有点难度的,这里还是以Golang
的github.com/syndtr/goleveldb/leveldb
这个库为例来说明。newManifest
是创建一个manifest, encode
编码后写入文件,最后将manifest文件的名称写入到current
文件中,current
只记录当前的manifest文件的文件名称: _, err = fmt.Fprintln(w, f2.name())
。
func (s *session) newManifest(rec *sessionRecord, v *version) (err error) {
num := s.allocFileNum()
file := s.stor.GetFile(num, storage.TypeManifest)
writer, err := file.Create()
if err != nil {
return
}
jw := journal.NewWriter(writer)
if v == nil {
v = s.version()
defer v.release()
}
if rec == nil {
rec = &sessionRecord{}
}
s.fillRecord(rec, true)
v.fillRecord(rec)
defer func() {
if err == nil {
s.recordCommited(rec)
if s.manifest != nil {
s.manifest.Close()
}
if s.manifestWriter != nil {
s.manifestWriter.Close()
}
if s.manifestFile != nil {
s.manifestFile.Remove()
}
s.manifestFile = file
s.manifestWriter = writer
s.manifest = jw
} else {
writer.Close()
file.Remove()
s.reuseFileNum(num)
}
}()
w, err := jw.Next()
if err != nil {
return
}
err = rec.encode(w)
if err != nil {
return
}
err = jw.Flush()
if err != nil {
return
}
err = s.stor.SetManifest(file)
return
}
一边学习,一边记录,end ~