美文网首页
Redis的expire的缓存过期策略是如何实现的?

Redis的expire的缓存过期策略是如何实现的?

作者: 程序员进阶之路 | 来源:发表于2020-07-28 22:25 被阅读0次

    自知对Redis的知识了解的还算不错,但当面试官问到expire是怎么实现的时候我突然懵了,虽然最后凭借了猜测也猜出了定期+惰性删除,但总感觉这块之前复习遗漏了,现在来重新梳理一下。

    面试官:你知道expire设置过期时间的工作原理是什么吗?到期的数据是怎么过期的呢?

    我:emmm...我觉得是采用了定期删除,每隔一段时间去扫描检测key对应的缓存是否过期,如果过期了就删除。

    面试官:那如果key刚好在你两次扫描之间过期了呢?如果key特别多全量扫描不是很耗费性能吗?

    我:嗯这样的话采用全量定期删除确认不够好,可以把全量改为随机抽取一部分进行检测,检测不到的那些数据可以在get的时候再检测是否过期,如果过期就删除数据并返回空。

    过期策略通常有三种:

    • 定时过期:每个设置过期时间的key都需要创建一个定时器,到过期时间就会立即清除。该策略可以立即清除过期的数据,对内存很友好;但是会占用大量的CPU资源去处理过期的数据,从而影响缓存的响应时间和吞吐量。
    • 惰性过期:只有当访问一个key时,才会判断该key是否已过期,过期则清除。该策略可以最大化地节省CPU资源,却对内存非常不友好。极端情况可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存。(memcache只使用惰性删除 )
    • 定期过期:每隔一定的时间(100ms),会扫描一定数量的数据库的expires字典中一定数量的key,并清除其中已过期的key。该策略是前两者的一个折中方案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。
      (expires字典会保存所有设置了过期时间的key的过期时间数据,其中,key是指向键空间中的某个键的指针,value是该键的毫秒精度的UNIX时间戳表示的过期时间。键空间是指该Redis集群中保存的所有键。)
      Redis中同时使用了惰性过期和定期过期两种过期策略

    面试官:嗯,没错。确实是这样的。那如果有些数据既没有被抽取到又没有被get的话怎么办,这样不就一直留在内存中了吗?

    我:如果数据没有被检测删除导致数据堆积的话,最后应该还有Redis内存淘汰机制的,比如说最常见的LRU。

    Redis的内存淘汰策略是指在Redis的用于缓存的内存不足时,怎么处理需要新写入且需要申请额外空间的数据。主要有报错处理,时间维度移除和空间维度移除。

    • noeviction:当内存不足以容纳新写入数据时,新写入操作会报错。
    • allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key。
    • allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个key。
    • volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key。
    • volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个key。
    • volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的key优先移除。

    面试官:嗯那你能手写个LRU算法吗?

    我:。。。好吧

    LRU 的全称是 Least Recently Used,也就是说我们认为最近使用过的数据应该是是「有用的」,很久都没用过的数据应该是无用的,内存满了就优先删那些很久没用过的数据。

    C++版本

       class LRUCache {
        public:
        LRUCache(int capacity) {
            cap=capacity;
        } 
        int get(int key) {
            if(hashtable.count(key)==0) return -1;
            else//更新到表头
            {
                auto iter = hashtable[key];
                cache.splice(cache.begin(),cache,iter);//移动到链表头
                return cache.begin()->second;
            }
        }
        
        void put(int key, int value) {
            if(hashtable.count(key)==0) {
                if(cache.size()==cap)   {
                    hashtable.erase(cache.back().first);
                    cache.pop_back();
                }
                cache.push_front(make_pair(key,value));
                hashtable[key]=cache.begin(); 
            }
            else {
                auto iter = hashtable[key];
                iter->second=value;
                cache.splice(cache.begin(),cache,iter);
                hashtable[key]=cache.begin();
            }
        }
        unordered_map<int,list<pair<int,int>>::iterator> hashtable;
        list<pair<int,int>> cache;
        int cap=0;
        };```
    
    

    PHP版本

        //PHP
        class LRUCache 
        {
        protected $capacity;
        protected $hash;
        protected $used;
        
        public function __construct($capacity) 
        {
            $this->capacity = $capacity;
            $this->hash = [];
            $this->used = [];
        }
        public function get($key) 
        {
            if (isset($this->hash[$key])) {
                $index = array_search($key, $this->used);
                unset($this->used[$index]);
                $this->used[] = $key;
                return $this->hash[$key];
            }
            return -1;
        }
        public function put($key, $value) 
        {
            if (isset($this->hash[$key])) {
                $index = array_search($key, $this->used);
                unset($this->used[$index]);
                $this->hash[$key] = $value;
                $this->used[] = $key;
            } else {
                if (count($this->hash) == $this->capacity) {
                    $k = array_shift($this->used);
                    unset($this->hash[$k]);
                }
    
                $this->used[] = $key;
                $this->hash[$key] = $value;
            }
        }
        }```
    
    
    面试官:Redis这里先过了,我们来看下一个问题吧。
    

    相关文章

      网友评论

          本文标题:Redis的expire的缓存过期策略是如何实现的?

          本文链接:https://www.haomeiwen.com/subject/bdtcrktx.html