• redis详细教程


    一、简介

    1. Redis是流行NoSQL数据库之一,并且是单线程的(多线程cpu上下文会切换,耗时),基于内存,cpu不是内存瓶颈,而是机器的内存和网络带宽。 因为redis的数据是存于内存中的,对于内存系统来说,如果没有上下文切换,效率就是最高的。多次读写都是写在一个cpu上,在内存情况下,这个是最佳的方案。官方统计 redis每秒读11w,写8w。是用c写的,不比memecache差。
    2. linux默认使用16个数据库,使用RDM软件可以看到16个库
    3. 五大基本数据类型:list、set、String、Hash、zset

    二、安装教程

    https://blog.csdn.net/weixin_40496191/article/details/121028500

    三、redis常用操作(命令行)

    1. String
      1)切换库:select 1,2,3…
      2)设置键值对: set key value
      3)设置键值对(不存在才会成功):setnx key value
      4)设置键值对(带过期时间):setex key 10 value
      5)设置键值对(批量):mset key1 value1 key2 value2
      6)查询键值:get key
      7)查询键值(批量):mget key1 key2
      4)查看库里的键情况:keys *
      5)查看库里的大小:dbsize
      6) 清空当前数据库:flushdb
      7) 清空所有数据库:flushall
      8) 查看键是否存在:exists key
      9) 删除:del key
      10) 设置过期时间:expire key 10(单位s)
      11) 查看剩余时间:ttl key
      12) 追加值,key不存在则新建:append key value
      13) 查看长度:strlen key
      14) 加1,比如浏览量:incr counts
      15) 减1:decr counts
      16) 加10:incrby key 10
      17) 截取:GETRANGE key start end(0 -1是全部)
      18) 替换:zhanzhk —》setrange key 1 555–》z555zhk
      19) 取出并且设置,值取原来:getset key value
      20) 存对象:mset zk:1 {name1:zhanzhk1,age:8}
      21) 取对象:mget zk:1
      22) 存对象(可以实现复用,比如mset 好文章:文章1:浏览量):mset zk:1:name1 zhanzhk1 zk:1:age 8 。
      23) 取对象:mget zk:1:name1 zk:1:age

    2. list(元素可重复)

      l开头:集合的进出顺序跟栈一样,先进后出。
      r开头:先进先出
      1) 查看集合元素:lrange key 0 -1
      2) 插入集合元素到第一位:lpush key value
      3) 插入集合元素到最后一位:rpush key value
      4) 删除第一位元素:lpop key
      5) 删除最后一位元素:rpop key
      6) 下标查找元素:lindex key 0
      7) 长度:llen key
      8) 移除指定元素的个数:lrem key 数量 元素
      9) 截取:ltrim key 0 1
      10) 移除并且添加到其他集合:rpoplpush key1 key2
      11) 是否存在集合:exists list
      12) 替换值:lset key index value
      13) 往某个元素前后插入值:linsert key before/after 集合元素 新元素
      14) 实际上算是个链表结构,往收尾插入效率最高,往中间插入效率低。

    3. set(无序不重复集合)

      1) 集合添加元素:sadd key value
      2) 查看集合元素:smembers key
      3) 判断是否存在某个元素:sismember key value
      4) 查看元素个数:scard key
      5) 移除元素:srem key value
      6) 随机抽取:srandmember key count(不写默认1个)
      7) 删除指定元素:srem key value
      8) 移动指定元素到另一个集合:smove key key2 value
      9) 以第一个key为基础,查看第二个key和第一个的差集:sdiff key1 key2
      10) 以第一个key为基础,查看第二个key和第一个的交集:sinter key1 key2(共同好友)
      11) 并集:sunion key1 key2

    4. zset(有序不重复集合)

      1)添加:zadd key value key1
      2)查看:zrange key 0 -1
      3)按照序号排序:zrangebyscore key -inf 5
      4) 按照序号排序(无穷大):zrangebyscore key -inf +inf
      5) 按照序号排序陪参数:zrangebyscore key -inf +inf withscores
      6) 移除元素:zrem key zzk10
      7) 查看数量:zcard key
      8) 查看区间数量:zcount key 0 100

    5. hash(当做map)

      1) 添加元素: hset key key1 value1 key2 value2
      2) 查看元素:hget key key1
      3) 查看所有元素:hgetall key
      4) 删除集合元素:hdel key key1
      5) 查看集合长度:hlen key key1
      6) 查看某个元素是否存在:hexists key key1
      7) 查看所有的key:hkeys key
      8) 查看所有的值:hvals key
      9) 某个属性自增:hincrby key key1 1
      10) key不存在创建,存在不创建:hsetnx key key1 value

    6. genpos(地图)

      1) 新增:geoadd china:city 精度 纬度 地点名
      2) 距离:geodist china:city 地点名 地点名
      3) 距离1:geodist china:city 地点名 地点名 km
      4) 扫描半径:georadius china:city 经度 纬度 5000 km
      5) 扫描半径,经纬度指定数量:georadius china:city 经度 纬度 5000 km withdist withcoord count 数量
      6) 找出某个城市周边的城市:georadiusbymember china:city 地点名 400 km
      7) 可以用zset命令查看:zrange china:city 0 -1

    7. hyperloglog(允许容错)

      1. 添加元素:pfadd hype1 a b c pfadd hype2 a b c
      2. 合并并集:pfmerage hype3 hype1 hype2
      3. 查看数量:pfcount hype1
    8. bitmaps

      1. 都是操作二进制位进行记录,只有0和1两种状态,只有两种状态的情况可以使用
      2. 添加打卡记录(一周打卡例子):setbit sign 1 0 setbit sign 2 0 setbit sign 3 1
      3. 获取打卡:getbit sign 1
      4. 统计打卡记录:bitcount sign

    四、redis常用操作(java)

    1. string
    package redis.jedis;
    
    import redis.clients.jedis.Jedis;
    
    import java.util.Set;
    
    /**
     * @author 天真热
     * @create 2021-09-25 20:47
     * @desc
     **/
    public class Redis_String {
        public static void main(String[] args) {
            Jedis jedis = new Jedis("192.168.248.11", 6379);
            //设置键值对
            jedis.set("name", "zzk");
            //设置键值对(不存在才会成功)
            jedis.setnx("name", "zzk");
            //设置键值对(带过期时间)
            jedis.setex("name", 10, "zzk");
            //查询键值
            String name = jedis.get("name");
            //查看库里的键情况
            Set<String> all = jedis.keys("*");
            //查看库里的大小
            Long size = jedis.dbSize();
            //清空当前数据库
            jedis.flushDB();
            //清空所有数据库
            jedis.flushAll();
            //查看键是否存在
            Boolean exist = jedis.exists("name");
            //删除元素
            jedis.del("name");
            //设置过期时间
            jedis.expire("name", 10);
            //查看剩余时间
            jedis.ttl("name");
            //追加值,key不存在则新建
            jedis.append("name", "zzk");
            //查看长度
            jedis.strlen("name");
            //加1,比如浏览量
            jedis.incr("age");
            //减1
            jedis.decr("age");
            //加10
            jedis.incrBy("age", 10);
            //截取
            jedis.getrange("name", 0, -1);
            //截取
            jedis.setrange("name", 1, "zhanzhk");
            //取出并且设置,值取原来
            jedis.getSet("name", "zahznhk");
            //存对象
            jedis.mset("zk:1", "{name1:zhanzhk1,age:8}");
            //取对象
            jedis.get("zk:1");
    
            jedis.close();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    1. list(元素可重复)
    package redis.jedis;
    
    import redis.clients.jedis.Jedis;
    import redis.clients.jedis.ListPosition;
    
    /**
     * @author 天真热
     * @create 2021-09-25 20:47
     * @desc
     **/
    public class Redis_list {
        public static void main(String[] args) {
            //l开头:集合的进出顺序跟栈一样,先进后出。
            //r开头:先进先出
            //实际上算是个链表结构,往收尾插入效率最高,往中间插入效率低。
            Jedis jedis = new Jedis("192.168.248.11", 6379);
            //查看集合元素
            jedis.lrange("name", 0, -1);
            //插入集合元素到第一位
            jedis.lpush("name", "zzk");
            //插入集合元素到最后一位
            jedis.rpush("name", "zzk");
            //删除第一位元素
            jedis.lpop("name");
            //下标查找元素
            jedis.lindex("name", 1);
            //长度
            jedis.llen("name");
            //移除指定元素的数量
            jedis.lrem("name", 10, "zzk");
            //截取
            jedis.ltrim("name", 0, 1);
            //移除并且添加到其他集合
            jedis.rpoplpush("name", "name1");
            //是否存在集合
            jedis.exists("name");
            //替换值
            jedis.lset("name", 1, "zzk");
            //往某个元素前后插入值
            jedis.linsert("name", ListPosition.BEFORE, "zzk", "zhanzhk");
            jedis.linsert("name", ListPosition.AFTER, "zzk", "zhanzhk");
    
            jedis.close();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    1. set(无序不重复集合)
    package redis.jedis;
    
    import redis.clients.jedis.Jedis;
    
    /**
     * @author 天真热
     * @create 2021-09-25 20:47
     * @desc
     **/
    public class Redis_set {
        public static void main(String[] args) {
            Jedis jedis = new Jedis("192.168.248.11", 6379);
            //集合添加元素
            jedis.sadd("name", "zzk");
            //查看集合元素
            jedis.smembers("name");
            //判断是否存在某个元素
            jedis.sismember("name", "zzk");
            //查看元素个数
            jedis.scard("name");
            //移除元素
            jedis.srem("name", "zzk");
            //随机抽取,不写默认1个
            jedis.srandmember("name", 2);
            //移动指定元素到另一个集合
            jedis.smove("name", "name1", "zzk");
            //以第一个key为基础,查看第二个key和第一个的差集
            jedis.sdiff("name", "name1");
            //以第一个key为基础,查看第二个key和第一个的交集
            jedis.sinter("name", "name1");
            //并集
            jedis.sunion("name", "name1");
    
            jedis.close();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    1. zset(有序不重复集合)
    package redis.jedis;
    
    import redis.clients.jedis.Jedis;
    
    /**
     * @author 天真热
     * @create 2021-09-25 20:47
     * @desc
     **/
    public class Redis_zset {
        public static void main(String[] args) {
            Jedis jedis = new Jedis("192.168.248.11", 6379);
            //添加
            jedis.zadd("name", 1, "zzk");
            //查看
            jedis.zrange("name", 0, -1);
            //按照序号排序
            jedis.zrangeByScore("name", 0, -1);
            //按照序号排序陪参数:zrangebyscore key -inf + inf withscores
            //查看数量
            jedis.zcard("name");
            //查看区间数量
            jedis.zcount("name", 0, 100);
    
            jedis.close();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    1. hash(当做map)
    package redis.jedis;
    
    import redis.clients.jedis.Jedis;
    
    import java.util.List;
    import java.util.Map;
    import java.util.Set;
    
    /**
     * @author 天真热
     * @create 2021-09-25 20:47
     * @desc
     **/
    public class Redis_hash {
        public static void main(String[] args) {
            Jedis jedis = new Jedis("192.168.248.11", 6379);
            //添加元素
            jedis.hset("name", "type1", "zzk");
            //查看元素
            String type1 = jedis.hget("name", "type1");
            //查看所有元素
            Map<String, String> all = jedis.hgetAll("name");
            //删除集合元素
            jedis.hdel("name", "type1");
            //查看集合长度
            jedis.hlen("name");
            //查看某个元素是否存在
            jedis.hexists("name", "type1");
            //查看所有的key
            Set<String> allkey = jedis.hkeys("name");
            //查看所有的值
            List<String> allvalue = jedis.hvals("name");
            //某个属性自增
            jedis.hincrBy("name", "age", 1);
            //key不存在创建,存在不创建
            jedis.hsetnx("name", "type1", "zzk");
    
            jedis.close();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    1. genpos
    package redis.jedis;
    
    import redis.clients.jedis.GeoUnit;
    import redis.clients.jedis.Jedis;
    import redis.clients.jedis.params.GeoRadiusParam;
    
    /**
     * @author 天真热
     * @create 2021-09-25 20:47
     * @desc
     **/
    public class Redis_genpos {
        public static void main(String[] args) {
            Jedis jedis = new Jedis("192.168.248.11", 6379);
            //新增
            jedis.geoadd("china:city", 125.01, 95.25, "beijing");
            jedis.geoadd("china:city", 122.01, 91.25, "shanghai");
            jedis.geoadd("china:city", 120.01, 88.25, "chongqing");
            //距离
            jedis.geodist("china:city", "beijing", "shanghai");
            //距离指定带单位
            jedis.geodist("china:city", "beijing", "shanghai", GeoUnit.KM);
            //扫描半径
            jedis.georadius("china:city", 125.01, 95.25, 5000, GeoUnit.KM);
            //扫描半径,经纬度指定数量
            jedis.georadius("china:city", 125.01, 95.25, 5000, GeoUnit.KM, new GeoRadiusParam().count(2));
            //找出某个城市周边的城市
            jedis.georadiusByMember("china:city", "chongqing", 5000, GeoUnit.KM);
            //可以用zset命令查看
            jedis.zrange("china:city", 0, -1);
    
            jedis.close();
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    1. hyperloglog(允许容错)
    package redis.jedis;
    
    import redis.clients.jedis.Jedis;
    
    /**
     * @author 天真热
     * @create 2021-09-25 20:47
     * @desc
     **/
    public class Redis_hyperloglog {
        public static void main(String[] args) {
            Jedis jedis = new Jedis("192.168.248.11", 6379);
            //添加元素
            jedis.pfadd("name", "zzk");
            jedis.pfadd("name", "zzk1");
            jedis.pfadd("name", "zzk2");
            jedis.pfadd("name1", "zzk2");
            jedis.pfadd("name1", "zzk3");
            jedis.pfadd("name1", "zzk4");
            //合并并集
            jedis.pfmerge("name", "name1");
            //查看数量
            jedis.pfcount("name");
    
            jedis.close();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    1. bitmaps
    package redis.jedis;
    
    import redis.clients.jedis.Jedis;
    
    /**
     * @author 天真热
     * @create 2021-09-25 20:47
     * @desc
     **/
    public class Redis_bitmaps {
        public static void main(String[] args) {
            // 都是操作二进制位进行记录,只有0和1两种状态,只有两种状态的情况可以使用
            Jedis jedis = new Jedis("192.168.248.11", 6379);
            //添加打卡记录(一周打卡例子)
            jedis.setbit("sign", 1, true);
            jedis.setbit("sign", 2, true);
            jedis.setbit("sign", 3, false);
            jedis.setbit("sign", 4, true);
            jedis.setbit("sign", 5, false);
            jedis.setbit("sign", 6, true);
            jedis.setbit("sign", 7, true);
            //获取打卡
            jedis.getbit("sign", 1);
            //统计打卡记录
            jedis.bitcount("sign");
    
            jedis.close();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    五、redis整合springboot

    pom文件

     <dependencies>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
    
    
            <!-- lombok -->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
            </dependency>
    
    
            <dependency>
                <groupId>redis.clients</groupId>
                <artifactId>jedis</artifactId>
                <version>3.2.0</version>
            </dependency>
    
    
            <!-- redis -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-redis</artifactId>
            </dependency>
        </dependencies>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    配置文件

    server:
      port: 8090
    
    spring:
      redis:
        host: 192.168.248.11
        port: 6379
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    配置类

    package redis.redis.config;
    
    import com.fasterxml.jackson.annotation.JsonAutoDetect;
    import com.fasterxml.jackson.annotation.PropertyAccessor;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import org.springframework.beans.factory.annotation.Configurable;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.data.redis.connection.RedisConnectionFactory;
    import org.springframework.data.redis.connection.StringRedisConnection;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
    import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
    import org.springframework.data.redis.serializer.StringRedisSerializer;
    
    import java.net.UnknownHostException;
    
    /**
     * @author 天真热
     * @create 2021-09-27 21:35
     * @desc
     **/
    @Configuration
    public class RedisConfig {
    
        /**
         * 自定义RedisTemplate
         *
         * @param redisConnectionFactory
         * @return
         * @throws UnknownHostException
         */
        @Bean
        @SuppressWarnings("all")
        public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
            //为了开发方便,一般使用<String, Object>类型
            RedisTemplate<String, Object> template = new RedisTemplate();
            //连接工厂,默认
            template.setConnectionFactory(redisConnectionFactory);
            //Json序列化配置
            Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
            //使用ObjectMapper进行转义
            ObjectMapper om = new ObjectMapper();
            om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
            om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
            jackson2JsonRedisSerializer.setObjectMapper(om);
            //String序列化配置
            StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
            //key采用String方式序列化
            template.setKeySerializer(stringRedisSerializer);
            //hash的key也采用String的序列化方式
            template.setHashKeySerializer(stringRedisSerializer);
            //value序列化采用jackson
            template.setValueSerializer(jackson2JsonRedisSerializer);
            //hash的value序列化采用jackson
            template.setHashValueSerializer(jackson2JsonRedisSerializer);
    
            template.afterPropertiesSet();
    
            return template;
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65

    redis调用工具类

    package redis.redis.utils;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.data.redis.core.*;
    import org.springframework.stereotype.Component;
    
    import javax.annotation.Resource;
    import java.io.Serializable;
    import java.util.Date;
    import java.util.List;
    import java.util.Set;
    import java.util.concurrent.TimeUnit;
    
    @Component
    public class RedisUtil {
        @Autowired
        @Qualifier("redisTemplate")
        public static RedisTemplate redisTemplate;
    
        @Resource
        public void setRedisTemplate(RedisTemplate redisTemplate) {
            this.redisTemplate = redisTemplate;
        }
    
    
        //**********************************String类型,按需编写工具类***************************************
        public static void StringSetMethod() {
            ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
            valueOperations.set("name", "zzk");
        }
    
        public static String StringGetMethod() {
            ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
            return objToString(valueOperations.get("name"));
        }
    
        //**********************************list类型,按需编写工具类***************************************
        public static void listMethod() {
            ListOperations listOperations = redisTemplate.opsForList();
            listOperations.leftPush("name", "zzk");
        }
    
        //**********************************set类型,按需编写工具类***************************************
        public static void setMethod() {
            SetOperations setOperations = redisTemplate.opsForSet();
            setOperations.add("name", "zzk");
        }
    
        //**********************************zset类型,按需编写工具类***************************************
        public static void zsetMethod() {
            ZSetOperations zsetOperations = redisTemplate.opsForZSet();
            zsetOperations.add("name", 1, 1d);
        }
    
        //**********************************hsah类型,按需编写工具类***************************************
        public static void hashMethod() {
            HashOperations hashOperations = redisTemplate.opsForHash();
            hashOperations.put("name", "type1", "zzk");
        }
    
        public static String objToString(Object obj) {
            if (obj == null) {
                return "";
            } else {
                return obj.toString();
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70

    六、redis配置文件详细

    # =================单位定义、大小写不敏感=================
    # 1k => 1000 bytes
    # 1kb => 1024 bytes
    # 1m => 1000000 bytes
    # 1mb => 1024*1024 bytes
    # 1g => 1000000000 bytes
    # 1gb => 1024*1024*1024 bytes
    
    #=================包含其他配置文件=================
    # include /path/to/local.conf
    # include /path/to/other.conf
    
    #=================加载so文件=================
    # loadmodule /path/to/my_module.so
    # loadmodule /path/to/other_module.so
    
    #=================网络配置=================
    bind 0.0.0.0 				#绑定ip
    protected-mode yes			#是否受保护
    port 6379					#端口
    tcp-backlog 511
    timeout 0
    tcp-keepalive 300
    
    #=================通用配置=================
    daemonize yes				#是否守护进行启动
    supervised no				#是否指定监控
    pidfile /var/run/redis_6379.pid  #如果以守护进程方式运行时,默认Redis写入进程id的文件路径
    loglevel notice				#日志级别
    logfile ""					#指定日志文件位置名
    databases 16				#默认redis数据库数量
    always-show-logo yes		#是否显示redis logo
    
    #=================快照(没有持久化则数据会丢失)=================
    save 900 1					#表示900 秒内如果至少有 1 个 key 的值变化,则保存
    save 300 10					#表示300 秒内如果至少有 10 个 key 的值变化,则保存
    save 60 10000				#表示60 秒内如果至少有 10000 个 key 的值变化,则保存
    stop-writes-on-bgsave-error yes		#持久化出错是否继续工作
    rdbcompression yes			#是否压缩rdb文件,将消耗cpu资源
    rdbchecksum yes				#保存rdb文件时,进行错误校验
    dbfilename dump.rdb			#数据持久化到磁盘的文件名
    dir ./						#设置快照文件的存放路径
    
    #=================主从复制=================
    # replicaof <masterip> <masterport>			#当设置本机为从服务器时,设置master服务的IP地址及端口,在Redis启动时,它会自动从master进行数据同步
    # masterauth <master-password>				#当master服务设置了密码保护时,从服务器连接master的密码
    replica-serve-stale-data yes				#当主从连接中断,或者主从复制建立期间,是否允许从服务器对外提供服务。默认为yes,即允许对外提供服务,但有可能读到脏数据。
    replica-read-only yes						#将从服务器设置为只读模式。需要注意,只读模式针对的只是客户端的写操作,对管理命令无效。
    repl-diskless-sync no						#是否使用无盘复制。为了降低主节点磁盘开销,Redis支持无盘复制,生成的RDB文件不保存到磁盘而是直接通过网络发送给从节点。无盘复制适用于主节点所在机器磁盘性能较差但网络宽带较充裕的场景。需要注意的是,无盘复制目前依然处于实验阶段。
    repl-diskless-sync-delay 5					#master每隔一段固定的时间向从服务器发送一个PING命令
    # repl-ping-replica-period 10				#master每隔一段固定的时间向从服务器发送一个PING命令
    # repl-timeout 60							#复制超时时间。
    repl-disable-tcp-nodelay no					#设置为yes,主节点会等待一段时间才发送TCP数据包,具体等待时间取决于Linux内核,一般是40毫秒。适用于主从网络环境复杂或带宽紧张的场景。默认为no。
    # repl-backlog-size 1mb						#复制积压缓冲区,复制积压缓冲区是保存在主节点上的一个固定长度的队列。用于从Redis 2.8开始引入的部分复制。
    # repl-backlog-ttl 3600						#如果master上的从服务器全都断开了,且在指定的时间内没有连接上,则backlog会被master清除掉。repl-backlog-ttl即用来设置该时长,默认为3600s,如果设置为0,则永不清除。
    replica-priority 100						#设置从服务器的优先级,用于Redis Sentinel主从切换时使用,值越小,则提升为主的优先级越高。需要注意的是,如果设置为0,则代表该从服务器不参加选主。
    # replica-announce-ip 5.5.5.5				#常用于端口转发或NAT场景下,对Master暴露真实IP和端口信息。
    # replica-announce-port 1234				#常用于端口转发或NAT场景下,对Master暴露真实IP和端口信息。
    
    #=================设置密码=================
    requirepass 123456				#密码
    
    
    #=================设置客户端=================
    maxclients 10000				#最大客户端数量
    
    #=================设置内存=================
    # maxmemory <bytes>				#最大内存容量
    # maxmemory-policy noeviction	#内存满了处理策略
    # maxmemory-samples 5			#设置样本数量,LRU算法和最小TTL算法都并非是精确的算法,而是估算值,所以你可以设置样本的大小
    # replica-ignore-maxmemory yes	#是否忽略maxmemory设置
    
    #=================释放内存=================
    lazyfree-lazy-eviction no
    lazyfree-lazy-expire no
    lazyfree-lazy-server-del no
    replica-lazy-flush no
    
    #=================AOF的配置=================
    appendonly no					#默认不开启aof模式,默认使用rdb持久化,因为rdb基本够用
    appendfilename "appendonly.aof"	#持久化文件名
    
    # appendfsync always			#每次修改同步,消耗性能
    appendfsync everysec			#每秒执行一次同步,弊端就是可能会丢失这一秒的数据
    # appendfsync no				#不同步,操作系统自己同步,效率最高
    no-appendfsync-on-rewrite no	#设置yes后,如果有保存的进程在执行,则不执行aof的appendfsync策略的fsync
    auto-aof-rewrite-percentage 100	#相对于上次aof文件大小的增长百分比如果超过这个值,则重写aof
    auto-aof-rewrite-min-size 64mb	#表示运行AOF重写时文件的最小大小,默认为64MB
    aof-load-truncated yes			#如果AOF文件在中间被破坏,服务器仍然会带着错误退出。这个选项只适用于当Redis试图从AOF文件中读取更多数据,但没有找到足够的字节时
    aof-use-rdb-preamble yes		#开启混合持久化
    
    #=================Lua脚本配置=================
    lua-time-limit 5000				#参数限制脚本的最长运行时间,默认为5 秒钟
    
    #=================REDIS CLUSTER集群配置=================
    # cluster-enabled yes					#是否开启集群配置。如果配置yes则开启集群功能,此redis实例作为集群的一个节点,否则,它是一个普通的单一的redis实例
    # cluster-config-file nodes-6379.conf	#虽然此配置的名字叫"集群配置文件",但是此配置文件不能人工编辑,它是集群节点自动维护的文件,主要用于记录集群中有哪些节点、他们的状态以及一些持久化参数等,方便在重启时恢复这些状态。通常是在收到请求之后这个文件就会被更新。
    # cluster-node-timeout 15000			#这是集群中的节点能够失联的最大时间,超过这个时间,该节点就会被认为故障。如果主节点超过这个时间还是不可达,则用它的从节点将启动故障迁移,升级成主节点。注意,任何一个节点在这个时间之内如果还是没有连上大部分的主节点,则此节点将停止接收任何请求。一般设置为15秒即可。
    # cluster-replica-validity-factor 10	#设置副本有效(可进行故障转移)因子,副本数据太老旧就不会被选为故障转移副本,如果副本与主服务器最后交互时间超过(node-timeout * replica-validity-factor) + repl-ping-replica-period的值,则不会进行故障转移;(因子过大可能更容易使用太旧的数据进行故障转移,因子过小可能无法选择副本进行故障转移,0是唯一可以保证所有分区恢复时集群能够继续运行的值,0值可以保证集群最大可用性)
    # cluster-migration-barrier 1			#设置一个master故障转移所需最少的副本个数,由于没有正常工作副本的master无法进行故障转移,当一个master拥有超过该值个副本,就会将多余副本转移给孤立的master,以提高抗故障能力;(默认值是1,因为至少有一个正常工作的副本时才能故障转移,如果要禁用只需设置一个很大的数即可或者设置cluster-allow-replica-migration为no,设置为0只用在调试中,在生产环境中设置0是危险的)
    # cluster-require-full-coverage yes		#设置集群是否覆盖全槽位,默认集群检测到16384个槽位没有全部覆盖(存在槽位没有正在运行的节点处理),整个集群停止查询服务,也就是整个集群不可用,当槽位再次全部覆盖后,集群自动变为可用,如果需要在槽位没有全覆盖情况下让已覆盖槽位支持查询,只需设置为no即可;
    # cluster-replica-no-failover no		#设置是否进行故障转移,如果设置为yes,则将阻止master故障时进行故障转移,但扔可以手动强制进行故障转移;(在一些特定场景下可能用得到)
    
    
    #=================docker集群/NAT支持=================
    # cluster-announce-ip 10.1.1.5			#宣布IP地址
    # cluster-announce-port 6379			#宣布服务端口
    # cluster-announce-bus-port 6380		#宣布集群总线端口
    
    #=================慢查询日志=================
    slowlog-log-slower-than 10000			#决定要对执行时间大于多少微秒(microsecond,1= 1,000,000 微秒)的查询进行记录。
    slowlog-max-len 128						#它决定 slow log 最多能保存多少条日志, slow log 本身是一个 FIFO 队列,当队列大小超过 slowlog-max-len 时,最旧的一条日志将被删除,而最新的一条日志加入到 slow log ,以此类推。
    
    ################################ 延时监控 ##############################
    latency-monitor-threshold 0				#能够采样不同的执行路径来知道redis阻塞在哪里。这使得调试各种延时问题变得简单,设置一个毫秒单位的延时阈值来开启延时监控。
    
    #=================事件通知=================
    notify-keyspace-events ""				#键事件通知
    
    #=================高级配置=================
    hash-max-ziplist-entries 512			#这个参数指的是ziplist中允许存储的最大条目个数,默认为512,建议为128。
    hash-max-ziplist-value 64				#ziplist中允许条目value值最大字节数,默认为64,建议为1024。
    list-max-ziplist-size -2				#ziplist列表最大值
    list-compress-depth 0					# 一个quicklist两端不被压缩的节点个数。0: 表示都不压缩。这是Redis的默认值,1: 表示quicklist两端各有1个节点不压缩,中间的节点压缩。3: 表示quicklist两端各有3个节点不压缩,中间的节点压缩。
    set-max-intset-entries 512				#当集合中的元素全是整数,且长度不超过set-max-intset-entries(默认为512),redis会选用intset作为内部编码,大于512用set。
    zset-max-ziplist-entries 128			#当有序集合的元素小于zset-max-ziplist-entries配置(默认是128),同时每个元素的值都小于zset-max-ziplist-value(默认是64字节),Redis会用ziplist来作为有序集合的内部编码实现,ziplist可以有效的减少内存的使用。
    zset-max-ziplist-value 64				#当有序集合的元素小于zset-max-ziplist-entries配置(默认是128),同时每个元素的值都小于zset-max-ziplist-value(默认是64字节),Redis会用ziplist来作为有序集合的内部编码实现,ziplist可以有效的减少内存的使用。
    hll-sparse-max-bytes 3000				#value大小 小于等于hll-sparse-max-bytes使用稀疏数据结构(sparse),大于hll-sparse-max-bytes使用稠密的数据结构(dense)
    stream-node-max-bytes 4096				#Streams单个节点的字节数,以及切换到新节点之前可能包含的最大项目数。
    stream-node-max-entries 100				#Streams单个节点的字节数,以及切换到新节点之前可能包含的最大项目数。
    activerehashing yes						#主动重新散列每100毫秒CPU时间使用1毫秒,以帮助重新散列主Redis散列表(将顶级键映射到值)。
    client-output-buffer-limit normal 0 0 0	#对客户端输出缓冲进行限制可以强迫那些不从服务器读取数据的客户端断开连接,用来强制关闭传输缓慢的客户端。
    client-output-buffer-limit replica 256mb 64mb 60		#对于slave client和MONITER client,如果client-output-buffer一旦超过256mb,又或者超过64mb持续60秒,那么服务器就会立即断开客户端连接。
    client-output-buffer-limit pubsub 32mb 8mb 60			#对于pubsub client,如果client-output-buffer一旦超过32mb,又或者超过8mb持续60秒,那么服务器就会立即断开客户端连接。
    # client-query-buffer-limit 1gb			#客户端查询缓冲区累积新命令。 默认情况下,它被限制为固定数量,以避免协议失步(例如由于客户端中的错误)将导致查询缓冲区中的未绑定内存使用。 但是,如果您有非常特殊的需求,可以在此配置它,例如我们巨大执行请求。
    # proto-max-bulk-len 512mb				#在Redis协议中,批量请求(即表示单个字符串的元素)通常限制为512 MB。 但是,您可以在此更改此限制。
    hz 10									#默认情况下,hz设置为10.提高值时,在Redis处于空闲状态下,将使用更多CPU。范围介于1500之间,大多数用户应使用默认值10,除非仅在需要非常低延迟的环境中将此值提高到100。
    dynamic-hz yes							#启用动态HZ时,实际配置的HZ将用作基线,但是一旦连接了更多客户端,将根据实际需要使用配置的HZ值的倍数。
    aof-rewrite-incremental-fsync yes		#当一个子进程重写AOF文件时,如果启用下面的选项,则文件每生成32M数据会被同步。
    rdb-save-incremental-fsync yes			#当redis保存RDB文件时,如果启用了以下选项,则每生成32 MB数据将对文件进行fsync。 这对于以递增方式将文件提交到磁盘并避免大延迟峰值非常有用。
    
    ##=================活跃的碎片整理=================
    # activedefrag yes						#是否启用碎片整理
    # active-defrag-ignore-bytes 100mb		#启动活动碎片整理的最小碎片浪费量
    # active-defrag-threshold-lower 10		#启动碎片整理的最小碎片百分比
    # active-defrag-threshold-upper 100		#使用最大消耗时的最大碎片百分比
    # active-defrag-cycle-min 5				#在CPU百分比中进行碎片整理的最小消耗
    # active-defrag-cycle-max 75			#在CPU百分比达到最大值时,进行碎片整理
    # active-defrag-max-scan-fields 1000	#从set / hash / zset / list 扫描的最大字段数
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151

    七、redis持久化

    -----------------------------------------------------------------1. redis持久化之RDB-----------------------------------------------------------------

    1. 概念:RDB全称Redis DataBase,在指定时间间隔内将内存中的数据集快照进行持久化。是Redis默认启用的持久化方案,持久化过程会生成一个压缩过的二进制文件,默认名称为dump.rdb,当服务器重启时,加载持久化文件恢复数据到内存中。

    2. 工作流程:

      redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好了的文件。整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能。以bgsave手动触发持久化为例:

      • 父进程通过fork来复制一个环境、变量等完全相同的子进程
      • 当复制完成后,子进程通过信号通知父进程,子进程开始将数据集写入一个临时的RDB文件,当新的RDB文件写入完成后会替换掉原来的RDB文件。
      • fork的过程如果出现错误将会交由父进程处理。
        在这里插入图片描述
    3. 触发条件

      2.1 满足持久化策略,可以在配置文件中配置。如果将持久化策略注释,在相当于关闭RDB持久化。

      save 900 1					#表示900 秒内如果至少有 1 个 key 的值变化,则保存
      save 300 10					#表示300 秒内如果至少有 10 个 key 的值变化,则保存
      save 60 10000				#表示60 秒内如果至少有 10000 个 key 的值变化,则保存
      stop-writes-on-bgsave-error yes		#持久化出错是否继续工作
      rdbcompression yes			#是否压缩rdb文件,将消耗cpu资源
      rdbchecksum yes				#保存rdb文件时,进行错误校验
      dbfilename dump.rdb			#数据持久化到磁盘的文件名
      dir ./						#设置快照文件的存放路径
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8

      2.2 执行flushall命令

      2.3 退出redis

      2.4 执行save命令:会阻塞主进程,直到RDB文件创建完成,在此期间服务器不再处理任何命令请求。

      2.5 执行bgsave命令:不阻塞主进程,后台保存的同时服务器可以持续处理客户端请求。(常用)

    4. 查看RDB持久化文件存储位置(只需要将rdb文件放在存储目录,redis启动时将会自动读取)

      3.1 进入redis:redis-cli -p 6379

      3.2 查看存储地址: config get dir
      在这里插入图片描述

    5. 优缺点

      优点

      • RDB是一个非常紧凑的单文件时间点的Redis数据表示,因此非常适合备份,可以传输到遥远的数据中心,进行灾难恢复。
      • RDB最大限度地提高了Redis的性能,因为Redis父进程需要做的唯一的工作是为了持久化Redis子进程,它将做所有其余的工作。父进程永远不会执行磁盘I/O或类似的操作。
      • 此外与AOF相比,RDB允许使用大数据集更快地重新启动。

      缺点

      • 由于RDB存储的是时间间隔内某个时刻的数据快照,因此有可能会丢失一个时间间隔的据。

      • RDB经常需要fork(),以便使用子进程在磁盘上持久化。如果数据集很大,fork()可能会花费时间,如果数据集很大,CPU性能不是很好,可能会导致Redis停止为客户端服务几毫秒甚至一秒钟。即使fork()使用了COW技术来优化内存占用,但此时如果出现大量key修改依然会需要大量的内存空间消耗。

    -----------------------------------------------------------------2. redis持久化之AOF-----------------------------------------------------------------

    1. 概念:AOF全称Append Only File,AOF通过特定条件触发服务器写操作追加进行内存数据状态持久化。默认不开启,需要在配置文件中设置开启。既然已经有了RDB为什么还要有AOF,上面已经说明RDB保存的是某个时刻的数据快照,在子进程保存过程中父进程新接收的请求增加或者修改的数据不会被处理,会造成一定的数据丢失,AOF 的主要作用是解决数据持久化的实时性和完整性问题。

    2. 工作流程:aof持久化保存的是appendonly.aof文件,步骤分为:命令追加(append)、文件写入(write)和文件同步(sync)。当我们执行命令时,会以协议格式将被执行的写命令追加到服务器的 aof_buf 缓冲区。然后再根据我们配置的aof策略,将 aof_buf 缓冲区的内容写入AOF文件中。当需要恢复数据时,重新执行appendonly.aof文件中的命令就可以了。AOF解决了数据持久化的实时性,也是目前主流的Redis持久化方式。

    3. 触发条件

      需要在配置文件配置AOF开启即可

      appendonly no					#默认不开启aof模式,默认使用rdb持久化,因为rdb基本够用
      appendfilename "appendonly.aof"	#持久化文件名
      # appendfsync always			#每次修改同步,消耗性能
      appendfsync everysec			#每秒执行一次同步,弊端就是可能会丢失这一秒的数据
      # appendfsync no				#不同步,操作系统自己同步,效率最高
      no-appendfsync-on-rewrite no	#设置yes后,如果有保存的进程在执行,则不执行aof的appendfsync策略的fsync
      auto-aof-rewrite-percentage 100	#相对于上次aof文件大小的增长百分比如果超过这个值,则重写aof
      auto-aof-rewrite-min-size 64mb	#表示运行AOF重写时文件的最小大小,默认为64MB
      aof-load-truncated yes			#如果AOF文件在中间被破坏,服务器会带着错误退出。只适用于当Redis试图从AOF文件读取更多数据,但没找到足够的字节时
      aof-use-rdb-preamble yes		#设置yes,redis可以启动并且显示日志告知这个信息,设置no,redis启动失败,显示错误
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
    4. AOF修复机制

      redis自身提供了aof文件修复工具redis-check-aof。如果我们最新的aof文件不小心被误操作导致数据缺失等,可以通过redis-check-aof --fix appendonly.aof指令修复。

    5. AOF重写机制

      会记录每个写命令到AOF文件,随着时间越来越长,AOF文件会变得越来越大。如果不加以控制,会对Redis服务器,甚至对操作系统造成影响,而且AOF文件越大,数据恢复也越慢。为了解决AOF文件体积膨胀的问题,Redis提供AOF文件重写机制来对AOF文件进行“瘦身”,移除无效命令(所谓无效命令指的是一个key被重复设置,我们不关心中间过程,只关心这个key的最终结果))

      在这里插入图片描述

      4.1 重写原理

      Redis提供的AOF重写功能,新文件在替换旧文件时并没有通过读旧文件中的写命令来获取结果,而是通过读取服务器中最新的数据库状态来获取值,这样就需要关心执行过程。

      4.2 重写流程

      • 主线程fork出子进程重写aof日志
      • 子进程重写日志完成后,主线程追加aof日志缓冲
      • 替换日志文件

      在这里插入图片描述

      4.3 AOF重写会阻塞吗?
      AOF重写过程是由后台进程bgrewriteaof来完成的。主线程fork出后台的bgrewriteaof子进程,fork会把主线程的内存拷贝一份给bgrewriteaof子进程,这里面就包含了数据库的最新数据。然后,bgrewriteaof子进程就可以在不影响主线程的情况下,逐一把拷贝的数据写成操作,记入重写日志。所以aof在重写时,在fork进程时是会阻塞住主线程的。

      4.4 AOF日志何时会重写?
      有两个配置项控制AOF重写的触发:
      auto-aof-rewrite-min-size:表示运行AOF重写时文件的最小大小,默认为64MB。
      auto-aof-rewrite-percentage:这个值的计算方式是,当前aof文件大小和上一次重写后aof文件大小的差值,再除以上一次重写后aof文件大小。也就是当前aof文件比上一次重写后aof文件的增量大小,和上一次重写后aof文件大小的比值。

      4.5 重写日志时,有新数据写入咋整?
      Redis服务器内置了一个AOF重写缓冲区,这个缓冲区在子进程创建完成后开始使用,父进程会同时将写命令发送至AOF缓冲区和AOF重写缓冲区。在bgrewriteaof子进程完成日志文件的重写操作后,会提示主线程已经完成重写操作,主线程会将AOF重写缓冲中的命令追加到新的日志文件后面,这些操作对新的日志文件是不存在影响的。(旧的日志文件:主线程使用的日志文件,新的日志文件:bgrewriteaof进程使用的日志文件)这时候在高并发的情况下,AOF重写缓冲区积累可能会很大,这样就会造成阻塞,Redis后来通过Linux管道技术让aof重写期间就能同时进行回放,这样aof重写结束后只需回放少量剩余的数据即可。最后通过修改文件名的方式,保证文件切换的原子性。

      在AOF重写日志期间发生宕机的话,因为日志文件还没切换,所以恢复数据时,用的还是旧的日志文件。
      在这里插入图片描述

    6. Aof修复机制

    7. 优缺点

      优点

      • AOF让Redis的数据可靠性更高,可以使用不同的fsync策略always,everysec,no。在默认的每秒fsync策略下,写性能仍然很好(fsync是使用后台线程执行的,当没有fsync进行时,主线程会努力执行写操作),在everysec策略下最多损失1秒的写操作。
      • AOF日志是一个仅追加的日志,因此不存在查找,也不存在断电时的损坏问题。即使日志由于某种原因(磁盘满了或其他原因)导致AOF文件不完整,redis-check-aof工具也能够轻松地修复它。
      • AOF以易于理解和解析的格式一个接一个地包含所有操作的日志。您甚至可以轻松地导出AOF文件。例如,即使你不小心用FLUSHALL命令刷新了所有的数据,只要在此期间没有重写日志,你仍然可以通过停止服务器,删除最新的命令,重新启动Redis来保存你的数据集。

      缺点

      • 对于同一个数据集,AOF文件通常比等效的RDB文件大。
      • AOF运行效率一般比RDB慢,所以redis默认开启的是RDB持久化。
      • 如果在重写期间有写入数据库的操作(这些操作被缓冲在内存中,并在最后写入新的AOF), AOF会使用大量内存。所有在重写期间到达的写命令都被写入磁盘两次。

    -----------------------------------------------------------------3. redis持久化之混用RDB和AOF-----------------------------------------------------------------

    1. 原理

      Redis 4.0 中提出了一个混合使用 AOF 日志和内存快照的方法。简单来说,内存快照以一定的频率执行,在两次快照之间,使用 AOF 日志记录这期间的所有命令操作,每次执行快照,AOF文件将被清空,然后重新记录下次两个快照之间的操作。这样一来,快照不用很频繁地执行,这就避免了频繁 fork 对主线程的影响。而且,AOF 日志也只用记录两次快照间的操作,也就是说,不需要记录所有操作了,因此,就不会出现文件过大的情况了,也可以避免重写开销。

    在这里插入图片描述

    1. 优点

      • 由于有 AOF 日志记录两次快照之间的所有记录,这样子就可以降低快照的执行频率
      • 由于有RDB记录所有的日志,所以AOF所需要做的就只是记录两次快照之间的所有操作,这样子就可以避免AOF文件太大的情况。
    2. 开启方式:同时开启AOFf和RDB即可

    3. 混用加载日志文件顺序

      由于使用是的混用持久机制,所以一般RDB和AOF持久化文件都会存在,需要先加载RDB历史快照,再追加最新的AOF日志操作。

      • 判断是否开启 AOF 持久化,开启继续执行后续流程,未开启执行加载 RDB 文件的流程;
      • 判断 appendonly.aof 文件是否存在,文件存在则执行后续流程;
      • 判断 AOF 文件开头是 RDB 的格式, 先加载 RDB 内容再加载剩余的 AOF 内容;
      • 判断 AOF 文件开头不是 RDB 的格式,直接以 AOF 格式加载整个文件。
        在这里插入图片描述

    七、redis主从复制

    1. 概念

      集群,顾名思义就是搭建多台redis服务器,而主从复制就是建立在集群基础上,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(master),可读写,后者称为从节点(slave),只能读。数据的复制是单向的,只能由主节点到从节点。一个主节点可以有多个从节点,但一个从节点只能有一个主节点。一般Master节点以写为主,而slave节点以读为主。

      ps:每个从节点还可以有自己的从节点,但是只要主节点一直在,该节点也只能充当从节点作用,只能读不能写。

      img

    2. 为什么使用主从复制

      • 数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。
      • 故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的冗余。
      • 负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。
      • 读写分离:可以用于实现读写分离,主库写、从库读,读写分离不仅可以提高服务器的负载能力,同时可根据需求的变化,改变从库的数量;
      • 高可用基石:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是Redis高可用的基础。
    3. 主从复制流程

      • 从服务器连接主服务器,发送SYNC命令;

      • 主服务器接收到SYNC命名后,发送fullresync 命令,通知从服务器全量复制

      • 主服务器开始执行BGSAVE命令生成RDB快照文件;

      • 主服务器BGSAVE执行完后,向所有从服务器发送快照文件,并在发送期间继续记录被执行的写命令,写入replication buffer缓冲区;

      • 从服务器收到快照文件后清空旧数据,加载新数据;

      • 主服务器快照发送完毕后开始向从服务器发送缓冲区中的数据;

      • 从服务器完成对快照的载入,开始接收命令请求,并执行来自主服务器缓冲区的写命令;

      • 首次连接结束,开始工作后,后续都以增量的方式进行同步

      • 如果主服务器的key过期,从服务器不会过期,需要主服务器发送删除指令给从服务器进行删除
        在这里插入图片描述

    4. redis主从复制的搭建

      ------------------------------------------------------------------集群的搭建-----------------------------------------------------------------------------

      以一台服务器,搭建三个redis集群为例

      1. 复制三个redis.conf,分别命名为:redis6379.conf、redis6380.conf、redis6381.conf

      2. 修改配置文件中的主要配置项:

      ​ 以6379为例,6380与6381同下修改即可

      daemonize yes
      bind 0.0.0.0
      port 6379
      pidfile /var/run/redis_6379.pid
      logfile "redis_6379.log"
      dbfilename dump6379.rdb
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      1. 启动:/usr/local/bin/redis-server /opt/redis/redis-5.0.6/redis6379.conf,另外两个同理

      2. 查看进行:netstat -luntp
        在这里插入图片描述

      3. 进入6379redis内部: redis-cli -p 6379

      4. 查看主从配置,可以看到当前每台redis都默认是主服务器,没有从服务
        在这里插入图片描述

      -----------------------------------------------------------------手动配置主从服务器--------------------------------------------------------------------------

      1. 手动配置主从关系

      ​ 进入6380和6381服务器中,执行指令:slaveof 127.0.0.1 6379,重新查看6379的主从配置,可以看到已经配置成功!
      在这里插入图片描述

      1. 进入6379中,写入name:set name zhanzhk

      2. 进入6380和6381中,获取name:get name
        在这里插入图片描述

      3. 注意

        • 由于我们是使用命令行配置的主从关系。所以当我们重启redis后,主从关系将会被重置。
        • 主服务器挂了,从服务器依旧可以读到数据。主服务器恢复,重新插入数据,依旧会同步到从服务器。(未配置哨兵)
        • 主服务器挂了,从服务器依旧不能写数据,除非将从服务器声明为主服务器。(slaveof no none)(未配置哨兵)
          .

      -------------------------------------------------------------配置文件配置主从服务器----------------------------------------------------------------

      1. 修改6380和6381配置文件,添加一行:replicaof 127.0.0.1 6379
      2. 重启6380和6381端口的redis,查看6379主从状态,配置成功!
        在这里插入图片描述

    八、redis哨兵模式

    1. 概念:在前面,如果我们的redis主服务器挂了,则就无法实现写入的操作,需要手动设置主服务器才能正常写入,较为麻烦,所以就有了哨兵模式。哨兵是一个独立的进程,通过发送命令,等待redis服务器响应,以此来监控redis服务器的运行情况。当哨兵发现主服务器宕机后,一方面可以通知我们,另一方面会通过投票,在slave服务器中选举出新的master服务器。当然,哨兵也有存在宕机的情况,所以一般也会配置多个哨兵来监视redis服务器的运行情况。总得来说,哨兵模式的职能可以总结为以下三点:监控、 提醒、自动故障迁移

    2. 工作原理:
      假设主服务器宕机,哨兵1先检测到结果,但是系统并不会马上进行failover过程,仅仅是哨兵1主观认为主服务器不可以用,这个现象称为主观下线,当后面的哨兵也检测到主服务器不可用,并且数量达到我们设定的阈值时,那么哨兵之间就会进行一次投票,投票的结果由一个哨兵发起,进行failover故障转移操作,最后得票多的从服务器就升级为主服务器。操作转移成功后。就会发布订阅模式,让各个哨兵把自己监控的从服务器实现切换主机,修改配置文件的主机指向,这一过程称为 客观下线。选举主机机制根据以下排序筛选进行:

      • 剔除与master断开链接的次数多,超时时间长的从服务器
      • 根据从服务器的优先级,即replica-priority配置
      • 根据数据复制的下标(用来评估slave当前拥有多少master的数据),即同步的进度
      • 根据进程ID,选最小的

      多哨兵监控Redis

    3. 哨兵模式配置文件详解

      # bind 127.0.0.1 192.168.1.1					# 使用bind,只有指定的ip地址才能访问此redis
      protected-mode no								# 保护模式关闭,这样其他服务起就可以访问此台redis  			
      port 26379										# 哨兵对应的端口
      daemonize yes									# 哨兵模式是否后台启动,默认no,改为yes
      pidfile /var/run/redis-sentinel.pid				# id
      logfile ""										# 日志文件
      dir /tmp										# 哨兵sentinel的工作目录
      
      
      # 哨兵sentinel监控的redis主节点的 ip port
      # master-name 可以自己命名的主节点名字 只能由字母A-z、数字0-9 、这三个字符".-_"组成。
      # quorum 配置多少个sentinel哨兵统一认为master主节点失联 那么这时客观上认为主节点失联了
      # 注意:如果 quorum 给的值过大, 超过主机数量, 可能会导致 master 主机挂掉之后, 没有新的 slave来替代 master
      # sentinel monitor <master-name> <ip> <redis-port> <quorum>
      sentinel monitor mymaster 127.0.0.1 6379 2		
      
      
      # 当在Redis实例中开启了requirepass foobared 授权密码 这样所有连接Redis实例的客户端都要提供密码
      # 设置哨兵sentinel 连接主从的密码 注意必须为主从设置一样的验证密码
      # sentinel auth-pass <master-name> <password>
      # sentinel auth-pass mymaster MySUPER--secret-0123passw0rd
      
      
      # sentinel down-after-milliseconds <master-name> <milliseconds>
      sentinel down-after-milliseconds mymaster 30000	# 指定多少毫秒之后 主节点没有应答哨兵sentinel 此时 哨兵主观上认为主节点下线 默认30秒
      
      
      # sentinel parallel-syncs <master-name> <numreplicas>
      sentinel parallel-syncs mymaster 1				# 这个配置项指定了在发生failover主备切换时最多可以有多少个slave同时对新的master进行同步,这个数字越小,完成failover所需的时间就越长,但是如果这个数字越大,就意味着越 多的slave因为replication而不可用。可以通过将这个值设为 1 来保证每次只有一个slave 处于不能处理命令请求的状态。
      
      
      # 故障转移的超时时间 failover-timeout 可以用在以下这些方面:
      #1. 同一个sentinel对同一个master两次failover之间的间隔时间。
      #2. 当一个slave从一个错误的master那里同步数据开始计算时间。直到slave被纠正为向正确的master那里同步数据时。
      #3.当想要取消一个正在进行的failover所需要的时间。
      #4.当进行failover时,配置所有slaves指向新的master所需的最大时间。不过,即使过了这个超时,slaves依然会被正确配置为指向master,但是就不按parallel-syncs所配置的规则来了
      # 默认三分钟
      # sentinel failover-timeout <master-name> <milliseconds>
      sentinel failover-timeout mymaster 180000
      
      
      # SCRIPTS EXECUTION
      # 配置当某一事件发生时所需要执行的脚本,可以通过脚本来通知管理员,例如当系统运行不正常时发邮件通知相关人员。
      # 对于脚本的运行结果有以下规则:
      # 若脚本执行后返回1,那么该脚本稍后将会被再次执行,重复次数目前默认为10
      # 若脚本执行后返回2,或者比2更高的一个返回值,脚本将不会重复执行。
      # 如果脚本在执行过程中由于收到系统中断信号被终止了,则同返回值为1时的行为相同。
      # 一个脚本的最大执行时间为60s,如果超过这个时间,脚本将会被一个SIGKILL信号终止,之后重新执行。
      # 通知型脚本:当sentinel有任何警告级别的事件发生时(比如说redis实例的主观失效和客观失效等等),将会去调用这个脚本,这时这个脚本应该通过邮件,SMS等方式去通知系统管理员关于系统不正常运行的信息。调用该脚本时,将传给脚本两个参数,一个是事件的类型,一个是事件的描述。如果sentinel.conf配置文件中配置了这个脚本路径,那么必须保证这个脚本存在于这个路径,并且是可执行的,否则sentinel无法正常启动成功。
      #通知脚本
      # sentinel notification-script <master-name> <script-path>
      # sentinel notification-script mymaster /var/redis/notify.sh
      
      
      # 客户端重新配置主节点参数脚本
      # 当一个master由于failover而发生改变时,这个脚本将会被调用,通知相关的客户端关于master 地址已经发生改变的信息。
      # 以下参数将会在调用脚本时传给脚本:
      # <master-name> <role> <state> <from-ip> <from-port> <to-ip> <to-port>
      # 目前<state>总是“failover”,
      # <role>是“leader”或者“observer”中的一个。
      # 参数 from-ip, from-port, to-ip, to-port是用来和旧的master和新的master(即旧的slave)通信的
      # 这个脚本应该是通用的,能被多次调用,不是针对性的。
      # CLIENTS RECONFIGURATION SCRIPT
      # sentinel client-reconfig-script <master-name> <script-path>
      # sentinel client-reconfig-script mymaster /var/redis/reconfig.sh
      
      
      sentinel deny-scripts-reconfig yes				# 不允许使用SENTINEL SET设置notification-script和client-reconfig-script。
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52
      • 53
      • 54
      • 55
      • 56
      • 57
      • 58
      • 59
      • 60
      • 61
      • 62
      • 63
      • 64
      • 65
      • 66
      • 67
      • 68
    4. 搭建哨兵模式(一主二从三哨兵)

      4.1 创建配置文件sentine26379.conf,sentine26380.conf,sentine26381.conf,

      bind 0.0.0.0					
      protected-mode no					
      port 26379					
      daemonize yes				
      pidfile /var/run/redis-sentinel26379.pid
      logfile "sentinel26379.log"		
      dir /tmp					
      sentinel monitor mymaster 127.0.0.1 6379 2
      sentinel down-after-milliseconds mymaster 30000
      sentinel parallel-syncs mymaster 1		
      sentinel failover-timeout mymaster 180000
      sentinel deny-scripts-reconfig yes	
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12

      4.2 启动哨兵: redis-sentinel /opt/redis/redis-5.0.6/sentine26380.conf

      4.3 查看启动进程:netstat -luntp
      在这里插入图片描述

      4.4 杀掉6379的linux进程,等待30s,再次查看6380的主从情况,发现主节点已经迁移了,说明配置成功!
      在这里插入图片描述

      4.5 优缺点

      优点

      • 哨兵模式是基于主从模式的,所有主从的优点,哨兵模式都具有
      • 主从可以自动切换,系统更健壮,可用性更高
      • Sentinel 会不断的检查 主服务器 和 从服务器 是否正常运行。当被监控的某个 Redis 服务器出现问题,Sentinel 通过API脚本向管理员或者其他的应用程序发送通知。

      缺点

      • Redis较难支持在线扩容,对于集群,容量达到上限时在线扩容会变得很复杂。
      • 运维与技术成本增高

    九、redis cluster集群

    -----------------------------------------------------------------Redis cluster集群介绍---------------------------------------------------------------------------

    1. 演变

      1.1 单台服务,读写压力瓶颈问题明显

      1.2 主从复制,使读写分离,减轻压力

      1.3 哨兵模式,解决了主从切换问题。但是只有一台服务在写,仍存在redis单机写入的瓶颈问题。而且当master挂掉的时候,sentinel 会选举出来一个 master,选举的时候是没有办法去访问Redis的,会存在访问瞬断的情况;若是在电商网站大促的时候master给挂掉了,几秒钟损失好多订单数据;

      1.4 cluster集群,属于无中心架构,各个节点都可充当读写角色,并且可动态扩展,整体降低了读写压力。

    2. 特点

      • 所有Redis节点使用(PING机制)互联
      • 集群中某个节点的失效,是整个集群中超过半数的节点监测都失效才算真正的失效
      • 客户端不需要proxy即可直接连接redis,应用程序需要写全部的redis服务器IP。
      • redis cluster一共有 0-16383等16384个槽位,搭建好集群后,将槽位分配到不同的redis服务器。当需要在redis集群中写入一个key -value的时候,会使用 CRC16(key) mod 16384之后的值来判断key具体需要写入到哪个槽位与对应的redis节点。因此有多少个reids节点相当于redis 并发扩展了多少倍。
      • redis cluster不支持单机版本的16个默认数据库,仅有0数据库, select命令被禁用
      • 每个master都可能有1个或多个slave节点, 这些节点将在网络分区或者发生故障时尝试替代master节点, 同时这些slave节点也可用于支撑大规模的读操作
    3. 优点

      • Redis集群有多个master,可以减小访问瞬断问题的影响;
      • Redis集群有多个master,可以提供更高的并发量;
      • Redis集群可以分片存储,这样就可以存储更多的数据;
    4. 原理:假如三个redis主节点分别是:A, B, C 三个节点,采用哈希槽 (hash slot)的方式来分配16384个slot 的话,它们三个节点分别承担的slot 区间是

      使用 CRC16(key) mod 16384 之后的值跟以下三个节点进行匹配,如果落在哪个区间,则实际key的存取就在哪个redis节点运行
      节点A覆盖 05460 
      节点B覆盖 546110922 
      节点C覆盖 1092316383
      
      • 1
      • 2
      • 3
      • 4

      在这里插入图片描述

    -----------------------------------------------------------------Redis cluster集群搭建--------------------------------------------------------------------------

    1. 开启三台虚拟机,分别为192.168.248.10,192.168.248.11,192.168.248.12

    2. 三台虚拟机分别各启动6380、6381端口。

      ps1:如果已经设置了主从配置,需要注释掉

      ps2:如果redis设置了密码,则每个节点的密码都需要一致。

      daemonize yes
      bind 0.0.0.0
      port 6380
      pidfile /var/run/redis_6380.pid
      logfile "redis_6380.log"
      dbfilename dump6380.rdb
      cluster-enabled yes
      cluster-config-file nodes-6380.conf
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
    3. 启动6个redis:/usr/local/bin/redis-server /opt/redis/redis-5.0.6/redis6380.conf

    4. 启动cluster集群(其中前三个为master,后三个为slave)

      ps1:如果已存在历史数据,需要先删除dump.rdb文件。如果还是不行,则直接进入数据库,清除数据:flushdb。

      redis-cli --cluster create 192.168.248.10:6380  192.168.248.11:6380 192.168.248.12:6380 192.168.248.10:6381 192.168.248.11:6381  192.168.248.12:6381 --cluster-replicas 1 
      
      • 1

    在这里插入图片描述

    1. 进入redis查看集群状态(-c 表示以集群模式登录):redis-cli -c -p 6380 --> CLUSTER INFO

    在这里插入图片描述

    1. 查看集群插槽分配情况:cluster slots
      在这里插入图片描述

    2. 查看集群主从配置:cluster nodes
      在这里插入图片描述

    3. 插入测试,成功
      在这里插入图片描述

    4. 新增节点

      9.1 启动两个redis新节点,端口为6382,6383

      9.2 将节点加入集群,可登录任意一台集群节点操作:cluster meet 192.168.248.12 6382、cluster meet 192.168.248.12 6383

      9.3 分配槽位,根据提示选填:redis-cli --cluster reshard 192.168.248.12:6382

      • 算出来每个 master 的槽数:4096(原本三个 master,加一个变四个 maste,16384/4=4096)。
      • 填写接收槽位的节点 ID:36efcd23eecc5c4238711c4e680026fea1a71e6d
      • 填写槽位来源节点 Source node 为 all,表示从其他所有 master 节点迁移槽位。

      9.4 查看槽位
      在这里插入图片描述

      9.5 将6383设置为从节点

      1. 登录6383:redis-cli -c -p 6383 
      2. 通过id指定主节点:cluster replicate cf4c1e949793cb30c284061ee83c6d767967a0dd
      3. 查看 cluster node
      
      • 1
      • 2
      • 3

    在这里插入图片描述

    1. 删除节点

      10.1 删除从节点:redis-cli --cluster del-node 192.168.248.12:6383 11408eaf11a25b8f4913dc26975b7ad4e3427b4e

      10.2 移除槽位:redis-cli --cluster reshard 192.168.248.12:6382
      在这里插入图片描述

      10.3 删除主节点:redis-cli --cluster del-node 192.168.248.12:6382 d73b1f5bbf649a278269ea03533204b8579c5834

    2. 相关问题

      集群是如何判断是否有某个节点挂掉

      首先要说的是,每一个节点都存有这个集群所有主节点以及从节点的信息。它们之间通过互相的ping-pong判断是否节点可以连接上。如果有一半以上的节点去ping一个节点的时候没有回应,集群就认为这个节点宕机了,然后去连接它的备用节点。

      整个集群挂掉的条件
      1.如果集群任意 master 挂掉,且当前 master 没有 slave或者从节点也挂了。此时集群进入 fail 状态,也可以理解成集群的 slot 映射[0-16383]不完整时进入 fail 状态。
      2.如果集群超过半数以上 master 挂掉,无论是否有 slave,集群进入 fail 状态

      集群挂掉怎么恢复

      重新启动各个节点即可,集群关系不需要重新create,node-xxx.conf记录了相关的节点关系

      怎么新增节点

      启动新的redis节点,接入集群,分配槽位即可。

    十、缓存击穿、穿透、雪崩

    1. 缓存击穿

      1.1 概念:热点数据缓存过期,导致这些请求都去访问数据库,造成数据库的崩溃,这就是缓存击穿。

      1.2 处理方案

      • 热点数据设置不过期

      • 设置分布式互斥锁,保证同一时间内只有一个请求来构建缓存

        当业务线程在处理用户请求时,如果发现访问的数据不在 Redis 里,就加个互斥锁,保证同一时间内只有一个请求来构建缓存(从数据库读取数据,再将数据更新到 Redis 里),当缓存构建完成后,再释放锁。未能获取互斥锁的请求,要么等待锁释放后重新读取缓存,要么就返回空值或者默认值。

    2. 缓存穿透

      2.1 概念:当客户端发起大量请求时,这些请求数据既不在缓存中,也不在数据库中。如果此时有黑客写了脚本恶意大量调用,就会造成数据库的崩溃。

      2.2 处理方案

      • 设置空缓存
      • 使用布隆过滤器快速判断数据是否存在,避免通过查询数据库来判断数据是否存在;
    3. 缓存雪崩

      3.1 概念:缓存同时间失效或redis的宕机,大量的请求都集中在数据库处理,导致数据库崩溃,从而造成一系列恶性循环,甚至导致应用崩溃。

      3.2 处理方案

      • 均匀设置过期时间,防止同一时间过期

      • 设置分布式互斥锁

      • 设计双 key 策略

        我们对缓存数据可以使用两个 key,一个是主 key,会设置过期时间,一个是备 key,不会设置过期,当主key过期,则使用副key的值,并将副key的值赋予给主key。同时去数据库查询数据,再次刷新主key和副key的值。

      • 服务熔断

        如果发现redis宕机了,则直接返回错误提示。

      • 集群配置

        搭建高可用的redis集群,尽量防止宕机情况。

  • 相关阅读:
    django orm DateTimeField 6位小数精度问题
    数据结构——栈和队列
    CPVT(ICLR 2023)论文解读
    14【视图】
    SpringMVC入门
    Azkaban集群模式部署
    7.javase_数组1
    女朋友说:你要搞懂了MySQL三大日志,我就让你嘿嘿嘿!
    Codeforces Round 895 (Div. 3)
    ros改进算法路径规划仿真
  • 原文地址:https://blog.csdn.net/weixin_40496191/article/details/125455994