参考leveldb相关部分写的c++LRU缓存淘汰组件

leveldb源码分析:https://github.com/chloro-pn/...
leveldb经常需要在各个sstable文件中进行搜索操作,其使用LRU缓存机制将最近访问的sstable文件的部分信息缓存在内存中便于查找。
LRU缓存机制的实现一般采取hash+双向链表实现,这样可以使查找,更新等操作都在O(1)时间复杂度下完成。另外还有一个需要注意的点:如何处理资源对象的生命周期?
资源对象的生命周期应该被LRU缓存组件和所有持有该资源的用户共同决定。可能LRU缓存和用户同时都持有该对象,也可能LRU缓存中已经不再持有而用户还持有该资源,也可能没有用户持有而LRU缓存中还持有该资源。leveldb使用in_use链表+lru_链表+hash表解决这个问题。
当有用户持有资源时,资源对象被放置在in_use链表中,且ref>1(因为in_use链表本身占有一个引用)。当所有用户通过Release操作释放对象之后资源对象的ref变为1,此时会被LRU缓存组件从in_use链表中删除然后放到lru_链表头部。
这种方法相当于,被用户持有的资源对象不会被LRU缓存组件主动释放,因为根本不对in_use链表执行释放操作,只有没有用户持有的资源对象才有可能被释放,这一点其实比较好理解,因为可以认为被用户当前访问的资源是热数据,具有较高的被LRU缓存保留的优先级。但是这种方法有另外两个问题:

  • 其一需要通过用户手动调用Release函数释放,较容易造成缓存对象永久停留在内存中,是某种意义上的内存泄漏与资源泄漏。
  • 其二可能造成缓存中的资源存储远远大于实际容量限制,试想短时间内大量用户向LRU缓存申请并持有资源对象,这些资源对象都被放置在in_use链表中,而LRU只根据lru_链表中存储的资源量判断释放处理,故超过容量限制的资源被存储在hash表中。

根据以上情况我这两天写了一个LRU缓存组件,虽然仍旧采用侵入式的计数设计,但不需要资源对象本身含有计数机制,而是通过代理类实现,因此不需要手动release。另外与leveldb不同,采用lru_链表+lru_hash表+in_use_hash表尝试解决第二个问题,目前看来还不错。等待后续测试与完善,提供线程安全保证。
url:https://github.com/chloro-pn/...

你可能感兴趣的:(c++,lrucache)