六、Redis内存

六、Redis内存

    1、内存配置

        (1)、内存设置

            ①、如果不设置内存大小或者设置内存大小为0,在64位操作系统下不限制内存大小,在32位操作系统下最多使用3GB内存。

            ②、Redis一般推荐设置内存为最大物理内存的四分之三。

        (2)、修改文件配置

            修改maxmemory大小,单位字节

        (3)、通过命令修改

#修改内存大小
config set maxmemory 1024 # 设置内存大小为1KB,成功返回OK
config get maxmemory # 获取内存大小
info memory # 查看内存使用情况 

        (4)、OOM 

#修改内存大小
config set maxmemory 1 # 设置内存大小为1Byte,成功返回OK
set k1 v1 # 添加k1,v1
(error) OOM command not allowed when used memory > 'maxmemory' # 报错:提示OOM,内存不够

    2、淘汰策略

        (1)、过期键删除策略

            ①、定时删除

                数据到了过期时间会立即删除,Redis不可能时刻遍历所有被设置了过期时间的key,来查看该key是否到达过期时间,然后进行删除操作。立即删除能保证内存中数据最大的准确性,但这样对CPU不友好,用CPU性能换取存储空间(拿时间换空间)。

            ②、惰性删除

                数据到了过期时间,不作处理,等下次访问该数据时,如果未过期,返回数据;发现已过期,则删除,返回不存在。如果一个键已过期,而这个键又保存在Redis中,那么它所占用的内存就不会释放。对内存不友好,用存储空间获取CPU性能(拿空间换时间)。

            ③、定期删除

                定期删除策略是以上两种策略的折中,就是每隔一段时间执行一次删除过期键操作,并通过限制删除操作执行的时长和频率来减少删除操作对CPU的影响。

                该策略难点是如果执行的时间太长,或者执行太频繁,定期删除策略就会退化成定时删除。如果执行太少,或者执行时间太短就会退化成惰性删除策略。所以执行时间和执行频率需要合理设置。

        (2)、缓存淘汰策略

            针对过期键删除策略的弊端,Redis提供了缓存淘汰策略,当Redis缓存达到maxmemory时,会触发缓存淘汰策略。

            LRU算法:Least Recently Used 最近最少使用。LFU算法:Least Frequently Uesd 最少使用频率。

            ①、noeviction:不会驱逐任何key,Redis默认的淘汰策略,内存满后报OOM。

            ②、allkeys-lru:对所有key使用LRU(Least Recently Used)算法进行删除。

            ③、volatile-lru:对所有设置了过期时间的key使用LRU算法进行删除。

            ④、allkeys-random:对所有key随机删除。

            ⑤、volatile-random:对所有设置了过期时间的key随机删除。

            ⑥、volatile-ttl:删除马上要过期的key。

            ⑦、allkeys-lfu:对所有key使用LFU算法进行删除。

            ⑧、volatile-lfu:对所有设置了过期时间的key使用LFU算法进行删除。

            ⑨、淘汰策略设置

                生产中多数使用allkeys-lru淘汰策略。

                a、修改文件配置

                b、通过命令修改

config set maxmemory- policy allkeys- lru # 设置缓存淘汰策略为allkeys- lru,设置成功返回OK
config get maxmemory- policy # 获取缓存淘汰策略配置

        (3)、LRU算法

            LRU是Least Recently Used的缩写,即最近最少使用,是一种常用的页面置换算法,选择最近最久未使用的数据予以淘汰。

            ①、利用LinkedHashMap实现LRU算法

                写操作 + 读操作时间复杂都为O(1)。

public class LRUCacheDemo<K,V> extends LinkedHashMap<K,V> {
    private int capacity;
    public LRUCacheDemo(int capacity){
        //accessOrder设置为false按照插入顺序排序(默认);为true按照访问顺序排序;
        super(capacity, 0.75f, true);
        this.capacity = capacity;
    }
    /**
     * 如果HashMap里的元素个数大于初始化容量capacity,则移除最近最少使用的元素,即LinkedHashMap的双向链表中最前面的元素
     * 重写LinkedHashMap的removeEldestEntry方法,因为LinkedHashMap是protected,所以此处不能大于protected
     */
    @Override
    protected boolean removeEldestEntry(Map.Entry<K,V> eldest){
        return super.size() > capacity;
    }

