lrucache学习总结(参考leveldb)

lrucache学习总结(参考leveldb)背景工程中需要经常访问数据库,在并发数比较大的情况下,对数据库的压力比较大。一种解决方案,可以使用缓存。 即在内存里保留最近使用的数据。缓存对数据敏感性要求高的话,每次可以和数据库的版本对比一下。如果要求性不高,则直接使用缓存的数据即可。cache的淘汰机制,lru=least recently used, 最近最少使用的数据,优先被淘...
摘要由CSDN通过智能技术生成

lrucache学习总结(参考leveldb)

  1. 背景
    • 工程中需要经常访问数据库,在并发数比较大的情况下,对数据库的压力比较大。
    • 一种解决方案,可以使用缓存。 即在内存里保留最近使用的数据。
    • 缓存对数据敏感性要求高的话,每次可以和数据库的版本对比一下。如果要求性不高,则直接使用缓存的数据即可。
    • cache的淘汰机制,lru=least recently used, 最近最少使用的数据,优先被淘汰。
    • 前人已经仿照leveldb中的lrucache, 封装成适合业务使用的模式,下面学习一下这里具体的实现。
  2. 实现思路
    • 主要的思路, 使用双向链表 + 哈希表即可完成。哈希表记录了key->链表位置。 最新的数据放在链表的头部,最旧的数据放在链表的尾部。 发现内存超过阈值时,将链表尾部的数据淘汰即可。
    • 读请求:先读哈希表, 找到链表节点, 将节点数据返回。 将节点数据调整到链表头部。
    • 写请求:先读哈希表, 找到链表节点。 如果旧数据存在,清理旧数据。插入新数据,节点数据放到链表头部,并且更新哈希表数据。
  3. 巧妙的实现
    • 写入的数据是指针。出于通用的考虑,Insert进来数据是指针变量(有些场景业务侧拿到的是指针),当数据被淘汰时。 会调用delete来释放相应的内存。
    • 引用计数。
      • 并发场景下对同一个key的访问,返回的数据是一个指针。 何时释放这个指针是一个需要思考的点。
      • 这里使用引用计数,计算了当前多少个并发持有了这个指针, 只有引用计数为0时,才会真正地delete内存。
      • 引用计数增加的场景: Lookup成功、Insert成功。
      • 引用计数减少的场景: lookup成功之后的业务析构、Insert成功之后的业务析构、Insert成功的时候如果存在旧数据需要清理 、lrucache将当节节点淘汰
    • 拆分两个链表。
      • 如果只有一个链表,如果链表的尾部数据引用计数>1的话,这个节点是不能被淘汰的。 只能每次从尾部往前查找,直到第一个引用计算=1的数据才能被淘汰。效率较低。
      • 于是这里拆成两个链表, used链表+lru链表。 used链表代表正在使用的链表,这里的数据引用计数>1, 这里的数据不可能被淘汰。 当引用计数减少到1的时候, 再放到lru链表,这里的数据可以被淘汰。 随着引用计数的变更, 在两个链表里来回切换。 从lru链表淘汰的时候,再delete清理内存。
    • 使用SharedLruCache分桶,减少竞争。
      • 单个cache内,对哈希表、两个链表的操作需要加锁,这里对所有的key分桶处理,减少竞争。
  4. 其它可优化的点。
    • CacheItem这个wrapper类, 需要限制一下复制构造函数。 考虑以下的场景:
      • Lookup获取cache的数据。
      • 对比了db的数据
      • 发现数据太旧,使用Insert写入新数据。
      • Lookup的wrapper和Insert的wrapper如果使用了相同一个实例,则可能引起内存泄露。
    • 容量问题。
      • 容量的计算,是由外部带入。 没有计算哈希表、链表占用的内存。会导致运行时,占用的内存比实际高。特别是key比较大,而value比较小的场景。 极端情况如果value的数据为空, 可能会导致key无限多!!!
      • 这里可以限制一下参数cap不允许为0, 并且容量在内部上加上key + list的数值。
    • 写入参数使用的指针,业务侧需要new出来。 这里如果数据比较小,频繁申请小块内存,使用tcmalloc替代ptmalloc会性能更优些。 (后续学习)


#pragma once
#include <pthread.h>
#include <string.h>
#include <assert.h>
#include <functional>
#include <mutex>


namespace cachespace
{

template<class KEY, class T>
class Node
{
public:
	Node()
		:pNext(NULL),
		pPrev(NULL),
		pNex
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值