buffer_pool.go 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. package pool
  2. import (
  3. "sync"
  4. )
  5. // pageEntry 缓冲池中的页条目
  6. type pageEntry struct {
  7. data []byte // 页数据副本
  8. dirty bool // 是否被修改(需要写回磁盘)
  9. pageLen int // 页字节长度(用于计算文件偏移)
  10. }
  11. // DirtyPage 脏页快照
  12. type DirtyPage struct {
  13. PageID uint32
  14. Data []byte
  15. PageLen int
  16. }
  17. // BufferPool 页面缓冲池
  18. // 拦截 ReadPage/WritePage,将页缓存在内存中,减少磁盘 I/O
  19. // 写回策略:
  20. // - Commit() → 通过 CollectDirty() 收集脏页,走 WAL 流程写回
  21. // - 脏页超过阈值时自动触发 onThreshold 回调 (由 DB 层执行 Commit)
  22. type BufferPool struct {
  23. pages map[uint32]*pageEntry // pageID → 页条目
  24. mu sync.Mutex
  25. capacity int // 最大缓存页数
  26. dirtyThreshold int // 脏页阈值 (0=不限制)
  27. onThreshold func() // 脏页超阈值回调
  28. dirtyCount int // 当前脏页数 (实时追踪)
  29. diskRead func(pageID uint32, buf []byte) error // 底层磁盘读
  30. diskWrite func(pageID uint32, buf []byte) error // 底层磁盘写
  31. // 锁表机制
  32. writeLocked bool // 写锁标志
  33. writeChan chan struct{} // 用于通知解锁的 channel
  34. // BufferPool 容量控制
  35. capacityWait chan struct{} // 容量满时等待的 channel
  36. }
  37. // NewBufferPool 创建页面缓冲池
  38. // - capacity: 最大缓存页数
  39. // - diskRead/diskWrite: 底层磁盘读写函数
  40. func NewBufferPool(capacity int,
  41. diskRead func(uint32, []byte) error,
  42. diskWrite func(uint32, []byte) error,
  43. ) *BufferPool {
  44. bp := &BufferPool{
  45. pages: make(map[uint32]*pageEntry, capacity),
  46. capacity: capacity,
  47. diskRead: diskRead,
  48. diskWrite: diskWrite,
  49. }
  50. return bp
  51. }
  52. // SetDirtyThreshold 设置脏页阈值和回调
  53. // 当脏页数超过 threshold 时调用 onThreshold (由 DB 层触发 Commit)
  54. func (bp *BufferPool) SetDirtyThreshold(threshold int, onThreshold func()) {
  55. bp.mu.Lock()
  56. defer bp.mu.Unlock()
  57. bp.dirtyThreshold = threshold
  58. bp.onThreshold = onThreshold
  59. }
  60. // LockWrites 锁定写入(Commit/Flush 期间调用)
  61. func (bp *BufferPool) LockWrites() {
  62. bp.mu.Lock()
  63. bp.writeLocked = true
  64. bp.writeChan = make(chan struct{}) // 创建新的 channel
  65. bp.mu.Unlock()
  66. }
  67. // UnlockWrites 解锁写入(Commit/Flush 完成后调用)
  68. func (bp *BufferPool) UnlockWrites() {
  69. bp.mu.Lock()
  70. bp.writeLocked = false
  71. if bp.writeChan != nil {
  72. close(bp.writeChan) // 通知所有等待的 goroutine
  73. bp.writeChan = nil
  74. }
  75. bp.mu.Unlock()
  76. }
  77. // waitForWriteLock 等待写锁释放(在 WritePage 中调用)
  78. func (bp *BufferPool) waitForWriteLock() {
  79. bp.mu.Lock()
  80. if !bp.writeLocked {
  81. bp.mu.Unlock()
  82. return // 未锁定,直接返回
  83. }
  84. // 保存 channel 引用
  85. ch := bp.writeChan
  86. bp.mu.Unlock()
  87. // 阻塞等待解锁(channel 关闭时会返回)
  88. <-ch
  89. }
  90. // CollectDirty 取出所有脏页快照并清除脏标记
  91. // 由 DB.Commit() 调用,配合 WAL 流程统一写回
  92. // 同时清除干净页面缓存,释放内存并确保后续脏页计数准确
  93. func (bp *BufferPool) CollectDirty() []DirtyPage {
  94. // 1. 锁定写入,防止新的写操作
  95. bp.LockWrites()
  96. defer bp.UnlockWrites()
  97. bp.mu.Lock()
  98. var dirty []DirtyPage
  99. for pid, entry := range bp.pages {
  100. if entry.dirty {
  101. data := make([]byte, len(entry.data))
  102. copy(data, entry.data)
  103. dirty = append(dirty, DirtyPage{pid, data, entry.pageLen})
  104. }
  105. // 无论脏净,都从缓存中移除
  106. delete(bp.pages, pid)
  107. }
  108. bp.dirtyCount = 0 // 重置脏页计数
  109. // 2. 通知等待容量的 goroutine
  110. if bp.capacityWait != nil {
  111. close(bp.capacityWait)
  112. bp.capacityWait = nil
  113. }
  114. bp.mu.Unlock()
  115. return dirty
  116. }
  117. // Flush 将所有脏页写入磁盘 (仅在 Close 时使用)
  118. func (bp *BufferPool) Flush() {
  119. dirty := bp.CollectDirty()
  120. for _, item := range dirty {
  121. _ = bp.diskWrite(item.PageID, item.Data)
  122. }
  123. }
  124. // Close 关闭缓冲池 (刷盘后停止)
  125. func (bp *BufferPool) Close() {
  126. bp.Flush()
  127. }
  128. // UpdatePage 更新缓存页 (WAL 提交后调用, 保持缓存与磁盘一致)
  129. func (bp *BufferPool) UpdatePage(pageID uint32, data []byte) {
  130. bp.mu.Lock()
  131. defer bp.mu.Unlock()
  132. cached := make([]byte, len(data))
  133. copy(cached, data)
  134. bp.pages[pageID] = &pageEntry{data: cached, dirty: false, pageLen: len(data)}
  135. }
  136. // ClearAll 清除所有脏标记 (WAL 提交后调用)
  137. // 保留干净页在缓存中, 避免下次 WritePage 重新缓存导致 BufferPool 反复膨胀
  138. func (bp *BufferPool) ClearAll() {
  139. bp.mu.Lock()
  140. defer bp.mu.Unlock()
  141. for _, entry := range bp.pages {
  142. entry.dirty = false
  143. }
  144. bp.dirtyCount = 0
  145. }
  146. // ReadPage 读页 — 先查缓存,未命中则读磁盘 (不缓存, 避免 BufferPool 膨胀)
  147. func (bp *BufferPool) ReadPage(pageID uint32, buf []byte) error {
  148. bp.mu.Lock()
  149. if entry, ok := bp.pages[pageID]; ok {
  150. copy(buf, entry.data)
  151. bp.mu.Unlock()
  152. return nil
  153. }
  154. bp.mu.Unlock()
  155. // 缓存未命中 → 直接从磁盘读取, 不写入缓存
  156. return bp.diskRead(pageID, buf)
  157. }
  158. // WritePage 写页 — 写入缓存并标记脏,不立即落盘
  159. func (bp *BufferPool) WritePage(pageID uint32, buf []byte) error {
  160. // 1. 等待写锁释放(Commit/Flush 期间阻塞)
  161. bp.waitForWriteLock()
  162. cached := make([]byte, len(buf))
  163. copy(cached, buf)
  164. var triggerThreshold bool
  165. bp.mu.Lock()
  166. // 2. 检查 BufferPool 容量,如果满了则等待
  167. for bp.capacity > 0 && len(bp.pages) >= bp.capacity {
  168. // 创建等待 channel
  169. if bp.capacityWait == nil {
  170. bp.capacityWait = make(chan struct{})
  171. }
  172. waitChan := bp.capacityWait
  173. bp.mu.Unlock()
  174. // 阻塞等待空间释放
  175. <-waitChan
  176. // 重新获取锁,继续检查
  177. bp.mu.Lock()
  178. }
  179. existing, exists := bp.pages[pageID]
  180. if exists && existing.dirty {
  181. // 已是脏页, 直接覆盖, 不增加脏页计数
  182. existing.data = cached
  183. } else {
  184. // 新页或干净页, 增加脏页计数
  185. bp.pages[pageID] = &pageEntry{data: cached, dirty: true, pageLen: len(buf)}
  186. bp.dirtyCount++
  187. }
  188. // 3. 检查是否超过阈值 (脏页或总页面数)
  189. totalPages := len(bp.pages)
  190. if bp.dirtyThreshold > 0 && bp.onThreshold != nil {
  191. if bp.dirtyCount >= bp.dirtyThreshold || totalPages >= bp.dirtyThreshold {
  192. triggerThreshold = true
  193. }
  194. }
  195. bp.mu.Unlock()
  196. // 4. 在锁外触发回调, 避免死锁
  197. if triggerThreshold {
  198. bp.onThreshold()
  199. }
  200. return nil
  201. }
  202. // Len 当前缓存页数 (调试用)
  203. func (bp *BufferPool) Len() int {
  204. bp.mu.Lock()
  205. defer bp.mu.Unlock()
  206. return len(bp.pages)
  207. }
  208. // DirtyCount 脏页数 (调试用)
  209. func (bp *BufferPool) DirtyCount() int {
  210. bp.mu.Lock()
  211. defer bp.mu.Unlock()
  212. return bp.dirtyCount
  213. }
  214. // SetCapacity 设置 BufferPool 容量(页数)
  215. func (bp *BufferPool) SetCapacity(capacity int) {
  216. bp.mu.Lock()
  217. bp.capacity = capacity
  218. bp.mu.Unlock()
  219. }