?!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
后台开发必备知识,不过我不是搞q个的,只是因ؓ很久以前想写这些东西,事情多,拖到现在。写的过E里面发现很多问题,不会全部_最后会带提一提?/span>
注意Q本笔记只是对接口写法做了记录Qƈ没有q行更严格的设计和限Ӟ包括更严密的装Q这里只是学习它实现的原理?/span>
不过有些ideaq是要知道的Q系l定时对~存q行清除q加入满x件的新数据,是根据:讉K旉Q访问次敎ͼ可用~存定wQ分配到的内存){因素决定的Q实际设计其实很多东襉K要考虑?/span>
一、介l:
LRUQLeast Recently UsedQ最q最用,服务器缓存常用算法的一U?/span>
比如说一些系l登录的操作Q不可能每次你访问系l都去调用数据库的东西,如果能划Z些空间来Q比如说500MQ用来缓存这些东西,q样用户讉K的时候先在缓存里找,找不刎ͼ再去讉K数据库,同时把被讉K的内Ҏ到缓存里面(我们可以假设q些东西q会l常被访问)。然而,我们分配用来做缓存(CacheQ的I间肯定是有限的QM可能从数据库ȝ东西全部攑ֈ~存里,所以,当缓存里的内容达C限值的时候,我们p把最用的东西写回数据库,再将新的讉K内容从数据库暂存到缓存里面?/span>
二、数据结构:
最常用的数据结构实现方式是hash_map和Double-Linked ListQhash_map只是Z实现键值key-value对应Q这样就避免了每ơ寻扄定值都要在双线链表里面序查找Q服务器是没办法负担q种低效率的查找Ҏ的?/span>
我们可以为链表节点写一个结构体Q用来定义节点的cdQ然后专门写一个类用来l织~存信息的存䏀—以双链表的形式?/span>
复制代码
template<class K, class T>
struct Node
{
K key;
T data;
Node *next;
Node *prev;
};
复制代码
复制代码
template<class K,class T>
class LRUCache
{
public:
LRUCache(size_t size); //typedef unsigned int size_t
~LRUCache();
void Put(K key,T data);
T Get(K key);
private:
void Attach(Node<K,T>* node);
void Detach(Node<K,T>* node);
private:
hash_map<K,Node<K,T>*> hashmap;
vector<Node<K,T>*> linkedList;
Node<K,T>* head;
Node<K,T>* tail;
Node<K,T>* entries; //temp nodes
};
复制代码
看代码太枯燥Q我M个UML图:
三、主要的两个函数接口Put()和Get()Q?/span>
最基本的,不是存,是取。那修改呢?合ƈ到存里面MQ通过键值key查找一个hash_map对应的valueQ如果value不是NULLQ那么更新value的内容即可。其实服务器~存比较多作的是d写少的东ѝ?/span>
因ؓ代码实在是太枯燥了,所以针对Put函数和Get函数M两张程图:
其中QDetach(node)表示这个节点从双链表中解出Q成Z个独立的节点QAttach(node)表示node插入到头节点head后面Q表C它可能再次被用刎ͼQ这L话,如果自己再设计一个GetFirst()函数Q就能直接获取上ơ的讉Kl果Q这U访问连hash_map都不需要用到?/span>
程讲解Q?/span>
在上qPut函数程图中Q注意第一个判断“node==NULL”,q个node地址是通过hashmap映射而来的:1、如果不是NULLQ说明这个节点已l存在,那么该节点的数据data重写以后加到链表_
2、如果是NULLQ还要进行第二个判断“分配地址的linkedList是不是已l空了”:
2.1、如果空了,说明全部可用地址已经分配完了Q那么,原链表的最后一个节点踢出链表(应写入数据库Q,然后被t出点的hashmap中对应的key-value擦除Q然后再加入新节点,q在hashmap中对应好新节点的key-valueQ?/span>
2.2、如果不I,那么从linkedList中分配个新地址l这个节点,同时linkedList要弹出分配完的地址Q然后再新节点加入链表中,对应好hashmap中的key-value?/span>
要注意,hashmap用来对应key-valueQ这里是方便查找Q而vector变量linkedList也只是在初始化的时候存储了一块连l地址Q用来分配地址Q它们两者都不是用来直接构徏链表的,链表是你自己建立的?/span>
Get()函数比较简单了Q?/span>
四、C++代码实现Q?/span>
代码不是我原创的Q后面给出原文地址Q,不过理清思\之后我自己实C一遍,试的过E实在是各种奇葩和辛苦(因ؓ一个不注意的小地方Q?/span>
代码实现里用Chash_mapQ注意,q个不是C++标准的一部分Q所以你要自己去库文仉找找Q一般来说库文g都是?usr/include下面的了Qcd到这个文件夹Q然后用grep找一下:
cd /usr/include
grep -R "hash_map"
最后你会发现是q个头文Ӟ<ext/hash_map>Q用文本文档打开来看一下,因ؓ是限制了命名I间的,会发现有两个可用的命名空_其中一个是__gnu_cxx?/span>
代码我一开始是用Qt写的Q不q后来发现Qt没法调试Q后面再_Q于是最后用Eclipse完成了调试和试Q下面先l出代码Q?/span>
LRUCache
调试的时候我发现了一个问题:
坑爹了,全部节点的next指针全部都指向自己,q样的话链表长得像什么样子呢Q应该是q样Q?/span>
q个错误到底是怎么来的Q?/span>
我反复地看代码,节点的链入(AttachQ和取出QDetachQ都是没有问题的Q而且Q插入新节点的时候,已经插入q的节点Z么没有了QAttachҎ既然是正的Q那Z么节点的nextZ么会指向自己Q?/span>
l合上面两个问题Q我H然意识刎ͼ那只能是分配地址的时候出现问题了Q?/span>
所以回到构造函数分配地址的部分,我发现在for循环里面Q本应是Q?/span>
linkedList.push_back(entries+i);
q样p序存储分配好的地址?/span>
但我竟然把i写成?Q所以每个地址都成了同一个!吐血的经历?/span>