关于用引用计数法写缓存的一点看法

最近看了一个用go写的数据库NYADB2,
github地址:https://github.com/qw4990/NYADB2。

看到作者关于存储模块的缓存用了引用计数法来写,而不是常用的LRU算法。
他在缓存部分用了二级缓存。

  • 第一级是page缓存,在内存维持page,实质是byte[],每个page都有一个引用数p,每次引用page,p++,释放page,p--。一旦p==0,就把page写回磁盘(page脏时),在内存中释放page。

  • 第二级缓存是dataItem缓存,这里的dataItem实质上是切片(切片是go的一种内置结构)对page中一段字节数组的引用,并没有实质上的缓存。这里同样是引用计数法,只不过,当p==0时,只是把切片释放了而已。

  • 在这里作者用了一个很漂亮的写法,独立写了一个引用计数法的缓存模块,把缓存的对象和use缓存和release缓存的接口留了出来,所以这里的page缓存和dataItem缓存都是用这个缓存模块做的。

整个使用缓存的流程是

1.查找dataItem(简写为d)-->
2.dataItem缓存存在d,直接返回d,不存在d时,在dataItem缓存中创建d缓存-->
3.创建d缓存会从其所在的page(简写为p)中创建切片,若page缓存中不存在p,创建p缓存
4.创建p缓存,即从磁盘中写出相应的page
  • 一个缓存要能维持在内存中,那么它的引用数就必须一直满足>=1,且不能中断,即是每时每刻都有对缓存的引用,这就意味着,如果对数据库的访问频率不够高,那么引用计数法写的缓存是无效的。可想而知,用引用计数法写的缓存一般比用LRU写的缓存,缓存数不够。
    所以在p--的时候可以考虑异步延迟,延迟时间由当前缓存数量决定。

  • 其次在这里我认为用引用计数法写page缓存并不是一个好选择,因为假设有10%的数据(访问频率够高)一直被缓存,就意味着,那10%数据对应的page也会一直维持在内存。如果这10%的数据分步在所有的page中,那么所有的page都会维持在缓存,那所谓的分页管理就没有意义了。

  • 用引用计数法写可以很容易把锁表和缓存结合,在实现诸如b-link-tree之类需要节点加读写锁的索引结构时很方便。

你可能感兴趣的:(关于用引用计数法写缓存的一点看法)