前言:
最近一直在致力于為公司app添加緩存功能,為了尋找一個最佳方案,這幾天先做個技術預研,經過這兩天的查找資料基本上確定了兩個開源框架進行選擇,這兩個開源框架分別是:pincache、yycache,上篇已經簡單介紹了pincache使用,本篇主要來學習一下yycache的使用方式,以及和pincache性能的簡單對比。
關于yycache
1. 內存緩存(yymemorycache)
存儲的單元是_yylinkedmapnode,除了key和value外,還存儲了它的前后node的地址_prev,_next.整個實現基于_yylinkedmap,它是一個雙向鏈表,除了存儲了字典_dic外,還存儲了頭結點和尾節點.它實現的功能很簡單,就是:有新數據了插入鏈表頭部,訪問過的數據結點移到頭部,內存緊張時把尾部的結點移除.就這樣實現了淘汰算法.因為內存訪問速度很快,鎖占用的時間少,所以用的速度最快的osspinlocklock
2. 硬盤緩存(yydiskcache)
采用的是文件和數據庫相互配合的方式.有一個參數inlinethreshold,默認20kb,小于它存數據庫,大于它存文件.能獲得效率的提高.key:path,value:cache存儲在nsmaptable里.根據path獲得cache,進行一系列的set,get,remove操作更底層的是yykvstorage,它能直接對sqlite和文件系統進行讀寫.每次內存超過限制時,select key, filename, size from manifest order by last_access_time desc limit ?1會根據時間排序來刪除最近不常用的數據.硬盤訪問的時間比較長,如果用osspinlocklock鎖會造成cpu消耗過大,所以用的dispatch_semaphore_wait來做.
yycache使用
1.同步方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
//模擬數據 nsstring *value=@ "i want to know who is lcj ?" ; //模擬一個key //同步方式 nsstring *key=@ "key" ; yycache *yycache=[yycache cachewithname:@ "lcjcache" ]; //根據key寫入緩存value [yycache setobject:value forkey:key]; //判斷緩存是否存在 bool iscontains=[yycache containsobjectforkey:key]; nslog(@ "containsobject : %@" , iscontains?@ "yes" :@ "no" ); //根據key讀取數據 id vuale=[yycache objectforkey:key]; nslog(@ "value : %@" ,vuale); //根據key移除緩存 [yycache removeobjectforkey:key]; //移除所有緩存 [yycache removeallobjects]; |
2.異步方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
//模擬數據 nsstring *value=@ "i want to know who is lcj ?" ; //模擬一個key //異步方式 nsstring *key=@ "key" ; yycache *yycache=[yycache cachewithname:@ "lcjcache" ]; //根據key寫入緩存value [yycache setobject:value forkey:key withblock:^{ nslog(@ "setobject sucess" ); }]; //判斷緩存是否存在 [yycache containsobjectforkey:key withblock:^(nsstring * _nonnull key, bool contains) { nslog(@ "containsobject : %@" , contains?@ "yes" :@ "no" ); }]; //根據key讀取數據 [yycache objectforkey:key withblock:^(nsstring * _nonnull key, id<nscoding> _nonnull object) { nslog(@ "objectforkey : %@" ,object); }]; //根據key移除緩存 [yycache removeobjectforkey:key withblock:^(nsstring * _nonnull key) { nslog(@ "removeobjectforkey %@" ,key); }]; //移除所有緩存 [yycache removeallobjectswithblock:^{ nslog(@ "removeallobjects sucess" ); }]; //移除所有緩存帶進度 [yycache removeallobjectswithprogressblock:^( int removedcount, int totalcount) { nslog(@ "removeallobjects removedcount :%d totalcount : %d" ,removedcount,totalcount); } endblock:^( bool error) { if (!error){ nslog(@ "removeallobjects sucess" ); } else { nslog(@ "removeallobjects error" ); } }]; |
yycache緩存lru清理
lru(least recently used)算法大家都比較熟悉,翻譯過來就是“最近最少使用”,lru緩存就是使用這種原理實現,簡單的說就是緩存一定量的數據,當超過設定的閾值時就把一些過期的數據刪除掉,比如我們緩存10000條數據,當數據小于10000時可以隨意添加,當超過10000時就需要把新的數據添加進來,同時要把過期數據刪除,以確保我們最大緩存10000條,那怎么確定刪除哪條過期數據呢,采用lru算法實現的話就是將最老的數據刪掉。接下來我們測試一下
1
2
3
4
5
6
|
yycache *yycache=[yycache cachewithname:@ "lcjcache" ]; [yycache.memorycache setcountlimit:50]; //內存最大緩存數據個數 [yycache.memorycache setcostlimit:1*1024]; //內存最大緩存開銷 目前這個毫無用處 [yycache.diskcache setcostlimit:10*1024]; //磁盤最大緩存開銷 [yycache.diskcache setcountlimit:50]; //磁盤最大緩存數據個數 [yycache.diskcache setautotriminterval:60]; //設置磁盤lru動態清理頻率 默認 60秒 |
模擬一下清理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
for ( int i=0 ;i<100;i++){ //模擬數據 nsstring *value=@ "i want to know who is lcj ?" ; //模擬一個key nsstring *key=[nsstring stringwithformat:@ "key%d" ,i]; [yycache setobject:value forkey:key]; } nslog(@ "yycache.memorycache.totalcost:%lu" ,(unsigned long )yycache.memorycache.totalcost); nslog(@ "yycache.memorycache.costlimit:%lu" ,(unsigned long )yycache.memorycache.costlimit); nslog(@ "yycache.memorycache.totalcount:%lu" ,(unsigned long )yycache.memorycache.totalcount); nslog(@ "yycache.memorycache.countlimit:%lu" ,(unsigned long )yycache.memorycache.countlimit); dispatch_after(dispatch_time(dispatch_time_now, (int64_t)(120 * nsec_per_sec)), dispatch_get_main_queue(), ^{ nslog(@ "yycache.diskcache.totalcost:%lu" ,(unsigned long )yycache.diskcache.totalcost); nslog(@ "yycache.diskcache.costlimit:%lu" ,(unsigned long )yycache.diskcache.costlimit); nslog(@ "yycache.diskcache.totalcount:%lu" ,(unsigned long )yycache.diskcache.totalcount); nslog(@ "yycache.diskcache.countlimit:%lu" ,(unsigned long )yycache.diskcache.countlimit); for ( int i=0 ;i<100;i++){ //模擬一個key nsstring *key=[nsstring stringwithformat:@ "whoislcj%d" ,i]; id vuale=[yycache objectforkey:key]; nslog(@ "key :%@ value : %@" ,key ,vuale); } }); |
yycache和pincache一樣并沒有實現基于最大內存開銷進行lru,不過yycache實現了最大緩存數據個數進行lru清理,這一點也是選擇yycache原因之一,對于yycache磁盤lru清理并不是及時清理,而是后臺開啟一個定時任務進行rlu清理操作,定時時間默認是60s。
yycache與pincache對比
對于我這里的使用場景大部分用于緩存json字符串,我這里就以存儲字符串來對比一下寫入與讀取效率
1.寫入性能對比
yycache
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
//模擬數據 nsstring *value=@ "i want to know who is lcj ?" ; //模擬一個key nsstring *key=@ "key" ; //yycache yycache *yycache=[yycache cachewithname:@ "lcjcache" ]; //寫入數據 cfabsolutetime start = cfabsolutetimegetcurrent(); [yycache setobject:value forkey:key withblock:^{ cfabsolutetime end = cfabsolutetimegetcurrent(); nslog(@ " yycache async setobject time cost: %0.5f" , end - start); }]; cfabsolutetime start1 = cfabsolutetimegetcurrent(); [yycache setobject:value forkey:key]; cfabsolutetime end1 = cfabsolutetimegetcurrent(); nslog(@ " yycache sync setobject time cost: %0.5f" , end1 - start1); |
運行結果
pincache
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
//pincache //模擬數據 nsstring *value=@ "i want to know who is lcj ?" ; //模擬一個key nsstring *key=@ "key" ; pincache *pincache=[pincache sharedcache]; //寫入數據 cfabsolutetime start = cfabsolutetimegetcurrent(); [pincache setobject:value forkey:key block:^(pincache * _nonnull cache, nsstring * _nonnull key, id _nullable object) { cfabsolutetime end = cfabsolutetimegetcurrent(); nslog(@ " pincache async setobject time cost: %0.5f" , end - start); }]; cfabsolutetime start1 = cfabsolutetimegetcurrent(); [pincache setobject:value forkey:key]; cfabsolutetime end1 = cfabsolutetimegetcurrent(); nslog(@ " pincache sync setobject time cost: %0.5f" , end1 - start1); |
運行結果
通過上面的測試可以看出 同樣大小的數據,無論同步方式還是異步方式,yycache性能都要由于pincache。
2.讀取性能對比
yycache
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
yycache *yycache=[yycache cachewithname:@ "lcjcache" ]; //模擬一個key nsstring *key=@ "key" ; cfabsolutetime start = cfabsolutetimegetcurrent(); //讀取數據 [yycache objectforkey:key withblock:^(nsstring * _nonnull key, id<nscoding> _nonnull object) { cfabsolutetime end = cfabsolutetimegetcurrent(); nslog(@ " yycache async objectforkey time cost: %0.5f" , end - start); }]; cfabsolutetime start1 = cfabsolutetimegetcurrent(); [yycache objectforkey:key]; cfabsolutetime end1 = cfabsolutetimegetcurrent(); nslog(@ " yycache sync objectforkey time cost: %0.5f" , end1 - start1); |
運行結果:
pincache
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
pincache *pincache=[pincache sharedcache]; //模擬一個key nsstring *key=@ "key" ; cfabsolutetime start = cfabsolutetimegetcurrent(); //讀取數據 [pincache objectforkey:key block:^(pincache * _nonnull cache, nsstring * _nonnull key, id _nullable object) { cfabsolutetime end = cfabsolutetimegetcurrent(); nslog(@ " pincache async objectforkey time cost: %0.5f" , end - start); }] ; cfabsolutetime start1 = cfabsolutetimegetcurrent(); [pincache objectforkey:key]; cfabsolutetime end1 = cfabsolutetimegetcurrent(); nslog(@ " pincache objectforkey time cost: %0.5f" , end1 - start1); |
運行結果:
通過運行結果,在讀取方面yycache也是優于pincache。
總結:
經過一番查閱資料和自己寫例子測試,最終項目中決定使用yycache進行緩存管理。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。
原文鏈接:http://www.cnblogs.com/whoislcj/p/6429108.html