一、緩存是什麼算法
二、什麼是LRU緩存編程
三、LRU緩存設計思路緩存
四、LRU緩存接口服務器
其實緩存的思想在生活中到處可見,好比說咱們的書架上擺滿了各類書籍,可是我最近想集中精力攻克數據結構這門課,因次爲了方便,我將與數據結構有關的書先從書架取下來,放在咱們的書桌上,這樣咱們取書的時候就快不少了,伸手可得,而不用跑到書架旁取書了,在這個例子裏,書桌所扮演的就是緩存的角色。網絡
在計算機中,咱們知道,訪問磁盤的效率比訪問內存的效率低得多,低多少了,大概是10W級別吧,以下圖所示。
咱們知道有個很著名的二八定律。其實計算機世界中也有不少狀況遵照這個定律,好比說網站訪問的特色:80% 的業務訪問集中在20% 的數據上。那麼很天然就想到:既然大部分業務訪問集中在一小部分數據上,那麼若是經過設計合理的數據結構把這一小部分數據存放在內存中,是否是極大的提升了系統的性能呢,這就是緩存。數據結構
從上面能夠看出,不管是生活中仍是計算機世界中的緩存,主要有如下兩個特色:併發
提升系統效率,ide
因爲內存是有限的,所以緩存總有滿的時候,那麼當緩存滿了的時候,這時候又有新的數據須要加入到緩存中時,咱們該怎麼辦了?沒什麼別的辦法,只有從緩存中刪除舊數據爲新數據騰出空間,那麼究竟刪除原來緩存的哪些數據了?這就涉及到緩存的替換策略,LRU就是一種緩存策略。函數
LRU,Least Recently Used的簡寫,翻譯過來就是「最近最少使用」。該算法依據於程序的局部性原理, 其淘汰舊數據的策略是,距離當前最久沒有被訪問過的數據應該被淘汰。性能
舉個例子:如今有一個能存放5個數據的緩存,剛開始中緩存中存放了3個數據A->B->C,以下所示:
接着又在緩存中插入2個數據D->E,此時緩存已經滿了。以下:
好了,如今咱們來訪問數據A,如今A成爲了最近訪問的數據,B成了最久沒有訪問過的數據:
如今咱們又有一個數據F想要插入到緩存中,因爲緩存已經滿了,須要從緩存中淘汰舊數據,此時緩存中的數據B是距離當前時間最久沒有被訪問過的數據,根據LRU算法,咱們會淘汰掉數據B:將F插入到當前B的位置。
此時數據C成爲了距離當前時間最久沒有被訪問過的數據,所以若是再來一個數據的話,就應該插入到C當前的位置,依次類推。
理解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緩存的最重要的操做無外乎是建立、銷燬、插入數據、查找數據。下面給出這幾個接口,至於具體實現,這裏先不分析。後面有機會再一一分析。
/********************************************* 函數名: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++札記】靜態庫和動態庫詳解(上)
碼農有道,爲您提供通俗易懂的技術文章,讓技術變的更簡單!