    public static void main(String[] args) {
        LRUCacheDemo lruCacheDemo = new LRUCacheDemo(3);
        lruCacheDemo.put(1, "a");
        lruCacheDemo.put(2, "b");
        lruCacheDemo.put(3, "c");
        //输出:[1, 2, 3]
        System.out.println(lruCacheDemo.keySet());
        lruCacheDemo.put(2, "b");
        //输出:[1, 3, 2]
        System.out.println(lruCacheDemo.keySet());
        lruCacheDemo.put(4, "e");
        //输出:[3, 2, 4]
        System.out.println(lruCacheDemo.keySet());
        lruCacheDemo.get(3);
        //输出:[2, 4, 3]
        System.out.println(lruCacheDemo.keySet());
        lruCacheDemo.put(5, "f");
        //输出:[4, 3, 5]
        System.out.println(lruCacheDemo.keySet());
    }
}

            ②、手写实现LRU算法

public class LRUCacheDemo {

    //Map负责查找,需要构建一个虚拟双向链表,它里面放的是一个个Node节点,作为数据载体。

    /**
     * 1、构建一个Node节点,作为数据载体
     */
    class Node<K,V> {
        K key;
        V value;
        Node<K,V> prev;
        Node<K,V> next;
        public Node(){
            this.prev = this.next = null;
        }
        public Node(K key, V value){
            this.key = key;
            this.value = value;
            this.prev = this.next = null;
        }
    }

    /**
     * 2、构建一个虚拟的双向链表,里面放置的是自定义的Node节点
     */
    class DoubleLinkedList<K,V>{
        Node<K,V> head;
        Node<K,V> tail;
        /**
         * 双向链表的构造方法
         */
        public DoubleLinkedList(){
            head = new Node<>();
            tail = new Node<>();
            head.next = tail;
            tail.prev = head;
        }
        private void addFirst(Node<K,V> node){
            node.prev = head;
            node.next = head.next;
            head.next.prev = node;
            head.next = node;
        }
        private void addLast(Node<K,V> node){
            node.prev = tail.prev;
            node.next = tail;
            tail.prev.next = node;
            tail.prev = node;
        }
        private void removeNode(Node<K,V> node){
            node.prev.next = node.next;
            node.next.prev = node.prev;
            node.prev = null;
            node.next = null;
        }

        private void removeFirst(){
            Node<K,V> node = head.next;
            head.next = node.next;
            node.next.prev = head;
            node.prev = null;
            node.next = null;
        }

        private Node getFirst(){
            return head.next;
        }

        private Node getLast(){
            return tail.prev;
        }
    }

    private int capacity;
    Map<Integer, Node<Integer, Integer>> map;
    DoubleLinkedList<Integer, Integer> doubleLinkedList;

    public LRUCacheDemo(int capacity){
        this.capacity = capacity;
        map = new HashMap<>();
        doubleLinkedList = new DoubleLinkedList<>();
    }

    public Integer get(int key){
        if(!map.containsKey(key)){
            return -1;
        }
        Node<Integer, Integer> node = map.get(key);
        doubleLinkedList.removeNode(node);
        doubleLinkedList.addLast(node);
        return node.value;
    }

    public void put(int key, int value){
        if(map.containsKey(key)){
            Node<Integer, Integer> updateNode = map.get(key);
            updateNode.value = value;
            map.put(key, updateNode);
            doubleLinkedList.removeNode(updateNode);
            doubleLinkedList.addLast(updateNode);
        }else{
            if(map.size() == capacity){
                Node<Integer, Integer> removeNode = doubleLinkedList.getFirst();
                map.remove(removeNode.key, removeNode);
                doubleLinkedList.removeNode(removeNode);
            }
            Node<Integer,Integer> addNode = new Node<>(key, value);
            map.put(key, addNode);
            doubleLinkedList.addLast(addNode);
        }
    }
    
    public static void main(String[] args) {
        LRUCacheDemo lruCacheDemo = new LRUCacheDemo(3);
        lruCacheDemo.put(1, 1);
        lruCacheDemo.put(2, 2);
        lruCacheDemo.put(3, 3);
        //输出:[1, 2, 3]
        System.out.println(lruCacheDemo.map.keySet());
        lruCacheDemo.get(1);
        lruCacheDemo.put(5, 5);
        //输出:[1, 3, 5] 不知道为啥map的顺序错误,但是doubleLinkedList的顺序是正确的
        System.out.println(lruCacheDemo.map.keySet());
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值