• redis过期键的策略


    一、过期时间设置:

    127.0.0.1:6379> expire key seconds //设置键的过期时间为多少秒

    127.0.0.1:6379> setex key seconds value  

    注意一下哟

    • 除了字符串自己独有设置过期时间的方法外,其他方法都需要依靠 expire 方法来设置时间
    • 如果没有设置时间,那键是永不过期的,一直留在内存中
    • 如果设置了过期时间,之后又想让缓存永不过期,使用  persist key 

    二、Redis 所有的数据结构都可以设置过期时间,时间一到,就会自动删除。你可以想象 Redis 内部有一个死神,时刻盯着所有设置了过期时间的 key,寿命一到就会立即收割。Redis的过期键的过期时间都是保存在过期字典中,过期键的删除策略有三种,分别是定时删除、惰性删除和定期删除。redis 会将每个设置了过期时间的 key 放入到一个独立的字典中,以后会定时遍历这个字典来删除到期的 key。除了定时遍历之外,它还会使用惰性策略来删除过期的 key,所谓惰性策略就是在客户端访问这个 key 的时候,redis 对 key 的过期时间进行检查,如果过期了就立即删除。定时删除是集中处理,惰性删除是零散处理。

    定时删除

    定时删除策略,是指在设置键的过期时间的同时,创建一个定时器,让定时器在键的过期时间到的时候,立即执行对键的删除操作。

    定时删除策略的优点:

      对内存友好,通过定时器可以保证过期键能尽可能快地被删除,并释放过期键占用的空间。

    定时删除策略的缺点

      1.对CPU不友好。在过期键较多的情况下,删除过期键可能会占用相当一部分的CPU执行时间。在内存不紧张而CPU紧张的情况下,将CPU资源用在删除和当前任务无关的过期键上,无疑也会对服务器的响应时间和吞吐量造成影响。

      2.创建定时器需要Redis服务器中的时间事件,而现在时间事件的实现方式是无序链表,查找一个事件的事件复杂度为O(N),并不能高效地处理大量时间事件。

    惰性删除

    惰性删除策略,是指放任键过期不管,每次从键空间获取键的时候才去检查取得的键是否过期,如果过期的话,就删除该键,如果不过期,就返回该键。

    惰性删除策略的优点

      对CPU友好,程序只在取出键时才对键进行过期检查,删除的目标进行预当前处理的键。

    惰性删除策略的缺点

      惰性删除策略对内存不友好,当数据库中有大量的过期键,而这些键又没有被访问到,那么它们可能因为永远都不会被进行过期检查而被删除。

    定期删除

    定期删除策略,是指每隔一段时间,程序就会对数据库进行一次检查,删除里面的过期键。至于删除多少过期键,以及检查多少数据库,都由算法来决定。

    定期删除策略的难点

      1.如果删除操作太频繁,或者执行时间过长,定期删除策略就会退化成定时删除策略。

      2.如果删除操作执行得太少,或者执行时间太短,定期删除策略又会和惰性删除策略一样,出现内存浪费的现象。

     

    Redis 过期策略实际使用的是惰性删除+定期删除两种策略的一个配合使用。

    三、AOF 和 RDB对过期键的处理

    1、RDB对过期key的处理

    过期key对RDB没有任何影响

    • 从内存数据库持久化数据到RDB文件
      • 持久化key之前,会检查是否过期,过期的key不进入RDB文件
    • 从RDB文件恢复数据到内存数据库
      • 数据载入数据库之前,会对key先进行过期检查,如果过期,不导入数据库(主库情况)

    2、AOF对过期key的处理

    过期key对AOF没有任何影响

    • 从内存数据库持久化数据到AOF文件:
      • 当key过期后,还没有被删除,此时进行执行持久化操作(该key是不会进入aof文件的,因为没有发生修改命令)
      • 当key过期后,在发生删除操作时,程序会向aof文件追加一条del命令(在将来的以aof文件恢复数据的时候该过期的键就会被删掉)
    • AOF重写
      • 重写时,会先判断key是否过期,已过期的key不会重写到aof文件 

    三、Redis中的内存淘汰机制:

    内存淘汰机制 redis.conf 中配置:

    # maxmemory-policy noeviction 
    参数 描述
    volatile-lru                  从已设置过期时间的数据集中挑选最近最少使用的数据淘汰
     volatile-lfu  从已设置过期时间的数据集中挑选最不经常使用的数据淘汰
     volatile-ttl  从已设置过期时间的数据集中挑选将要过期的数据淘汰
     volatile-random  从已设置过期时间的数据集中挑选任意数据淘汰
     allkeys-lru  当内存不足写入新数据时淘汰最近最少使用的Key
     allkeys-random  当内存不足写入新数据时随机选择key淘汰
     allkeys-lfu  当内存不足写入新数据时移除最不经常使用的Key
     no-eviction  当内存不足写入新数据时,写入操作会报错,同时不删除数据
    • volatile 为前缀的策略都是从已过期的数据集中进行淘汰。
    • allkeys 为前缀的策略都是面向所有key进行淘汰。
    • LRU (least recently used)最近最少用到的。
    • LFU (Least Frequently Used)最不常用的。
    • 它们的触发条件都是 Redis 使用的内存达到阈值时

    1、手写LRU缓存

    public class LRUCache {
     
        private Map<Integer, Integer> map;
        private final int capacity;
     
        public LRUCache(int capacity) {
            this.capacity = capacity;
            //定义了迭代顺序(true)
            map = new LinkedHashMap<Integer, Integer>(capacity,0.75f,true){
                @Override
                protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) {
                    //当map中的数据量大于指定的缓存个数的时候,就自动删除最老的数据
                    return size() > capacity;
                }
            };
        }
     
        public int get(int key) {
            return map.getOrDefault(key, -1);
        }
     
        public void put(int key, int value) {
            map.put(key, value);
        }
     
    } 
    View Code

    对于 LinkedHashMap 而言:

    public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V>

    底层使用哈希表与双向链表来保存所有元素。

    LinkedHashMap 中的 Entry 集成与 HashMap 的 Entry,但是其增加了 before 和 after 的引用,指的是上一个元素和下一个元素的引用

    static class Entry<K,V> extends HashMap.Node<K,V> {
        Entry<K,V> before, after;
        Entry(int hash, K key, V value, Node<K,V> next) {
            super(hash, key, value, next);
        }
    } 

    初始化:

    在 LinkedHashMap 的构造方法中,实际调用了父类 HashMap 的相关构造方法来构造一个底层存放的 table 数组,但额外可以增加 accessOrder 这个参数,如果不设置,默认为 false,代表按照插入顺序进行迭代;当然可以显式设置为 true,代表以访问顺序进行迭代。

    public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) {
        super(initialCapacity, loadFactor);
        this.accessOrder = accessOrder;
    }

    2.手写 LFU 算法

     1 public class LFUCache<K,V> {
     2 
     3     private Map<K, LinkNode> cache ;
     4     private Map<Integer, DoubleLinkList<K,V>> freq ;
     5     private int maxSize;
     6     private int size;
     7 
     8     public LFUCache(int maxSize) {
     9         this.maxSize = maxSize;
    10         this.cache = new HashMap<>(maxSize*4/3);
    11         this.freq = new HashMap<>();
    12     }
    13 
    14     public V get(K key){
    15         LinkNode node = cache.get(key);
    16         if(node==null){
    17             return null;
    18         }
    19         changeNodeFreq(node);
    20         return (V) node.value;
    21     }
    22 
    23     private void changeNodeFreq(LinkNode node) {
    24         freq.get(node.freq).remove(node);
    25         node.freq = node.freq + 1;
    26         if (!freq.containsKey(node.freq)) {
    27             freq.put(node.freq, new DoubleLinkList<>());
    28         }
    29         freq.get(node.freq).addNodeToTail(node);
    30     }
    31 
    32 
    33     public void put(K key,V value){
    34 
    35         LinkNode node = cache.get(key);
    36 
    37         if(node!=null){
    38             node.value = value;
    39             changeNodeFreq(node);
    40             return;
    41         }
    42 
    43         if(node==null){
    44             node = new LinkNode(key,value);
    45             cache.put(key,node);
    46             if(!freq.containsKey(node.freq)){
    47                 freq.put(node.freq,new DoubleLinkList<>());
    48             }
    49             freq.get(node.freq).addNodeToTail(node);
    50             size++;
    51             if(size>maxSize){
    52                 int minFreq = getMinFreq();
    53                 LinkNode remove = freq.get(minFreq).removeFromHead();
    54                 cache.remove(remove.key);
    55                 size--;
    56             }
    57             return;
    58         }
    59 
    60 
    61     }
    62 
    63     public int getMinFreq(){
    64         int min = Integer.MAX_VALUE;
    65         for (Map.Entry<Integer, DoubleLinkList<K, V>> entry : freq.entrySet()) {
    66             Integer freq = entry.getKey();
    67             min = freq<min ? freq : min;
    68             if(min==1){
    69                 break;
    70             }
    71         }
    72         return min;
    73     }
    74 
    75     public static void main(String[] args) {
    76         LFUCache<String,String> lfuCache = new LFUCache<>(4);
    77 
    78         lfuCache.put("1","1");
    79         lfuCache.put("2","2");
    80         lfuCache.put("3","3");
    81         lfuCache.put("4","4");
    82 
    83         lfuCache.get("1");
    84         lfuCache.get("2");
    85 
    86         lfuCache.put("5","5");
    87 
    88         System.out.println(lfuCache.get("3"));
    89     }
    90 }
    View Code
  • 相关阅读:
    oracle闪回查询
    带搜索框的jQuery下拉框插件
    Eclipse、Tomcat、Spring3等使用过程的一些配置、错误等的总结记录
    局域网不能访问本机IIS网站的解决方法
    在同一台电脑部署多个Tomcat服务
    Tomcat重启脚本
    IE8下面parseInt('08')、parseInt('09')会转成0
    [转]Examining Open vSwitch Traffic Patterns
    [转]Ubuntu Precise
    [转] iptables
  • 原文地址:https://www.cnblogs.com/xiaowei123/p/13167224.html
Copyright © 2020-2023  润新知