YYCache的總體結構是分爲兩部分:內存緩存和磁盤緩存。node
內存緩存提供容量小但高速的存取功能,磁盤緩存提供大容量但低速的持久化存儲。
算法
內存的讀寫速度遠大於磁盤的讀寫速度,將頻繁使用的數據存在內存中,再次用到的時候直接從內存中讀取,從而提升性能。數組
1. 緩存算法:根據場景選擇合適的緩存算法,才能使緩存有較高的命中率。常見的緩存算法有:LRU(最近最少使用)、LRU-2(相似LRU,准入門檻變成2次)、LFU(最不常用)、FIFO(先進先出)等等。緩存
2. 讀寫性能:相同狀況下,存儲數據和讀取數據的速度。安全
3. 線程安全:考慮到可能會在多個線程中讀寫緩存。bash
LRU算法(least recently used)顧名思義,淘汰掉最近最少使用的數據。異步
1. 訪問的數據未命中緩存,若是此時緩存未滿的時候。直接將新數據插到最前面(這也決定了基礎結構不能用數組,只能用鏈表,數組插入頭部時間複雜度爲O(n),鏈表插入頭部時間複雜度爲O(1))。async
2. 訪問的數據命中緩存,將訪問的數據插到鏈表的最前面。性能
3. 訪問的數據未命中數據,可是此時緩存已經滿了,此時須要先淘汰掉鏈表的尾部,而後再見新數據插到最前面。ui
YYModel的內存緩存部分是YYMemoryCache
這個類實現的。YYMemoryCache使用了LRU緩存算法,由hashMap+雙鏈表實現。由於hashMap的讀操做是O(1),用於保證讀取速度。雙鏈表的刪除和插入操做都是O(1),很好的保證了寫操做的速度。
1. 單鏈表的刪除操做須要讀到前一個節點,而讀取前一個節點須要從頭遍歷。
2. 有人會說,也不是吧,能夠將下一個節點的數據賦給當前節點,而後刪除下一個節點,這樣就至關於刪除了當前節點(刪除了當前節點的數據)。首先這樣有一遍拷貝數據的操做,若是刪除的是最後一個,這種作法就沒有了。而緩存算法的淘汰機制恰好刪除的都是最後一個。
hashmap做用在於快速讀取。雙鏈表用於維護順序,實現淘汰機制。
LRU機制對應的僞代碼:
1. 訪問的數據未命中緩存,若是此時緩存未滿的時候。直接將新數據插到最前面
if (!hashmap[@"key"]) {
Node *node = [[Node alloc] init];
node.data = data;
hashmap[@"key"] = node;
if (self.linkedList.head) {
node.next = self.linkedList.head;
node.prev = nil;
self.linkedList.head.prev = node;
self.linkedList.head = node;
} else {
node.prev = nil;
node.next = nil;
self.linkedList.head = node;
self.linkedList.tail = node;
}
}複製代碼
2. 訪問的數據命中緩存,將訪問的數據插到鏈表的最前面。
if (hashmap[@"key"]) {
Node *node = hashmap[@"key"];
if (node == self.linkedList.head) {
return;
} else if (node == self.linkedList.tail) {
node.prev.next = nil;
self.linkedList.tail = node.prev;
node.next = self.linkedList.head;
self.linkedList.head.prev = node;
self.linkedList.head = node;
} else {
node.prev.next = node.next;
node.next.prev = node.prev;
node.next = self.linkedList.head;
self.linkedList.head.prev = node;
self.linkedList.head = node;
}
}複製代碼
3. 訪問的數據未命中數據,可是此時緩存已經滿了,此時須要先淘汰掉鏈表的尾部,而後再見新數據插到最前面。
Node *node = self.linkedList.tail;
node.prev.next = nil;
self.linkedList.tail = node.prev;
[hashmap removeObjectForKey:@"key"];
free(node);複製代碼
1. 讀寫的時候加鎖保證線程安全
2. 刪除釋放節點在異步線程,提升性能。
if (_lru->_totalCost > _costLimit) {
dispatch_async(_queue, ^{
[self trimToCost:_costLimit];
});
}複製代碼