当前位置: 首页 > 工具软件 > BoltDB > 使用案例 >

Boltdb源码分析——bolt.Open

岳宣
2023-12-01

Bolt是受LMDB、hyc_symas激发的纯key/value存储。Since Bolt is meant to be used as such a low-level piece of functionality, simplicity is key. The API will be small and only focus on getting values and setting values. That’s it.
[hyc_symas]: https://twitter.com/hyc_symas
[lmdb]: http://symas.com/mdb/

Getting Started

Installing

安装Boltdb数据库$ go get github.com/boltdb/bolt/...
This will retrieve the library and install the bolt command line utility into your $GOBIN path.

Opening a database

Bolt中的顶级对象为DB,它代表磁盘上的一个单独的文件,代表数据的consistent snapshot。To open your database, simply use the bolt.Open() function:

package main
import (
	"log"
	"github.com/boltdb/bolt"
)
func main() {
	// Open the my.db data file in your current directory.
	// It will be created if it doesn't exist.
	db, err := bolt.Open("my.db", 0600, nil)
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()
	...
}

Open使用给定的路径创建并打开一个数据库。Options配置为nil,Bolt会使用默认选项打开数据库。db.go 150 func Open(path string, mode os.FileMode, options *Options) (*DB, error)

  • 如果!options.ReadOnly(read-write模式),数据库文件是独占锁定的(仅仅一个process能够grab lock)
  • 如果options.ReadOnly,数据库文件是共享锁定的(多个进程可以同时获得该锁)
db, err := bolt.Open("my.db", 0600, &bolt.Options{Timeout: 1 * time.Second})

Please note that Bolt obtains a file lock on the data file so multiple processes cannot open the same database at the same time. Opening an already open Bolt database will cause it to hang until the other process closes it. To prevent an indefinite wait you can pass a timeout option to the Open() function:

func Open(path string, mode os.FileMode, options *Options) (*DB, error) {
	var db = &DB{opened: true}   // db是指向DB结构体的指针
	// Set default options if no options are provided.
	if options == nil { options = DefaultOptions }
	db.NoGrowSync = options.NoGrowSync
	db.MmapFlags = options.MmapFlags
	// Set default values for later DB operations.
	db.MaxBatchSize = DefaultMaxBatchSize
	db.MaxBatchDelay = DefaultMaxBatchDelay
	db.AllocSize = DefaultAllocSize

	flag := os.O_RDWR      // 如果没指定options.ReadOnly,则文件打开方式为os.O_RDWR
	if options.ReadOnly {  // 如果指定options.ReadOnly,则文件打开方式为os.O_RDONLY
		flag = os.O_RDONLY
		db.readOnly = true
	}
	// Open data file and separate sync handler for metadata writes.
	db.path = path
	var err error
	if db.file, err = os.OpenFile(db.path, flag|os.O_CREATE, mode); err != nil {
		_ = db.close()
		return nil, err
	}
    // 锁定文件,这样使用Bolt read-write模式下的其他进程不能同时使用数据库。
	// Lock file so that other processes using Bolt in read-write mode cannot
	// use the database  at the same time. This would cause corruption since
	// the two processes would write meta pages and free pages separately.
	// The database file is locked exclusively (only one process can grab the lock)
	// if !options.ReadOnly.
	// 如果!options.ReadOnly,数据库文件是独占锁定的(仅仅一个process能够grab lock)
	// 如果options.ReadOnly,数据库文件是共享锁定的(多个进程可以同时获得该锁)
	// The database file is locked using the shared lock (more than one process may
	// hold a lock at the same time) otherwise (options.ReadOnly is set).
	if err := flock(db, mode, !db.readOnly, options.Timeout); err != nil {
		_ = db.close()
		return nil, err
	}
	// Default values for test hooks
	db.ops.writeAt = db.file.WriteAt

	// Initialize the database if it doesn't exist.
	if info, err := db.file.Stat(); err != nil {
		return nil, err
	} else if info.Size() == 0 {
		// Initialize new files with meta pages.  使用meta page初始化新文件
		if err := db.init(); err != nil {
			return nil, err
		}
	} else { // Read the first meta page to determine the page size.  读取meta page取出页大小
		var buf [0x1000]byte
		if _, err := db.file.ReadAt(buf[:], 0); err == nil {
			m := db.pageInBuffer(buf[:], 0).meta()
			if err := m.validate(); err != nil {
				// If we can't read the page size, we can assume it's the same
				// as the OS -- since that's how the page size was chosen in the
				// first place.
				// If the first page is invalid and this OS uses a different
				// page size than what the database was created with then we
				// are out of luck and cannot access the database.
				db.pageSize = os.Getpagesize()
			} else {
				db.pageSize = int(m.pageSize)
			}
		}
	}

	// Initialize page pool.
	db.pagePool = sync.Pool{
		New: func() interface{} {
			return make([]byte, db.pageSize)
		},
	}

	// Memory map the data file. 映射数据文件
	if err := db.mmap(options.InitialMmapSize); err != nil {
		_ = db.close()
		return nil, err
	}
	// Read in the freelist. 读入Freelist
	db.freelist = newFreelist()
	db.freelist.read(db.page(db.meta().freelist))	
	return db, nil  // Mark the database as opened and return.
}
 类似资料: