第六节 Bucket的页分裂、页合并

spill()

  1. // spill writes all the nodes for this bucket to dirty pages.
  2. func (b *Bucket) spill() error {
  3. // Spill all child buckets first.
  4. for name, child := range b.buckets {
  5. // If the child bucket is small enough and it has no child buckets then
  6. // write it inline into the parent bucket's page. Otherwise spill it
  7. // like a normal bucket and make the parent value a pointer to the page.
  8. var value []byte
  9. if child.inlineable() {
  10. child.free()
  11. // 重新更新bucket的val的值
  12. value = child.write()
  13. } else {
  14. if err := child.spill(); err != nil {
  15. return err
  16. }
  17. // Update the child bucket header in this bucket.
  18. // 记录value
  19. value = make([]byte, unsafe.Sizeof(bucket{}))
  20. var bucket = (*bucket)(unsafe.Pointer(&value[0]))
  21. *bucket = *child.bucket
  22. }
  23. // Skip writing the bucket if there are no materialized nodes.
  24. if child.rootNode == nil {
  25. continue
  26. }
  27. // Update parent node.
  28. var c = b.Cursor()
  29. k, _, flags := c.seek([]byte(name))
  30. if !bytes.Equal([]byte(name), k) {
  31. panic(fmt.Sprintf("misplaced bucket header: %x -> %x", []byte(name), k))
  32. }
  33. if flags&bucketLeafFlag == 0 {
  34. panic(fmt.Sprintf("unexpected bucket header flag: %x", flags))
  35. }
  36. // 更新子桶的value
  37. c.node().put([]byte(name), []byte(name), value, 0, bucketLeafFlag)
  38. }
  39. // Ignore if there's not a materialized root node.
  40. if b.rootNode == nil {
  41. return nil
  42. }
  43. // Spill nodes.
  44. if err := b.rootNode.spill(); err != nil {
  45. return err
  46. }
  47. b.rootNode = b.rootNode.root()
  48. // Update the root node for this bucket.
  49. if b.rootNode.pgid >= b.tx.meta.pgid {
  50. panic(fmt.Sprintf("pgid (%d) above high water mark (%d)", b.rootNode.pgid, b.tx.meta.pgid))
  51. }
  52. b.root = b.rootNode.pgid
  53. return nil
  54. }
  55. // inlineable returns true if a bucket is small enough to be written inline
  56. // and if it contains no subbuckets. Otherwise returns false.
  57. func (b *Bucket) inlineable() bool {
  58. var n = b.rootNode
  59. // Bucket must only contain a single leaf node.
  60. if n == nil || !n.isLeaf {
  61. return false
  62. }
  63. // Bucket is not inlineable if it contains subbuckets or if it goes beyond
  64. // our threshold for inline bucket size.
  65. var size = pageHeaderSize
  66. for _, inode := range n.inodes {
  67. size += leafPageElementSize + len(inode.key) + len(inode.value)
  68. if inode.flags&bucketLeafFlag != 0 {
  69. // 有子桶时,不能内联
  70. return false
  71. } else if size > b.maxInlineBucketSize() {
  72. // 如果长度大于1/4页时,就不内联了
  73. return false
  74. }
  75. }
  76. return true
  77. }
  78. // Returns the maximum total size of a bucket to make it a candidate for inlining.
  79. func (b *Bucket) maxInlineBucketSize() int {
  80. return b.tx.db.pageSize / 4
  81. }

rebalance()

  1. // rebalance attempts to balance all nodes.
  2. func (b *Bucket) rebalance() {
  3. for _, n := range b.nodes {
  4. n.rebalance()
  5. }
  6. for _, child := range b.buckets {
  7. child.rebalance()
  8. }
  9. }