内容简介:今天来看看WAL(Write-Ahead Logging)。这是数据库中保证数据持久化的常用技术,即每次真正操作数据之前,先往磁盘上追加一条日志,由于日志 是追加的,也就是顺序写,而不是随机写,所以写入性能还是很高的。这样做的目的是,如果在写入磁盘之前发生崩溃,那么数据肯定是没有写入 的,如果在写入后发生崩溃,那么还是可以从WAL里恢复出来。首先看一下我们先阅读
今天来看看WAL(Write-Ahead Logging)。这是数据库中保证数据持久化的常用技术,即每次真正操作数据之前,先往磁盘上追加一条日志,由于日志 是追加的,也就是顺序写,而不是随机写,所以写入性能还是很高的。这样做的目的是,如果在写入磁盘之前发生崩溃,那么数据肯定是没有写入 的,如果在写入后发生崩溃,那么还是可以从WAL里恢复出来。
首先看一下 wal
里有什么:
$ tree . ├── decoder.go ├── doc.go ├── encoder.go ├── file_pipeline.go ├── file_pipeline_test.go ├── metrics.go ├── record_test.go ├── repair.go ├── repair_test.go ├── util.go ├── wal.go ├── wal_bench_test.go ├── wal_test.go └── walpb ├── record.go ├── record.pb.go └── record.proto 1 directory, 16 files
我们先阅读 doc.go
,可以知道这些东西:
w.Save w.Close
我们看看 Save
的实现:
func (w *WAL) Save(st raftpb.HardState, ents []raftpb.Entry) error { w.mu.Lock() defer w.mu.Unlock() // short cut, do not call sync if raft.IsEmptyHardState(st) && len(ents) == 0 { return nil } mustSync := raft.MustSync(st, w.state, len(ents)) // TODO(xiangli): no more reference operator for i := range ents { if err := w.saveEntry(&ents[i]); err != nil { return err } } if err := w.saveState(&st); err != nil { return err } curOff, err := w.tail().Seek(0, io.SeekCurrent) if err != nil { return err } if curOff < SegmentSizeBytes { if mustSync { return w.sync() } return nil } return w.cut() }
可以看出来, Save
做的事情,就是写入一条记录,然后调用 w.sync
,而 w.sync
做的事情就是:
func (w *WAL) sync() error { if w.encoder != nil { if err := w.encoder.flush(); err != nil { return err } } start := time.Now() err := fileutil.Fdatasync(w.tail().File) took := time.Since(start) if took > warnSyncDuration { if w.lg != nil { w.lg.Warn( "slow fdatasync", zap.Duration("took", took), zap.Duration("expected-duration", warnSyncDuration), ) } else { plog.Warningf("sync duration of %v, expected less than %v", took, warnSyncDuration) } } walFsyncSec.Observe(took.Seconds()) return err
调用了 fileutil.Fdatasync
,而 fileutil.Fdatasync
就是调用了 fsync
这个系统调用保证数据会被写到磁盘。
而快照也是类似的,写入一条记录,然后同步。
func (w *WAL) SaveSnapshot(e walpb.Snapshot) error { b := pbutil.MustMarshal(&e) w.mu.Lock() defer w.mu.Unlock() rec := &walpb.Record{Type: snapshotType, Data: b} if err := w.encoder.encode(rec); err != nil { return err } // update enti only when snapshot is ahead of last index if w.enti < e.Index { w.enti = e.Index } return w.sync() }
WAL更多的是对多个WAL文件进行管理,WAL文件的命名规则是 $seq-$index.wal
。第一个文件会是 0000000000000000-0000000000000000.wal
,
此后,如果文件大小到了64M,就进行一次cut,比如,第一次cut的时候,raft的index是20,那么文件名就会变成 0000000000000001-0000000000000021.wal
。
WAL就看到这。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 【源码阅读】AndPermission源码阅读
- 【源码阅读】Gson源码阅读
- 如何阅读Java源码 ,阅读java的真实体会
- 我的源码阅读之路:redux源码剖析
- JDK源码阅读(六):HashMap源码分析
- 如何阅读源码?
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Computational Geometry
Mark de Berg、Otfried Cheong、Marc van Kreveld、Mark Overmars / Springer / 2008-4-16 / USD 49.95
This well-accepted introduction to computational geometry is a textbook for high-level undergraduate and low-level graduate courses. The focus is on algorithms and hence the book is well suited for st......一起来看看 《Computational Geometry》 这本书的介绍吧!