第七节 db.allocate()和db.grow()分析

  1. // allocate returns a contiguous block of memory starting at a given page.
  2. func (db *DB) allocate(count int) (*page, error) {
  3. // Allocate a temporary buffer for the page.
  4. var buf []byte
  5. if count == 1 {
  6. buf = db.pagePool.Get().([]byte)
  7. } else {
  8. buf = make([]byte, count*db.pageSize)
  9. }
  10. // 转成*page
  11. p := (*page)(unsafe.Pointer(&buf[0]))
  12. p.overflow = uint32(count - 1)
  13. // Use pages from the freelist if they are available.
  14. // 先从空闲列表中找
  15. if p.id = db.freelist.allocate(count); p.id != 0 {
  16. return p, nil
  17. }
  18. // 找不到的话,就按照事务的pgid来分配
  19. // 表示需要从文件内部扩大
  20. // Resize mmap() if we're at the end.
  21. p.id = db.rwtx.meta.pgid
  22. // 因此需要判断是否目前所有的页数已经大于了mmap映射出来的空间
  23. // 这儿计算的页面总数是从当前的id后还要计算count+1个
  24. var minsz = int((p.id+pgid(count))+1) * db.pageSize
  25. if minsz >= db.datasz {
  26. if err := db.mmap(minsz); err != nil {
  27. return nil, fmt.Errorf("mmap allocate error: %s", err)
  28. }
  29. }
  30. // Move the page id high water mark.
  31. // 如果不是从freelist中找到的空间的话,更新meta的id,也就意味着是从文件中新扩展的页
  32. db.rwtx.meta.pgid += pgid(count)
  33. return p, nil
  34. }
  35. // grow grows the size of the database to the given sz.
  36. func (db *DB) grow(sz int) error {
  37. // Ignore if the new size is less than available file size.
  38. if sz <= db.filesz {
  39. return nil
  40. }
  41. // 满足这个条件sz>filesz
  42. // If the data is smaller than the alloc size then only allocate what's needed.
  43. // Once it goes over the allocation size then allocate in chunks.
  44. if db.datasz < db.AllocSize {
  45. sz = db.datasz
  46. } else {
  47. sz += db.AllocSize
  48. }
  49. // Truncate and fsync to ensure file size metadata is flushed.
  50. // https://github.com/boltdb/bolt/issues/284
  51. if !db.NoGrowSync && !db.readOnly {
  52. if runtime.GOOS != "windows" {
  53. if err := db.file.Truncate(int64(sz)); err != nil {
  54. return fmt.Errorf("file resize error: %s", err)
  55. }
  56. }
  57. if err := db.file.Sync(); err != nil {
  58. return fmt.Errorf("file sync error: %s", err)
  59. }
  60. }
  61. db.filesz = sz
  62. return nil
  63. }