Redis LRU算法

4 篇文章 0 订阅

一、配置Redis内存淘汰策略

maxmemory 100mb

maxmemory-policy allkeys-lru

maxmemory-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 => node
    private $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));

学习地址

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值