暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

etcd存储格式分析:snap与wal

零君聊软件 2021-06-06
4819

本文简要总结了etcd中的snap与wal的存储格式。snap是快照(snapshot), wal是Write Ahead Log。本文是基于etcd 3.4.15的代码进行分析的。


snap存储格式

Snap文件位于${etcd}/member/snap目录下,是后缀为.snap的文件。snap的格式主要是定义在snap.proto文件中:

    https://github.com/etcd-io/etcd/blob/v3.4.15/etcdserver/api/snap/snappb/snap.proto
    复制

    每个.snap文件其实就是存储的下面这个结构体的内容:

      type Snapshot struct {
      Crc uint32 `protobuf:"varint,1,opt,name=crc" json:"crc"`
      Data []byte `protobuf:"bytes,2,opt,name=data" json:"data,omitempty"`
      XXX_unrecognized []byte `json:"-"`
      }
      复制

      对应的源代码:

        https://github.com/etcd-io/etcd/blob/v3.4.15/etcdserver/api/snap/snappb/snap.pb.go#L38-L42
        复制

        用一个简单的图来表示,就是下面这个样子。

        Crc是内容的crc校验和,Data是snap的具体内容。其中Data的存储的其实又是下面这个结构体:

          type Snapshot struct {
          Data []byte `protobuf:"bytes,1,opt,name=data" json:"data,omitempty"`
          Metadata SnapshotMetadata `protobuf:"bytes,2,opt,name=metadata" json:"metadata"`
          XXX_unrecognized []byte `json:"-"`
          }
          复制

          源代码位置:

            https://github.com/etcd-io/etcd/blob/v3.4.15/raft/raftpb/raft.pb.go#L284-L288
            复制

            用一个简单的图来表示,就是下面这样:

            其中Data存储的又是下面这个结构体,

              type store struct {
              Root *node
              WatcherHub *watcherHub
              CurrentIndex uint64
              Stats *Stats
              CurrentVersion int
              ttlKeyHeap *ttlKeyHeap need to recovery manually
              worldLock sync.RWMutex stop the world lock
              clock clockwork.Clock
              readonlySet types.Set
              }
              复制

              源代码位置:

                https://github.com/etcd-io/etcd/blob/v3.4.15/etcdserver/api/v2store/store.go#L74-L84
                复制


                其实存储的就是结构体store的json序列化之后的字符串而已。除了序列化和反序列化结构体store是etcd自己实现的之外,其它所有的存储/解析都是借助于protobuf自动生成的代码完成的。

                其实snap存储的核心内容就是这个结构体store。要讲清楚这个结构体,不是一两句话的事,回头再单独写文章说。



                如果你打开一个.snap文件,你会发现除了开头的几个字节是不可见字符之外,后面的都是可见字符,因为就是直接存储的json格式序列化之后字符串。例如:


                用一个完整的图来表示snap的存储结构:

                下面的代码片段可以理解snap的存储结构:

                  https://github.com/etcd-io/etcd/blob/v3.4.15/etcdserver/api/snap/snapshotter.go#L189-L230
                  https://github.com/etcd-io/etcd/blob/v3.4.15/etcdserver/api/v2store/store.go#L767-L780
                  复制


                  wal存储格式

                  wal文件位于{etcd}/member/wal目录下,是后缀为.wal的文件。每个wal文件是由一条条的record组成的,用一个简单的图表示:

                  每条record的格式如下:

                  从其中length字段可以分别解析出后面的record和pad的实际长度。其中低56位存储record bytes的具体长度。高8位可以解析出后面的pad bytes的具体长度。具体见下图描述:

                  对应的源码:

                    https://github.com/etcd-io/etcd/blob/v3.4.15/wal/decoder.go#L122-L131
                    复制


                    pad bytes是填充字节,忽略即可。而record bytes具体又是存储的下面这个结构体:

                      type Record struct {
                      Type int64 `protobuf:"varint,1,opt,name=type" json:"type"`
                      Crc uint32 `protobuf:"varint,2,opt,name=crc" json:"crc"`
                      Data []byte `protobuf:"bytes,3,opt,name=data" json:"data,omitempty"`
                      XXX_unrecognized []byte `json:"-"`
                      }
                      复制

                      源码位置:

                        https://github.com/etcd-io/etcd/blob/v3.4.15/wal/walpb/record.pb.go#L39-L44
                        复制

                        用一个简单的图表示如下:

                        Type具体有下面几种类型:

                            metadataType int64 = iota + 1
                          entryType
                          stateType
                          crcType
                          snapshotType
                          复制

                          源码:

                            https://github.com/etcd-io/etcd/blob/v3.4.15/wal/wal.go#L39-L43
                            复制


                            根据不同的Type,实际存储的数据格式各不相同,这里就不一一列举各种具体的格式了。直接给出wal文件的总体存储结构:

                            下面的代码片段可以理解wal的存储结构:

                              https://github.com/etcd-io/etcd/blob/v3.4.15/wal/decoder.go#L67-L120
                              https://github.com/etcd-io/etcd/blob/v3.4.15/wal/wal.go#L448-L493
                              复制


                              另外,wal文件名的格式必须为"%016x-%016x.wal",参考代码:

                                https://github.com/etcd-io/etcd/blob/v3.4.15/wal/util.go#L118
                                复制


                                小结

                                本文只是简单总结了snap和wal文件的存储格式,并没有深入讲述每个字段的具体的含义,比如并没有讲述snap中结构体store。


                                etcd在启动的时候,只需要加载最后(最新)一个snap文件,并加载snap之后的所有的wal文件,这里主要是通过index来判断。


                                本文的源码都是基于3.4.15分析的,最新的master上的代码结构做了一定的调整,例如将目录etcdserver放到目录sever下面去了。但总体的代码逻辑是相同的。


                                另外,在${etcd}/member/snap目录下,还有一个db文件,这个是按BoltDB格式存储的。具体可参考我去年总结的几篇文章:


                                --END--

                                文章转载自零君聊软件,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                                评论