I. Overview
- the LRU cache policy for the management , which itself has implemented in Linux / Redis / Mysql in. Just different implementations.
- LRU algorithm [ Least recently used (Least Recently Used) ]
- to eliminate data access based on historical data record, its core idea is "if the data has recently been visited, then the probability of future access is also higher."
Two: Lru algorithm implemented singly linked list
- Ideas
- singly linked list to achieve
- Process
- 1. The new data is inserted into the head of the linked list ;
- 2. Whenever a cache hit (i.e., cache data to be accessed), then the data is moved to the head of the linked list ;
- 3. When the list is full, the tail of the linked list data is discarded .
- Benefits
- simple.
- when there is hot data, the LRU good efficiency.
- shortcomings
- sporadic, periodic batch operation will lead to a sharp decline in LRU hit rate , cache pollution is very serious .
- need to traverse when the hit list , find the data block index hit, then need to move the data to the head.
- flow chart
-
- code implementation (PHP)
-
-
/ * * * Idea * achieve single chain * principles * single chain * Process * 1. The new data is inserted into the head of the linked list; * 2. Whenever a cache hit (i.e., cache data to be accessed), the data is moved to the head of the list ; * 3. when the list is full, the tail of the linked list data is discarded. * Advantage * simple. * When present hotspot data, the LRU good efficiency. * Shortcomings * sporadic, periodic batch operation will lead to a sharp decline in LRU hit rate, cache pollution is very serious. * Need to traverse when the hit list, find the data block index hit, then you need to move the data to the head. * / Class LRU { public static $ lruList = []; // sequential storage single chain structure public static $ = maxLen . 5 ; // list maximum length allowed public static $ nowLen = 0; // list the current length / * * . * LRU_1 constructor * Because PHP is not a resident program process, so the list can be achieved by initialization Mysql / Redis * / public function __construct () { Self :: $ lruList = []; Self: : $ nowLen = COUNT (Self :: $ lruList); } / * * * = Get Key> value * @param Key $ * @return null * / public function GET (Key $) { $ value = null ; // LRU the queue is empty, the direct return IF (!:: $ lruList Self) { Self lruList :: $ [] = [Key $ => $ the this -> the getData (Key $)]; // Get the actual situation data item Self :: $ nowLen ++ ; return $ value; } // Find lru buffer for ($ I = 0 ; $ I <Self :: $ nowLen; $ I ++ ) { // if the cache exists, the process directly returns, and reinsert the list header data IF (isset (Self :: lruList $ [$ I] [Key $])) { $ value = Self :: $ lruList [$ I] [Key $]; the unset ($ lruList Self :: [$ I]); array_unshift (Self :: $ lruList , [Key $ => $ value]); BREAK; } } // If no cache lru IF (! Isset ($ value)) { // insertion head array_unshift (Self :: $ lruList, [Key $ => $ the this -> the getData (Key $)]); // get the data items according to the actual situation Self :: $ nowLen ++ ; IF (Self :: $ nowLen> Self maxLen :: $) { Self nowLen :: $ - ; array_pop (Self :: $ lruList); } } return $ value; } / * * * Lru output queue * / public function echoLruList () { var_dump (Self :: $ lruList); } / * * * based on real environmental data acquired * @param Key $ * @return String * / public function the getData (Key $) { return ' Data ' ; } }
-
Three: Lru K algorithm
- Ideas
- In order to avoid the LRU 'cache poisoning' problem
- add a queue to maintain the number of cache arise. The core idea is to extend the "recently used one time," the criteria for the "recently used K times."
- Principle
- compared to the LRU, the LRU-K need more to maintain a queue for history being accessed recorded all cached data.
- Only when the number of accesses data reaches K times before the data into the cache.
- When you need to eliminate data, LRU-K would eliminate K-th access time from the current time the largest data.
- Process
- 1. Data is first accessed, added to the access history list ;
- 2. If the data access in the history list has not reached K times access, according to certain rules LRU eliminated ;
- 3. When the number of accesses data access history queue reached K times , the data will be deleted from the index history queue, the data to the buffer queue , the buffer and the data buffer queue re-ordered by time;
- 4. cache data queue is accessed again, reordering;
- 5. need to eliminate data buffer queue row phased out at the end of the data, namely: elimination "penultimate K visit from now the oldest" data.
- Benefits
- LRU-K reduces the problem of "cache pollution" caused, the hit rate is higher than the LRU.
- shortcomings
- the LRU-K queue is a priority queue, the algorithm complexity and the cost is relatively high.
- Since the LRU-K also need to maintain a historical cohort, so the memory consumption will be more .
- flow chart
-
- Code
-
-
/** * 思路 * 为了避免 LRU 的 '缓存污染' 问题 * 增加一个队列来维护缓存出现的次数。其核心思想是将“最近使用过1次”的判断标准扩展为“最近使用过K次”。 * 原理 * 相比LRU,LRU-K需要多维护一个队列,用于记录所有缓存数据被访问的历史。 * 只有当数据的访问次数达到K次的时候,才将数据放入缓存。 * 当需要淘汰数据时,LRU-K会淘汰第K次访问时间距当前时间最大的数据 * 流程 * 1.数据第一次被访问,加入到访问历史列表; * 2.如果数据在访问历史列表里后没有达到K次访问,则按照一定规则 LRU淘汰; * 3.当访问历史队列中的数据访问次数达到K次后,将数据索引从历史队列删除,将数据移到缓存队列中,并缓存此数据,缓存队列重新按照时间排序; * 4.缓存数据队列中被再次访问后,重新排序; * 5.需要淘汰数据时,淘汰缓存队列中排在末尾的数据,即:淘汰“倒数第K次访问离现在最久”的数据。 * 优点 * LRU-K降低了“缓存污染”带来的问题,命中率比LRU要高。 * 缺点 * LRU-K队列是一个优先级队列,算法复杂度和代价比较高。 * 由于LRU-K还需要维护历史队列,所以消耗的内存会更多。 */ class Lru_K { public static $historyList = []; // 访问历史队列 public static $lruList = []; // 顺序存储单链表结构 public static $maxLen = 5; // 链表允许最大长度 public static $nowLen = 0; // 链表当前长度 /** * LRU_K constructor. * 由于 PHP 不是常驻进程程序,所以链表初始化可以通过 Mysql/Redis 实现 */ public function __construct() { self::$lruList = []; self::$historyList = []; self::$nowLen = count(self::$lruList); } /** * 获取 key => value * @param $key * @return null */ public function get($key) { $value = null; // 查找 lru 缓存 for ($i = 0; $i < self::$nowLen; $i++) { // 如果存在缓存,则直接返回,并将数据重新插入链表头部 if (isset(self::$lruList[$i][$key])) { $value = self::$lruList[$i][$key]; unset(self::$lruList[$i]); array_unshift(self::$lruList, [$key => $value]); break; } } // 如果没有找到 lru 缓存, 则进入历史队列进行计数,当次数大于等于5时候,进入缓存队列 if (!isset($value)) { self::$historyList[$key]++; $value = $this->getData($key); // 进入缓存队列 if (self::$historyList[$key] >= 5) { array_unshift(self::$lruList, [$key => $value]); // 根据实际项目情况获取数据 self::$nowLen++; if (self::$nowLen > self::$maxLen) { self::$nowLen--; array_pop(self::$lruList); } unset(self::$historyList[$key]); // 历史队列推出 } } return $value; } /** * 输出 Lru 队列 */ public function echoLruList() { var_dump(self::$lruList); var_dump(self::$historyList); } /** * 根据真实环境获取数据 * @param $key * @return string */ public function getData($key) { return 'data'; } }
-