第三节 Open()实现分析

Open()方法主要用来创建一个boltdb的DB对象,底层会执行新建或者打开存储数据的文件,当指定的文件不存在时, boltdb就会新建一个数据文件。否则的话,就直接加载指定的数据库文件内容。

值的注意是,boltdb会根据Open时,options传递的参数来判断到底加互斥锁还是共享锁。

新建时: 会调用init()方法,内部主要是新建一个文件,然后第0页、第1页写入元数据信息;第2页写入freelist信息;第3页写入bucket leaf信息。并最终刷盘。

加载时: 会读取第0页内容,也就是元信息。然后对其进行校验和校验,当校验通过后获取pageSize。否则的话,读取操作系统默认的pagesize(一般4k)

上述操作完成后,会通过mmap来映射数据。最后再根据磁盘页中的freelist数据初始化db的freelist字段。

  1. // Open creates and opens a database at the given path.
  2. // If the file does not exist then it will be created automatically.
  3. // Passing in nil options will cause Bolt to open the database with the default options.
  4. func Open(path string, mode os.FileMode, options *Options) (*DB, error) {
  5. var db = &DB{opened: true}
  6. // Set default options if no options are provided.
  7. if options == nil {
  8. options = DefaultOptions
  9. }
  10. db.NoGrowSync = options.NoGrowSync
  11. db.MmapFlags = options.MmapFlags
  12. // Set default values for later DB operations.
  13. db.MaxBatchSize = DefaultMaxBatchSize
  14. db.MaxBatchDelay = DefaultMaxBatchDelay
  15. db.AllocSize = DefaultAllocSize
  16. flag := os.O_RDWR
  17. if options.ReadOnly {
  18. flag = os.O_RDONLY
  19. db.readOnly = true
  20. }
  21. // Open data file and separate sync handler for metadata writes.
  22. db.path = path
  23. var err error
  24. // 打开db文件
  25. if db.file, err = os.OpenFile(db.path, flag|os.O_CREATE, mode); err != nil {
  26. _ = db.close()
  27. return nil, err
  28. }
  29. // Lock file so that other processes using Bolt in read-write mode cannot
  30. // use the database at the same time. This would cause corruption since
  31. // the two processes would write meta pages and free pages separately.
  32. // The database file is locked exclusively (only one process can grab the lock)
  33. // if !options.ReadOnly.
  34. // The database file is locked using the shared lock (more than one process may
  35. // hold a lock at the same time) otherwise (options.ReadOnly is set).
  36. // 只读加共享锁、否则加互斥锁
  37. if err := flock(db, mode, !db.readOnly, options.Timeout); err != nil {
  38. _ = db.close()
  39. return nil, err
  40. }
  41. // Default values for test hooks
  42. db.ops.writeAt = db.file.WriteAt
  43. // Initialize the database if it doesn't exist.
  44. if info, err := db.file.Stat(); err != nil {
  45. return nil, err
  46. } else if info.Size() == 0 {
  47. // Initialize new files with meta pages.
  48. // 初始化新db文件
  49. if err := db.init(); err != nil {
  50. return nil, err
  51. }
  52. } else {
  53. // 不是新文件,读取第一页元数据
  54. // Read the first meta page to determine the page size.
  55. // 2^12,正好是4k
  56. var buf [0x1000]byte
  57. if _, err := db.file.ReadAt(buf[:], 0); err == nil {
  58. // 仅仅是读取了pageSize
  59. m := db.pageInBuffer(buf[:], 0).meta()
  60. if err := m.validate(); err != nil {
  61. // If we can't read the page size, we can assume it's the same
  62. // as the OS -- since that's how the page size was chosen in the
  63. // first place.
  64. //
  65. // If the first page is invalid and this OS uses a different
  66. // page size than what the database was created with then we
  67. // are out of luck and cannot access the database.
  68. db.pageSize = os.Getpagesize()
  69. } else {
  70. db.pageSize = int(m.pageSize)
  71. }
  72. }
  73. }
  74. // Initialize page pool.
  75. db.pagePool = sync.Pool{
  76. New: func() interface{} {
  77. // 4k
  78. return make([]byte, db.pageSize)
  79. },
  80. }
  81. // Memory map the data file.
  82. // mmap映射db文件数据到内存
  83. if err := db.mmap(options.InitialMmapSize); err != nil {
  84. _ = db.close()
  85. return nil, err
  86. }
  87. // Read in the freelist.
  88. db.freelist = newFreelist()
  89. // db.meta().freelist=2
  90. // 读第二页的数据
  91. // 然后建立起freelist中
  92. db.freelist.read(db.page(db.meta().freelist))
  93. // Mark the database as opened and return.
  94. return db, nil
  95. }
  96. // init creates a new database file and initializes its meta pages.
  97. func (db *DB) init() error {
  98. // Set the page size to the OS page size.
  99. db.pageSize = os.Getpagesize()
  100. // Create two meta pages on a buffer.
  101. buf := make([]byte, db.pageSize*4)
  102. for i := 0; i < 2; i++ {
  103. p := db.pageInBuffer(buf[:], pgid(i))
  104. p.id = pgid(i)
  105. // 第0页和第1页存放元数据
  106. p.flags = metaPageFlag
  107. // Initialize the meta page.
  108. m := p.meta()
  109. m.magic = magic
  110. m.version = version
  111. m.pageSize = uint32(db.pageSize)
  112. m.freelist = 2
  113. m.root = bucket{root: 3}
  114. m.pgid = 4
  115. m.txid = txid(i)
  116. m.checksum = m.sum64()
  117. }
  118. // Write an empty freelist at page 3.
  119. // 拿到第2页存放freelist
  120. p := db.pageInBuffer(buf[:], pgid(2))
  121. p.id = pgid(2)
  122. p.flags = freelistPageFlag
  123. p.count = 0
  124. // 第三块存放叶子page
  125. // Write an empty leaf page at page 4.
  126. p = db.pageInBuffer(buf[:], pgid(3))
  127. p.id = pgid(3)
  128. p.flags = leafPageFlag
  129. p.count = 0
  130. // Write the buffer to our data file.
  131. // 写入4页的数据
  132. if _, err := db.ops.writeAt(buf, 0); err != nil {
  133. return err
  134. }
  135. // 刷盘
  136. if err := fdatasync(db); err != nil {
  137. return err
  138. }
  139. return nil
  140. }
  141. // page retrieves a page reference from the mmap based on the current page size.
  142. func (db *DB) page(id pgid) *page {
  143. pos := id * pgid(db.pageSize)
  144. return (*page)(unsafe.Pointer(&db.data[pos]))
  145. }
  146. // pageInBuffer retrieves a page reference from a given byte array based on the current page size.
  147. func (db *DB) pageInBuffer(b []byte, id pgid) *page {
  148. return (*page)(unsafe.Pointer(&b[id*pgid(db.pageSize)]))
  149. }
  150. // mmap opens the underlying memory-mapped file and initializes the meta references.
  151. // minsz is the minimum size that the new mmap can be.
  152. func (db *DB) mmap(minsz int) error {
  153. db.mmaplock.Lock()
  154. defer db.mmaplock.Unlock()
  155. info, err := db.file.Stat()
  156. if err != nil {
  157. return fmt.Errorf("mmap stat error: %s", err)
  158. } else if int(info.Size()) < db.pageSize*2 {
  159. return fmt.Errorf("file size too small")
  160. }
  161. // Ensure the size is at least the minimum size.
  162. var size = int(info.Size())
  163. if size < minsz {
  164. size = minsz
  165. }
  166. size, err = db.mmapSize(size)
  167. if err != nil {
  168. return err
  169. }
  170. // Dereference all mmap references before unmapping.
  171. if db.rwtx != nil {
  172. db.rwtx.root.dereference()
  173. }
  174. // Unmap existing data before continuing.
  175. if err := db.munmap(); err != nil {
  176. return err
  177. }
  178. // Memory-map the data file as a byte slice.
  179. if err := mmap(db, size); err != nil {
  180. return err
  181. }
  182. // Save references to the meta pages.
  183. // 获取元数据信息
  184. db.meta0 = db.page(0).meta()
  185. db.meta1 = db.page(1).meta()
  186. // Validate the meta pages. We only return an error if both meta pages fail
  187. // validation, since meta0 failing validation means that it wasn't saved
  188. // properly -- but we can recover using meta1. And vice-versa.
  189. err0 := db.meta0.validate()
  190. err1 := db.meta1.validate()
  191. if err0 != nil && err1 != nil {
  192. return err0
  193. }
  194. return nil
  195. }
  196. // mmapSize determines the appropriate size for the mmap given the current size
  197. // of the database. The minimum size is 32KB and doubles until it reaches 1GB.
  198. // Returns an error if the new mmap size is greater than the max allowed.
  199. func (db *DB) mmapSize(size int) (int, error) {
  200. // Double the size from 32KB until 1GB.
  201. for i := uint(15); i <= 30; i++ {
  202. if size <= 1<<i {
  203. return 1 << i, nil
  204. }
  205. }
  206. // Verify the requested size is not above the maximum allowed.
  207. if size > maxMapSize {
  208. return 0, fmt.Errorf("mmap too large")
  209. }
  210. // If larger than 1GB then grow by 1GB at a time.
  211. sz := int64(size)
  212. if remainder := sz % int64(maxMmapStep); remainder > 0 {
  213. sz += int64(maxMmapStep) - remainder
  214. }
  215. // Ensure that the mmap size is a multiple of the page size.
  216. // This should always be true since we're incrementing in MBs.
  217. pageSize := int64(db.pageSize)
  218. if (sz % pageSize) != 0 {
  219. sz = ((sz / pageSize) + 1) * pageSize
  220. }
  221. // If we've exceeded the max size then only grow up to the max size.
  222. if sz > maxMapSize {
  223. sz = maxMapSize
  224. }
  225. return int(sz), nil
  226. }