【系统设计】LRU缓存

本文阅读目录

一、缓存是什么算法

二、什么是LRU缓存编程

三、LRU缓存设计思路缓存

四、LRU缓存接口服务器

缓存是什么

其实缓存的思想在生活中到处可见,好比说咱们的书架上摆满了各类书籍,可是我最近想集中精力攻克数据结构这门课,因次为了方便,我将与数据结构有关的书先从书架取下来,放在咱们的书桌上,这样咱们取书的时候就快不少了,伸手可得,而不用跑到书架旁取书了,在这个例子里,书桌所扮演的就是缓存的角色。网络

在计算机中,咱们知道,访问磁盘的效率比访问内存的效率低得多,低多少了,大概是10W级别吧,以下图所示。
【系统设计】LRU缓存
咱们知道有个很著名的二八定律。其实计算机世界中也有不少状况遵照这个定律,好比说网站访问的特色:80% 的业务访问集中在20% 的数据上。那么很天然就想到:既然大部分业务访问集中在一小部分数据上,那么若是经过设计合理的数据结构把这一小部分数据存放在内存中,是否是极大的提升了系统的性能呢,这就是缓存。数据结构

从上面能够看出,不管是生活中仍是计算机世界中的缓存,主要有如下两个特色:并发

  • 提升系统效率,ide

  • 容量小于外部存储容量,如书桌容量小于书架容量,内存容量小于硬盘容量

什么是LRU缓存

因为内存是有限的,所以缓存总有满的时候,那么当缓存满了的时候,这时候又有新的数据须要加入到缓存中时,咱们该怎么办了?没什么别的办法,只有从缓存中删除旧数据为新数据腾出空间,那么究竟删除原来缓存的哪些数据了?这就涉及到缓存的替换策略,LRU就是一种缓存策略。函数

LRU,Least Recently Used的简写,翻译过来就是“最近最少使用”。该算法依据于程序的局部性原理, 其淘汰旧数据的策略是,距离当前最久没有被访问过的数据应该被淘汰。性能

举个例子:如今有一个能存放5个数据的缓存,刚开始中缓存中存放了3个数据A->B->C,以下所示:
【系统设计】LRU缓存
接着又在缓存中插入2个数据D->E,此时缓存已经满了。以下:
【系统设计】LRU缓存
好了,如今咱们来访问数据A,如今A成为了最近访问的数据,B成了最久没有访问过的数据:
【系统设计】LRU缓存
如今咱们又有一个数据F想要插入到缓存中,因为缓存已经满了,须要从缓存中淘汰旧数据,此时缓存中的数据B是距离当前时间最久没有被访问过的数据,根据LRU算法,咱们会淘汰掉数据B:将F插入到当前B的位置。
【系统设计】LRU缓存
此时数据C成为了距离当前时间最久没有被访问过的数据,所以若是再来一个数据的话,就应该插入到C当前的位置,依次类推。

LRU缓存设计

理解LRU缓存的思想其实挺简单,可是要本身动手设计一个LRU缓存就并不那么简单了,这节主要讲讲LRU缓存的设计思路。

首先从前面的分析能够看出,LRU缓存主要的操做是插入删除以及查找,所以咱们能够考虑用双链表来维护缓存,该链表将缓存数据按访问时间重新到旧排列起来。

若是咱们只是单存将缓存中的数据维护在一个双向链表中, 那么当咱们须要从缓存中查找数据时,须要遍历链表,其时间复杂度为O(n)。 这样的设计, 是一种比较低效率的作法。 所以, 咱们除了将数据维护在双向链表中, 咱们同时还将数据维护在一个哈希表中。 哈希表访问数据的时间复杂度为O(1)。

依据上述设计, 一个LRU缓存包含了一个双向链表和一个哈希表, 双向链表以及哈希表的一个节点表明缓存中的一个缓存单元, 所以咱们能够这样定义咱们缓存单元的数据结构:

//LRU缓存的缓存单元 
typedef struct cacheEntryS
{  
    char key;   //数据的key 
    char data;  // 数据的data

    struct cacheEntryS *hashListPrev; //指向哈希链表的前一个元素
    struct cacheEntryS *hashListNext; //指向哈希链表的后一个元素

    struct cacheEntryS *lruListPrev;  //指向链表的前一个元素
    struct cacheEntryS *lruListNext;  //指向链表后一个元素
}cacheEntryS;

LRU缓存的数据结构以下:

//定义LRU缓存
typedef struct LRUCacheS
{  
    int cacheCapacity;  //缓存的容量
    cacheEntryS **hashMap;  //缓存的哈希表

    cacheEntryS *lruListHead;//缓存的双向链表表头
    cacheEntryS *lruListTail;//缓存的双向链表表尾
    int lruListSize;    //缓存的双向链表节点个数
}LRUCacheS;

若是一大坨代码看的比较晕,那么对着下面的结构关系图应该能理解的比较透彻,注意下图中没有指向的指针都是NULL:
【系统设计】LRU缓存

LRU缓存接口设计

经过前面的分析不难看出,对一个LRU缓存的最重要的操做无外乎是建立、销毁、插入数据、查找数据。下面给出这几个接口,至于具体实现,这里先不分析。后面有机会再一一分析。

/*********************************************
函数名:LRUCacheCreate
功能:建立LRU缓存
输入参数:capacity,缓存的数据容量
输出参数:lruCache,指向新建缓存的指针
针返回值:0---成功     -1---失败

*********************************************/
int LRUCacheCreate(int capacity, void **lruCache);
/*********************************************
函数名:LRUCacheDestory
功能:销毁LRU缓存
输入参数:lruCache指向新建缓存的指针
输出参数:无
针返回值:0---成功     -1---失败
*********************************************/
int LRUCacheDestory(void *lruCache);
/*********************************************
函数名:LRUCacheSet
功能:将数据插入到LRU缓存中
输入参数:key:数据索引   data:数据内容
输出参数:无
针返回值:0---成功     -1---失败
*********************************************/
int LRUCacheSet(void *lruCache, char key, char data);
/*********************************************
函数名:LRUCacheGet
功能:将数据插入到LRU缓存中
输入参数:key:数据索引
输出参数:无
针返回值:缓存中存在key对应的data,返回
        缓存中不存在key对应的data,返回'\0'
*********************************************/
int LRUCacheGet(void *lruCache, char key);

推荐阅读:

【福利】本身搜集的网上精品课程视频分享(上)
【协议森林】邮差与邮局 (网络协议概观)

【数据结构与算法】 通俗易懂讲解 位排序
【C++札记】C++11并发编程(一)开启线程之旅
【C++札记】C/C++指针使用常见的坑
【C++札记】静态库和动态库详解(上)

码农有道 coding

码农有道,为您提供通俗易懂的技术文章,让技术变的更简单!

【系统设计】LRU缓存

专一服务器后台技术栈知识总结分享

欢迎关注交流共同进步

相关文章
相关标签/搜索