zoukankan      html  css  js  c++  java
  • 《算法

    一:概述

      - LRU 用于管理缓存策略,其本身在 Linux/Redis/Mysql 中均有实现。只是实现方式不尽相同。

      - LRU 算法【Least recently used(最近最少使用)

      - 根据数据的历史访问记录来进行淘汰数据,其核心思想是 "如果数据最近被访问过,那么将来被访问的几率也更高"。

       - GITHUB

    二:单链表实现的 Lru 算法

      - 思路

        - 单链表实现

      - 流程

        - 1. 新数据插入到链表头部

        - 2. 每当缓存命中(即缓存数据被访问),则将数据移到链表头部

        - 3. 当链表满的时候,将链表尾部的数据丢弃

      - 优点

        - 实现简单。

        - 当存在热点数据时,LRU的效率很好。

      - 缺点

        - 偶发性的、周期性的批量操作会导致LRU命中率急剧下降缓存污染情况比较严重

        - 命中时需要遍历链表,找到命中的数据块索引,然后需要将数据移到头部。

      - 流程图

        - 

      - 代码实现(PHP)

      • /**
         * 思路
         *     单链表实现
         * 原理
         *     单链表
         * 流程
         *     1. 新数据插入到链表头部;
         *     2. 每当缓存命中(即缓存数据被访问),则将数据移到链表头部;
         *     3. 当链表满的时候,将链表尾部的数据丢弃。
         * 优点
         *     实现简单。
         *     当存在热点数据时,LRU的效率很好。
         * 缺点
         *     偶发性的、周期性的批量操作会导致LRU命中率急剧下降,缓存污染情况比较严重。
         *     命中时需要遍历链表,找到命中的数据块索引,然后需要将数据移到头部。
         */
        class Lru
        {
            public static $lruList = []; // 顺序存储单链表结构
            public static $maxLen  = 5;  // 链表允许最大长度
            public static $nowLen  = 0;  // 链表当前长度
        
            /**
             * LRU_1 constructor.
             * 由于 PHP 不是常驻进程程序,所以链表初始化可以通过 Mysql/Redis 实现
             */
            public function __construct()
            {
                self::$lruList = [];
                self::$nowLen  = count(self::$lruList);
            }
        
            /**
             * 获取 key => value
             * @param $key
             * @return null
             */
            public function get($key)
            {
                $value = null;
        
                // lru 队列为空,直接返回
                if (!self::$lruList) {
                    self::$lruList[] = [$key => $this->getData($key)]; // 根据实际项目情况获取数据
                    self::$nowLen++;
                    return $value;
                }
        
                // 查找 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 缓存
                if (!isset($value)) {
        
                    // 插入头部
                    array_unshift(self::$lruList, [$key => $this->getData($key)]); // 根据实际项目情况获取数据
                    self::$nowLen++;
        
                    if (self::$nowLen > self::$maxLen) {
                        self::$nowLen--;
                        array_pop(self::$lruList);
                    }
                }
        
                return $value;
        
            }
        
            /**
             * 输出 Lru 队列
             */
            public function echoLruList()
            {
                var_dump(self::$lruList);
            }
        
            /**
             * 根据真实环境获取数据
             * @param $key
             * @return string
             */
            public function getData($key)
            {
                return 'data';
            }
        }

    三:Lru K 算法

      - 思路

        - 为了避免 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还需要维护历史队列,所以消耗的内存会更多

      - 流程图

        - 

      - 代码实现

      • /**
         * 思路
         *     为了避免 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';
            }
        }
  • 相关阅读:
    梅花雨控件使用时注意的...
    利用XML实现通用WEB报表打印(实现篇)
    Improve performance using ADO.NET 2.0 batch update feature
    hook
    owc11生成饼状图
    PHP数组合并:[“+”运算符]、[array_merge]、[array_merge_recursive]区别
    PHP中使用函数array_merge()合并数组
    WCF 第四章 绑定
    WCF 第四章 绑定 跨机器通信
    WCF 第六章 序列化与编码 系列文章
  • 原文地址:https://www.cnblogs.com/25-lH/p/11121172.html
Copyright © 2011-2022 走看看