一、配置Redis内存淘汰策略
maxmemory 100mbmaxmemory-policy allkeys-lrumaxmemory-samples 5
注意:Redis的LRU算法并非完整的实现,而是近似LRU的算法,详细介绍点击这里
二、LRU实现原理
1、双向链表 + 哈希表
1、哈希表:查找快,但是哈希表的数据是乱序的。
2、链表:插入、删除都很快,但是查询慢。
- 1.每次默认从链表头部添加元素,那么显然越靠近头部的元素就越是最近使用的。越靠近尾部的元素就是越久未使用的。
- 2.对于某一个 key ,可以通过哈希表快速定位到链表中的节点,从而取得对应的 value。
- 3.链表显然是支持在任意位置快速插入和删除的,修改指针就行。但是单链表无法按照索引快速访问某一个位置的元素,都是需要遍历链表的,所以这里借助哈希表,可以通过 key,快速的映射到任意一个链表节点,然后进行插入和删除。
1、为什么这里要用双链表呢,单链表为什么不行?
答:双链表删除元素除了自己本身的指针信息,前驱节点的指针和后驱节点的指针。单链表只有后驱节点的指针。
2、哈希表里面已经保存了 key ,那么链表中为什么还要存储 key 和 value 呢,只存入 value 不就行了?
答:删除哈希表中的数据需要通过双链表中的key,才能删除对应value。
三、PHP实现LRU
源码位置
/*** LRU算法* 构造一个双向链表(双向链表删除尾部节点的上一个元素时间复杂度为O(1))*/
class LRUCache
{//头部节点private $head;//尾部节点private $tail;//最大容量,大于淘汰尾部节点指向的上一个元素private $capacity;//存放key对应的节点 key => nodeprivate $hashmap;/*** 初始化头部尾部节点* LRUCache constructor.* @param $capacity*/public function __construct($capacity){$this->capacity = $capacity;$this->hashmap = array();$this->head = new Node(null, null);$this->tail = new Node(null, null);//头结点右指针指向尾结点$this->head->setNext($this->tail);//尾结点左指针指向头结点$this->tail->setPrevious($this->head);}/*** 获取元素* @param $key* @return null*/public function get($key){if (!isset($this->hashmap[$key])) {return null;}$node = $this->hashmap[$key];if (count($this->hashmap) == 1) {return $node->getData();}//先删除已经存在的结点$this->detach($node);//重新将新结点插入到头结点之后$this->attach($this->head, $node);return $node->getData();}/*** 设置key value* @param $key* @param $data* @return bool*/public function put($key, $data){if ($this->capacity <= 0) {return false;}if (isset($this->hashmap[$key]) && !empty($this->hashmap[$key])) {$node = $this->hashmap[$key];//重置结点到头结点之后$this->detach($node);$this->attach($this->head, $node);$node->setData($data);} else {$node = new Node($key, $data);$this->hashmap[$key] = $node;//添加节点到头部节点之后$this->attach($this->head, $node);//检测容量是否达到最大值if (count($this->hashmap) > $this->capacity) {//如果达到最大值 删除尾节点左指针指向的元素$nodeToRemove = $this->tail->getPrevious();$this->detach($nodeToRemove);unset($this->hashmap[$nodeToRemove->getKey()]);}}return true;}/*** 删除key* @param $key* @return bool*/public function remove($key){if (!isset($this->hashmap[$key])) {return false;}$nodeToRemove = $this->hashmap[$key];$this->detach($nodeToRemove);unset($this->hashmap[$nodeToRemove->getKey()]);return true;}/*** 添加新结点到头结点之后* @param $head* @param $node*/private function attach($head, $node){//双向链表插入一个元素到头结点之后$node->setPrevious($head);$node->setNext($head->getNext());$node->getNext()->setPrevious($node);$node->getPrevious()->setNext($node);}/*** 删除结点* @param $node*/private function detach($node){$node->getPrevious()->setNext($node->getNext());$node->getNext()->setPrevious($node->getPrevious());}}/*** 结点数据结构*/
class Node
{private $key;//key对应的内容private $data;//结点右指针private $next;//结点左指针private $previous;/*** Node constructor.* @param $key* @param $data*/public function __construct($key, $data){$this->key = $key;$this->data = $data;}/*** 设置节点数据的新值* Sets a new value for the node data* @param string the new content of the node*/public function setData($data){$this->data = $data;}/*** 将节点设置为下一个节点* Sets a node as the next node* @param Node $next the next node*/public function setNext($next){$this->next = $next;}/*** 将节点设置为前一个节点* Sets a node as the previous node* @param Node $previous the previous node*/public function setPrevious($previous){$this->previous = $previous;}/*** 返回节点键* Returns the node key* @return string the key of the node*/public function getKey(){return $this->key;}/*** 返回节点数据* Returns the node data* @return mixed the content of the node*/public function getData(){return $this->data;}/*** 返回下一个节点, 该节点的下一个节点* Returns the next node* @return Node the next node of the node*/public function getNext(){return $this->next;}/*** 返回上一个节点, 该节点的上一个节点* Returns the previous node* @return Node the previous node of the node*/public function getPrevious(){return $this->previous;}}$lru = new LRUCache(4);
$lru->put(1, 1111);
$lru->put(2, 2222);
$lru->put(3, 3333);
//$lru->put(3, 3333);
//$lru->put(4, 4444);
//var_dump($lru->get(2));
//var_dump($lru->get(1));
学习地址