最近这两天闲了一些,准备好好学一下golang。

学习一门语言最好的办法就是通过项目学习,所以我就找了个评价比较高代码不太大的项目 boltdb,开始学习。

Open

为了方便理解,我们假设执行Open是这一句话

1
db, err := bolt.Open("my.db", 0600, &bolt.Options{Timeout: 1 * time.Second})

系统首先创建一个数据库的结构体

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
type DB struct {
	StrictMode bool
	NoSync bool
	NoGrowSync bool
	MmapFlags int
	MaxBatchSize int
	MaxBatchDelay time.Duration
	AllocSize int
	path     string
	file     *os.File
	lockfile *os.File 
	dataref  []byte   
	data     *[maxMapSize]byte
	datasz   int
	filesz   int 
	meta0    *meta
	meta1    *meta
	pageSize int
	opened   bool
	rwtx     *Tx
	txs      []*Tx
	freelist *freelist
	stats    Stats
	pagePool sync.Pool
	batchMu sync.Mutex
	batch   *batch
	rwlock   sync.Mutex   
	metalock sync.Mutex  
	mmaplock sync.RWMutex 
	statlock sync.RWMutex
	ops struct {
		writeAt func(b []byte, off int64) (n int, err error)
	}
	readOnly bool
}

实例化

这个结构体变量有点多,我们按照程序顺序解释我们遇到的程序。 首先默认设置这个db已经Open,然后如果没有配置的话,就设置不超时,我们设置的超时时间为1s,也就是如果1s没有操作的话,就结束了。 其他的我们都没配置,都是变量的默认值。 首先在数据库变大的时候先不同步。 Options里面的MmapFlags是为了大量读取数据所使用的,然而默认配置并没有设置,也就是说这个默认是关闭的。 除此之外,还配置了默认执行Batch的大小,延时还有分配的大小。 最后根据是否配置数据库为只读模式,配置接下来的操作方式flagos.O_RDWR或者os.O_RDONLY。我们没有指定ReadOnly,所以数据库是读写模式。 到此,系统默认数据库已经配置完毕。

创建

接下来的系统创建就很简单了,直接根据我们以上的配置和文件路径创建文件即可

1
db.file, err = os.OpenFile(db.path, flag|os.O_CREATE, mode)

然后系统为了防止一个数据库同时被两个进程使用,为文件创建了一个锁,保证在读写模式的时候,只会有一个进程在调用。 然后我们的数据库支持按指定便宜存储,所以定了了一个writeat函数。 最后我们要为程序指定一个PageSize,这样是为了高效的从系统读取写入数据。 这块的逻辑也很简单,整体顺序就是看下文件是否是首次创建的,如果不是的话就读去文件的meta数据,读取出来设置的meta数据,有错误的话就用系统的默认PageSize。 如果是第一次创建,就对本文件进行初始化操作。具体的初始化操作涉及系统的操作,我们留到以后分析。 然后就可以配置数据库的分页池,把上数据库文件直接映射到内存上,然后读取freelist(还不是太理解含义) 到目前为止,数据库实例化成功。