zoukankan      html  css  js  c++  java
  • redis过期策略以及内存淘汰机制(理论+配置)

    一、redis的过期策略:

    redis的过期策略是:定期删除+惰性删除
    redis在存储数据时,可能会设置过期时间,而所谓的定期删除,指的是redis默认是每隔100ms就随机抽取一些设置了过期时间的key进行检查,如果过期了就会删除。
    至于为啥是每隔100ms随机抽取一些数据进行检查而不是全部检查,这就与cpu负载有关了,如redis中的数据十分庞大,并且全部都设置了过期时间,依次全部检查并且进行删除的话负载太高,影响性能。
    但是,由于是随机抽取的key进行检查进行删除,那么很多的key可能会到了过期时间了还没进行删除,那么怎么办呢?这时候,惰性删除就会发挥作用了,所谓的惰性删除,就是在读取某个key的时候,redis会先检查一个该key是否过期,如果过期了,就会在此时删除,然后不会给你返回任何东西。
    但是此时就会产生另外一个问题,假如一些key设置了过期时间,而定期删除的随机抽取没有选中这些key,而恰好也没有人去获取这些key,惰性删除也发挥不了作用了,那么这些数据就会越积累越多,redis一般作为缓存的,是基于内存的,这些数据越来越多的时候回导致内存耗尽,影响性能,这时候应该怎么办呢?这时候,另一个重量型的武器就要发挥作用了,那就是:内存淘汰机制


     二、内存淘汰机制:

    redis 内存淘汰机制(内存淘汰策略)有以下几个:
    • noeviction: 当内存不足以容纳新写入数据时,新写入操作会报错,这个一般没人用吧,实在是太恶心了。
    • allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的 key(这个是最常用的)。
    • allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个 key,这个一般没人用吧,为啥要随机,肯定是把最近最少使用的 key 给干掉啊。
    • volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的 key(这个一般不太合适)。
    • volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个 key。
    • volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的 key 优先移除。

    但是理论性的东西我们明白了,那么该如何配置内存淘汰策略呢?


    三、内存淘汰策略的配置

    我们通过配置redis.conf中的maxmemory这个值来开启内存淘汰功能。

    # maxmemory

    值得注意的是,maxmemory为0的时候表示我们对Redis的内存使用没有限制。

    根据应用场景,选择淘汰策略

    # maxmemory-policy noeviction

    我们来了解下,内存淘汰的过程

    首先,客户端发起了需要申请更多内存的命令(如set)。

    然后,Redis检查内存使用情况,如果已使用的内存大于maxmemory则开始根据用户配置的不同淘汰策略来淘汰内存(key),从而换取一定的内存。

    最后,如果上面都没问题,则这个命令执行成功。

    动态改配置命令

    此外,redis支持动态改配置,无需重启。

    设置最大内存

    config set maxmemory 100000

    设置淘汰策略

    config set maxmemory-policy noeviction

    如何选择淘汰策略

    下面看看几种策略的适用场景

    allkeys-lru:如果我们的应用对缓存的访问符合幂律分布,也就是存在相对热点数据,或者我们不太清楚我们应用的缓存访问分布状况,我们可以选择allkeys-lru策略。

    allkeys-random:如果我们的应用对于缓存key的访问概率相等,则可以使用这个策略。

    volatile-ttl:这种策略使得我们可以向Redis提示哪些key更适合被eviction。

    另外,volatile-lru策略和volatile-random策略适合我们将一个Redis实例既应用于缓存和又应用于持久化存储的时候,然而我们也可以通过使用两个Redis实例来达到相同的效果,值得一提的是将key设置过期时间实际上会消耗更多的内存,因此我们建议使用allkeys-lru策略从而更有效率的使用内存。


    四、设置过期时间

    redis有四种命令可以用于设置键的生存时间和过期时间:

    EXPIRE <KEY> <TTL> : 将键的生存时间设为 ttl 秒
    PEXPIRE <KEY> <TTL> :将键的生存时间设为 ttl 毫秒
    EXPIREAT <KEY> <timestamp> :将键的过期时间设为 timestamp 所指定的秒数时间戳
    PEXPIREAT <KEY> <timestamp>: 将键的过期时间设为 timestamp 所指定的毫秒数时间戳.

    当然,平时我们也可以使用java代码的方式进行过期时间的设置,一些工具类可以直接满足要求,在这里,我就提供一个工具类:

     import org.springframework.beans.factory.annotation.Autowired;
        import org.springframework.data.redis.core.RedisTemplate;
        import org.springframework.stereotype.Component;
        import org.springframework.util.CollectionUtils;
        import java.util.List;
        import java.util.Map;
        import java.util.Set;
        import java.util.concurrent.TimeUnit;

        /**
         * redis的工具类
         * @author zangchuanlei
         * @date 2019.09.18
         */
        @Component
        public final class RedisUtil {

        @Autowired
        private RedisTemplate<String,Object> redisTemplate;

            /**
             * 指定缓存失效的时间
             * @param key 键
             * @param time 时间(秒)
             * @return
             */
        public boolean expire(String key,long time){
            try {
                if(time > 0){
                    redisTemplate.expire(key,time,TimeUnit.SECONDS);
                }
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }

        /**
             * 根据key获取过期时间
             * @param key 键 不能为null
             * @return 时间(秒)返回0代表为永久有效
         */
        public long getExpire(String key){
            return redisTemplate.getExpire(key,TimeUnit.SECONDS);
        }

            /**
             * 判断key是否存在
             * @param key 键
             * @return true 存在 false 不存在
             */
        public boolean hasKey(String key){
            try {
                return redisTemplate.hasKey(key);
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }

            /**
             * 删除缓存
             * @param key 可以传一个值或者多个值
             */
        public void del(String... key){
            if(key != null && key.length > 0){
                if(key.length == 1){
                    redisTemplate.delete(key[0]);
                }else{
                    redisTemplate.delete(CollectionUtils.arrayToList(key));
                }
            }
        }

        //=========================String======================
            /**
             *普通缓存获取
             * @param key 键
             * @return 值
             */
        public Object get(String key){
            return key == null ? null : redisTemplate.opsForValue().get(key);
        }

            /**
             * 普通缓存的放入
             * @param key
             * @param value
             * @return
             */
        public boolean set(String key, Object value){
            try {
                redisTemplate.opsForValue().set(key,value);
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }

            /**
             * 普通缓存放入并设置时间
             * @param key 键
             * @param value 值
             * @param time 时间(秒) time要大于0,如果time要是小于0,将设置无限期
             * @return true成功 false 失败
             */
        public boolean set(String key,Object value,long time){
            try {
                if(time > 0){
                    redisTemplate.opsForValue().set(key,value,time,TimeUnit.SECONDS);
                }else{
                    set(key,value);
                }
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }

            /**
             * 递增
             * @param key 键
             * @param delta 要增加几(大于0)
             * @return
             */
        public long incr(String key,long delta){
            if(delta < 0){
                throw  new RuntimeException("增强因子必须大于0");
            }
            return redisTemplate.opsForValue().increment(key,delta);
        }

            /**
             * 递减
             * @param key 键
             * @param delta 要减少几(大于0)
             * @return
             */
        public long decr(String key,long delta){
            if(delta < 0){
                throw  new RuntimeException("递减因子必须大于0");
            }
            return redisTemplate.opsForValue().increment(key,-delta);
        }

            /**
             * HashGet
             * @param key 键 不能为null
             * @param item 项 不能为null
             * @return 值
             */
        public Object hget(String key,String item){
            return redisTemplate.opsForHash().get(key,item);
        }

            /**
             * 获取hashKey对应的所有的键值
             * @param key 键
             * @return 对应的多个键值
             */
        public Map<Object,Object> hmget(String key){
            return redisTemplate.opsForHash().entries(key);
        }

            /**
             * hashSet
             * @param key 键
             * @param map 对应多个键值
             * @return true成功 false 失败
             */
        public boolean hmset(String key,Map<String,Object> map){
            try {
                redisTemplate.opsForHash().putAll(key,map);
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }
        
            /**
             * HashSet并设置时间
             * @param key 键
             * @param map 对应多个键值
             * @param time 时间秒
             * @return true 成功 false 失败
             */
        public boolean hmset(String key,Map<String,Object> map,long time){
            try {
                redisTemplate.opsForHash().putAll(key,map);
                if(time > 0){
                    expire(key,time);
                }
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }

            /**
             * 向一张hash表中放入数据,如果不存在将创建
             * @param key 键
             * @param item 项
             * @param value 值
             * @return true成功 false失败
             */
        public boolean hset(String key,String item,Object value){
            try {
                redisTemplate.opsForHash().put(key,item,value);
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }

            /**
             * 向一张hash表中放入数据,如果不存在将创建
             * @param key 键
             * @param item 项
             * @param value 值
             * @param time 时间(秒) 注意:如果已经存在的hash表有时间,这里将会替换原有的时间
             * @return true成功 false 失败
             */
        public boolean hset(String key,String item,Object value,long time){
            try {
                redisTemplate.opsForHash().put(key,item,value);
                if(time > 0){
                    expire(key,time);
                }
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }
        
            /**
             * 删除hash表中的值
             * @param key 键 不能为null
             * @param item 项 可以使用多个 但不能为null
             */
        public void hdel(String key,Object... item){
            redisTemplate.opsForHash().delete(key,item);
        }

            /**
             * 判断hash表中是否有该项的值
             * @param key 键 不能为null
             * @param item 项 不能为null
             * @return true 存在 false 不存在
             */
        public boolean hHasKey(String key,String item){
            return redisTemplate.opsForHash().hasKey(key,item);
        }

            /**
             * hash递增 如果不存在,就会创建一个 把新增后的值返回
             * @param key 键
             * @param item 项
             * @param by 要增加几(大于0)
             * @return
             */
        public double hincr(String key,String item,double by){
            return redisTemplate.opsForHash().increment(key,item,by);
        }

            /**
             * hash递减 如果不存在,就会创建一个 把新增后的值返回
             * @param key 键
             * @param item 项
             * @param by 要减少几(大于0)
             * @return
             */
        public double hdecr(String key,String item,double by){
            return redisTemplate.opsForHash().increment(key,item,-by);
        }

        //==========================set====================

            /**
             * 根据key获取set中的所有值
             * @param key 键
             * @return
             */
        public Set<Object> sGet(String key){
            try {
                return redisTemplate.opsForSet().members(key);
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }
        
            /**
             * 根据value从一个set中查询,是否存在
             * @param key 键
             * @param value 值
             * @return true存在 false不存在
             */
        public boolean sHashKey(String key,Object value){
            try {
                return redisTemplate.opsForSet().isMember(key,value);
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }

            /**
             * 将数据放入set缓存
             * @param key 键
             * @param values 值 可以是多个
             * @return 成功个数
             */
        public long sSet(String key,Object... values){
            try {
                return redisTemplate.opsForSet().add(key,values);
            } catch (Exception e) {
                e.printStackTrace();
                return 0;
            }
        }

            /**
             * 将set数据放入缓存并设置失效时间
             * @param key 键
             * @param time 时间(秒)
             * @param values 值 可以是多个
             * @return 成功个数
             */
        public long sSetAndTime(String key,long time,Object... values){
            try {
                Long count = redisTemplate.opsForSet().add(key,values);
                if(time > 0){
                    expire(key,time);
                }
                return count;
            } catch (Exception e) {
                e.printStackTrace();
                return 0;
            }
        }

            /**
             * 获取set缓存的长度
             * @param key 键
             * @return
             */
        public long sGetSetSize(String key){
            try {
                return redisTemplate.opsForSet().size(key);
            } catch (Exception e) {
                e.printStackTrace();
                return 0;
            }
        }

            /**
             * 移除值为value的
             * @param key 键
             * @param values 值 可以是多个
             * @return 移除的个数
             */
        public long setRemove(String key,Object... values){
            try {
                Long count = redisTemplate.opsForSet().remove(key,values);
                return count;
            } catch (Exception e) {
                e.printStackTrace();
                return 0;
            }
        }

        //===================list====================

            /**
             * 获取list缓存的内容
             * @param key 键
             * @param start 开始
             * @param end 结束 0到-1代表所有值
             * @return
             */
        public List<Object> lGet(String key,long start,long end){
            try {
                return redisTemplate.opsForList().range(key,start,end);
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }

            /**
             * 获取list缓存的长度
             * @param key 键
             * @return
             */
        public long lGetListSize(String key){
            try {
                return redisTemplate.opsForList().size(key);
            } catch (Exception e) {
                e.printStackTrace();
                return 0;
            }
        }

            /**
             * 通过索引 获取list中的值
             * @param key 键
             * @param index 索引 index>=0, 0表头,1 第二个元素 ;
             *              索引index<0时,-1,表尾,-2倒数第二个元素 以此类推
             * @return
             */
        public Object iGetIndex(String key,long index){
            try {
                return redisTemplate.opsForList().index(key,index);
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }

            /**
             * 将list放入缓存
             * @param key 键
             * @param value 值
             * @return
             */
        public boolean lSet(String key,Object value){
            try {
                redisTemplate.opsForList().rightPush(key,value);
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }

        /**
         * 将list放入缓存
         * @param key 键
         * @param value 值
         * @param time 时间(秒)
         * @return
         */
        public boolean lSet(String key,Object value,long time){
            try {
                redisTemplate.opsForList().rightPush(key,value);
                if(time > 0){
                    expire(key,time);
                }
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }

        /**
         * 将list放入缓存
         * @param key 键
         * @param value 值
         * @return
         */
        public boolean lSet(String key,List<Object> value){
            try {
                redisTemplate.opsForList().rightPushAll(key,value);
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }

        /**
         * 将list放入缓存
         * @param key 键
         * @param value 值
         * @param time 时间(秒)
         * @return
         */
        public boolean lSet(String key,List<Object> value,long time){
            try {
                redisTemplate.opsForList().rightPushAll(key,value);
                if(time > 0){
                    expire(key,time);
                }
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }

        /**
         * 根据索引修改list中的某条数据
         * @param key 键
         * @param index 索引
         * @param value 值
         * @return
         */
        public boolean lUpdateIndex(String key,long index,Object value){
            try {
                redisTemplate.opsForList().set(key,index,value);
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }

        /**
         * 移除N个值为value
         * @param key 键
         * @param count 移除多少个
         * @param value 值
         * @return 移除的个数
         */
        public long lRemove(String key,long count,Object value){
            try {
                Long remove = redisTemplate.opsForList().remove(key,count,value);
                return remove;
            } catch (Exception e) {
                e.printStackTrace();
                return 0;
            }
        } }



    参考原文链接:https://blog.csdn.net/baidu_26954625/article/details/90648597

  • 相关阅读:
    Android应用插件式开发解决方法
    给windows服务打包,并生成安装程序
    如何在VS2013中新建WindowsService定时任务
    从源代码分析Android-Universal-Image-Loader图片下载技巧
    Android内存溢出解决方案(OOM)
    Android网络传输中必用的两个加密算法:MD5 和 RSA (附java完成测试代码)
    Volley的基本用法
    Redis 5种数据类型,2种特殊数据处理策略
    SiteWhere物联网云平台架构
    杭电1276 士兵队列训练问题
  • 原文地址:https://www.cnblogs.com/zaevn00001/p/12396242.html
Copyright © 2011-2022 走看看