成神之路之Redis从头开始学习(二)

四 Java连接Redis

Jedis连接Redis,Lettuce连接Redis

4.1 Jedis连接Redis

1、创建maven项目

image-20201021082340924

2、导入需要的依赖包

https://mvnrepository.com/

<dependencies>
  <!--1、Jedis依赖包-->
  <!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
  <dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.9.0</version>
  </dependency>
  <!--2、Junit测试-->
  <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13</version>
  </dependency>
  <!--3、Lombok依赖包-->
  <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.12</version>
  </dependency>
</dependencies>

3、测试

public class Demo1 {
    @Test
    public void set(){
        //1、连接Redis
        Jedis jedis = new Jedis("127.0.0.1",6379);
        //2、操作Redis - redis的命令是什么jedis对应的方法就是什么
        jedis.set("name","zhangsan");
        //3、释放资源
        jedis.close();
    }
    @Test
    public void get(){
        //1、连接Redis
        Jedis jedis = new Jedis("127.0.0.1",6379);
        //2、操作Redis - redis的命令是什么jedis对应的方法就是什么
        String value = jedis.get("name");
        System.out.println(value);
        //3、释放资源
        jedis.close();
    }
}

 

4.2 Jedis如何存储一个对象到Redis

准备一个User实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {

    private Long id;

    private String name;

    private Date birthday;
}

导入spring-context依赖

<!--4、导入spring-context-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>4.3.20.RELEASE</version>
</dependency>

创建Demo测试类,编写内容

public class Demo2 {
    //存储对象 -- 以byte[]形式存储在redis中
    @Test
    public void setByteArray(){
        //1、连接redis服务
        Jedis jedis = new Jedis("127.0.0.1",6379);
        //2.1 准备key(String) - value(User)
        String key = "user";
        User user = new User(1L,"张三",new Date());
        //2.2 将key和value转换为byte[]
        byte[] byteKey = SerializationUtils.serialize(key);
        //user对象序列化和反序列化,需要在User类实现Serializable接口
        byte[] byteValue = SerializationUtils.serialize(user);
        //2.3 将key和value存储到redis
        jedis.set(byteKey,byteValue);
        //3、释放资源
        jedis.close();
    }
    @Test
    public void getByteArray(){
        //1、连接redis服务
        Jedis jedis = new Jedis("127.0.0.1",6379);
        //2.1 准备key(String)
        String key = "user";
        //2.2 将key转换为byte[]
        byte[] byteKey = SerializationUtils.serialize(key);
        //2.3 获取value
        byte[] byteValue = jedis.get(byteKey);
        //2.4 将value反序列化为user对象
        User user2 = (User)SerializationUtils.deserialize(byteValue);
        System.out.println(user2);
        //3、释放资源
        jedis.close();
    }
}

 

4.3 Jedis如何存储一个对象到Redis,以String的形式存储

导入一个fastjson依赖

<!--5、导入fastjson-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.71</version>
</dependency>

编写测试类

public class Demo3 {
    //存储的对象,以String形式
    @Test
    public void setString(){
        //1、连接redis
        Jedis jedis = new Jedis("127.0.0.1",6379);
        //2.1 准备key(String) - value(User)
        String stringKey = "stringUser";
        User value = new User(2L,"李四",new Date());
        //2.2 使用fastjson将value格式化为json字符串
        String stringVlue = JSON.toJSONString(value);
        //2.3 存储到redis中
        jedis.set(stringKey,stringVlue);
        //3关闭连接
        jedis.close();
    }
    @Test
    public void getString(){
        //1、连接redis
        Jedis jedis = new Jedis("127.0.0.1",6379);
        //2.1 准备key
        String stringKey = "stringUser";
        //2.2 去redis中查询value
        String stringValue =jedis.get(stringKey);
        //2.3 将value反序列化为User
        User user = JSON.parseObject(stringValue,User.class);
        System.out.println(user);
        //3关闭连接
        jedis.close();
    }
}

 

4.4 Jedis连接池的操作

@Test
public void pool2(){
    //1、创建连接池的配置信息
    GenericObjectPoolConfig config = new GenericObjectPoolConfig();
    //连接池中最大的活跃数
    config.setMaxTotal(100);
    //最大空闲数
    config.setMaxIdle(10);
    //最大空闲数
    config.setMinIdle(5);
    //当连接池空了之后,多久没获取到jedis对象就超时,单位毫秒
    config.setMaxWaitMillis(3000);
    //2、创建连接池
    JedisPool pool = new JedisPool(config,"127.0.0.1",6379);
    //3、获取jedis
    Jedis jedis = pool.getResource();
    //4、操作
    String value = jedis.get("stringUser");
    System.out.println(value);
    //6、释放连接
    jedis.close();
}

 

4.5 Redis的管道操作

因为在操作Redis的时候,执行一个命令需要先发送请求到Redis服务器,这个过程需要经历网络延迟,Redis还需要给客户端一个响应。

如果我需要一次性执行很多个命令,上述的方式效率很低,可以通过Redis的管道,先将命令放到客户端的一个pipeline中,之后一次性的将全部命令发送到Redis服务器,Redis服务一次性的将全部的返回结果响应给客户端。

//Redis的管道操作
@Test
public void pipeline(){
    //1、创建连接
    JedisPool pool = new JedisPool("127.0.0.1",6379);
    long start = System.currentTimeMillis();
    //2、获取一个连接对象
    Jedis jedis = pool.getResource();
//        //3、执行incr - 10000次
//        for (int i = 0; i < 50000; i++) {
//            jedis.incr("pp");
//        }
//        //4、释放资源
//        jedis.close();
    //------------------

    //3、创建管道
    Pipeline pipeline = jedis.pipelined();
    //4、执行incr - 10000次放到管道中
    for (int i = 0; i < 50000; i++) {
        pipeline.incr("qq");
    }
    pipeline.syncAndReturnAll();
    //5、释放资源
    jedis.close();
    long end = System.currentTimeMillis();
    System.out.println(end-start);
}

 

五 Redis其他配置及集群

修改yaml文件,以方便后期修改Redis配置信息

#指定本 yml 依从的 compose 哪个版本制定的
version: '3.1'
#定义服务
services:
#定义一个服务
  redis:
    #指定镜像
    image: daocloud.io/library/redis:5.0.7
    #容器总是从新启动
    restart: always
    #容器名称
    container_name: redis
    #添加环境变量。指定时区
    environment:
      - TZ=Asia/Shanghai
    #添加端口映射
    ports:
      - 6379:6379
    #将主机当前目录的conf目录下的redis.conf挂载到容器里的/usr/local/redis.conf。
    volumes:
      - ./conf/redis.conf:/usr/local/redis.conf
    #覆盖容器启动的默认命令。
    command: ["redis-server","/usr/local/redis.conf"]

执行docker命令:

#停止和删除容器、网络、卷、镜像。先停止和删除之前的redis容器
docker-compose down
#重新创建容器并后台启动
docker-compose up -d

 

5.1 Redis的AUTH

方式一:通过修改Redis的配置文件,实现Redis的密码校验

#在./conf/redis.conf里面添加如下配置
requirepass 密码

然后重启redis容器:docker-compose restart

三种客户端的连接方式

  1. redis-cli:在输入正常命令之前,先输入auth密码即可

  2. 图形化界面:在连接Redis的信息中添加上验证的密码

  3. Jedis客户端:

    第一种:jedis.auth(password);(不推荐)

    第二种:使用JedisPool的方式

public JedisPool(GenericObjectPoolConfig poolConfig, String host, int port, int timeout, String password)

 

方式二:在不修改redis.conf文件的前提下,在第一次连接redis时,输入命令:Config set requirepass 密码,后续再次操作redis时,需要先AUTH做一次校验。(不推荐这种方式)重启之后密码就失效了。

 

5.2 Redis的事务

Redis的事务:一次事务,该成功的成功,该失败的失败。

先开启事务,执行一系列的命令,但是密码不会立即执行,会被放在一个队列中,如果你执行事务,那么这个队列中的命令全部执行,如果取消事务,一个队列中的命令全部作废。

  1. 开启事务:multi

  2. 输入要执行的命令,命令被放入到一个队列中

  3. 执行事务:exec

  4. 取消事务:discard

Redis的事务想发挥功能,需要配合watch监听机制

在开启事务之前,先通过watch命令监听一个或者多个key,在开启事务之后,如果有其他客户端修改了我监听的key,事务会自动取消。

如果执行了事务或者取消了事务,watch监听自动消除,一般不需要手动执行unwatch释放监听。

 

5.3 Redis持久化机制

RDB方式-默认

RDB是Redis默认的持久化机制

  1. RDB持久化文件,速度比较快,而且存储的是一个二进制的文件,传输起来很方便。

  2. RDB持久化的时机:

    save 900 1 #在900秒内,有1个key改变,就执行RDB持久化 save 300 10 #在300秒内,有10个key改变,就执行RDB持久化 save 60 10000 #在60秒内,有10000个key改变,就执行RDB持久化

  3. RDB无法保证数据的绝对安全

#RDB主要配置项
#持久化时机:在900秒内,有1个key改变,就执行RDB持久化
save 900 1
#持久化时机:在300秒内,有10个key改变,就执行RDB持久化
save 300 10
#持久化时机:在60秒内,有10000个key改变,就执行RDB持久化
save 60 10000

#开启RDB持久化的压缩
rdbcompression yes
#RDB持久化文件的名称
dbfilename dump.rdb

 

AOF方式

AOF持久化机制默认是关闭的,Redis官方推荐同时开启RDB和AOF持久化,更安全,避免数据丢失。在aof无法使用的时候,再用rdb的备份文件做替补恢复

  1. AOF持久化的速度相对RDB较慢,存储的是一个文本文件,时间久了文件会比较大,传输困难

  2. AOF持久化机制:

    #每执行一个写操作,立即持久化到AOF文件中,性能比较低

    appendfsync always

    #每秒执行一次持久化 appendfsync everysec #会根据你的操作系统不同,环境的不同,在一定时间执行一次持久化

    appendfsync no

  3. AOF相对RDB更安全,推荐同时开启AOF和RDB。

#AOF主要配置项
#代表开启AOF持久化
appendonly yes
#AOF文件的名称
appendfilename "redis.aof"
#AOF持久化执行的时机
#每执行一个写操作,立即持久化到AOF文件中,性能比较低
appendfsync always
#每秒执行一次持久化
appendfsync everysec
#会根据你的操作系统不同,环境的不同,在一定时间执行一次持久化
appendfsync no

同时开启RDB和AOF的注意事项:

如果同时开启了AOF和RDB持久化,那么Redis宕机重启之后,需要加载一个持久化文件,优先选择AOF文件。

如果先开启了RDB,然后之后开启AOF,如果RDB执行了持久化,那么RDB文件中的内容会被AOF覆盖掉。

 

5.4 Redis主从架构

单机版Redis存在读写瓶颈的问题

image-20201024165216365

docker-compose.yml文件:

#指定本 yml 依从的 compose 哪个版本制定的
version: '3.1'
#定义服务
services:
#定义一个服务
  redis1:
    #指定镜像
    image: daocloud.io/library/redis:5.0.7
    #容器总是从新启动
    restart: always
    #容器名称
    container_name: redis1
    #添加环境变量。指定时区
    environment:
      - TZ=Asia/Shanghai
    #添加端口映射
    ports:
      - 7001:6379
    #将主机当前目录的conf目录下的redis.conf挂载到容器里的/usr/local/redis.conf。
    volumes:
      - ./conf/redis1.conf:/usr/local/redis.conf
    #覆盖容器启动的默认命令。
    command: ["redis-server","/usr/local/redis.conf"]
  redis2:
    #指定镜像
    image: daocloud.io/library/redis:5.0.7
    #容器总是从新启动
    restart: always
    #容器名称
    container_name: redis2
    #添加环境变量。指定时区
    environment:
      - TZ=Asia/Shanghai
    #添加端口映射
    ports:
      - 7002:6379
    #将主机当前目录的conf目录下的redis.conf挂载到容器里的/usr/local/redis.conf。
    volumes:
      - ./conf/redis2.conf:/usr/local/redis.conf
    #配置链接,redis1容器别名master
    links:
      - redis1:master
    #覆盖容器启动的默认命令。
    command: ["redis-server","/usr/local/redis.conf"]
  redis3:
    #指定镜像
    image: daocloud.io/library/redis:5.0.7
    #容器总是从新启动
    restart: always
    #容器名称
    container_name: redis3
    #添加环境变量。指定时区
    environment:
      - TZ=Asia/Shanghai
    #添加端口映射
    ports:
      - 7003:6379
    #将主机当前目录的conf目录下的redis.conf挂载到容器里的/usr/local/redis.conf。
    volumes:
      - ./conf/redis3.conf:/usr/local/redis.conf
    links:
      - redis1:master
    #覆盖容器启动的默认命令。
    command: ["redis-server","/usr/local/redis.conf"]   

#redis2和redis3从节点配置 replicaof <masterip> <masterport>
replicaof master 6379

具体操作步骤

  1. 在/opt目录下面创建工作目录

    mkdir docker_redis_master_salve

  2. vi docker-compose.yml

    复制上面配置信息到yml

  3. 在docker_redis_master_salve下创建conf目录

  4. touch redis1.conf

  5. touch redis2.conf

  6. touch redis3.conf

  7. 向redis2.conf和redis3.conf中添加配置:replicaof master 6379

 

5.5 哨兵

哨兵可以帮助我们解决主从架构中的单点故障问题

image-20201024215538511

修改docker-compose.yml,为了可以在容器内部使用哨兵的配置

#指定本 yml 依从的 compose 哪个版本制定的
version: '3.1'
#定义服务
services:
#定义一个服务
  redis1:
    #指定镜像
    image: daocloud.io/library/redis:5.0.7
    #容器总是从新启动
    restart: always
    #容器名称
    container_name: redis1
    #添加环境变量。指定时区
    environment:
      - TZ=Asia/Shanghai
    #添加端口映射
    ports:
      - 7001:6379
    #将主机当前目录的conf目录下的redis.conf挂载到容器里的/usr/local/redis.conf。
    volumes:
      - ./conf/redis1.conf:/usr/local/redis.conf
      - ./conf/sentinel1.conf:/data/sentinel.conf  #添加的内容
    #覆盖容器启动的默认命令。
    command: ["redis-server","/usr/local/redis.conf"]
  redis2:
    #指定镜像
    image: daocloud.io/library/redis:5.0.7
    #容器总是从新启动
    restart: always
    #容器名称
    container_name: redis2
    #添加环境变量。指定时区
    environment:
      - TZ=Asia/Shanghai
    #添加端口映射
    ports:
      - 7002:6379
    #将主机当前目录的conf目录下的redis.conf挂载到容器里的/usr/local/redis.conf。
    volumes:
      - ./conf/redis2.conf:/usr/local/redis.conf
      - ./conf/sentinel2.conf:/data/sentinel.conf  #添加的内容
    #配置链接,redis1容器别名master
    links:
      - redis1:master
    #覆盖容器启动的默认命令。
    command: ["redis-server","/usr/local/redis.conf"]
  redis3:
    #指定镜像
    image: daocloud.io/library/redis:5.0.7
    #容器总是从新启动
    restart: always
    #容器名称
    container_name: redis3
    #添加环境变量。指定时区
    environment:
      - TZ=Asia/Shanghai
    #添加端口映射
    ports:
      - 7003:6379
    #将主机当前目录的conf目录下的redis.conf挂载到容器里的/usr/local/redis.conf。
    volumes:
      - ./conf/redis3.conf:/usr/local/redis.conf
      - ./conf/sentinel3.conf:/data/sentinel.conf  #添加的内容
    links:
      - redis1:master
    #覆盖容器启动的默认命令。
    command: ["redis-server","/usr/local/redis.conf"]

准备哨兵的配置文件,并且在容器内部手动启动哨兵即可

哨兵基本配置:

#哨兵需要后台启动
daemonize yes
#指定Master节点的ip和端口(主) 哨兵 监视 主节节点 主节点IP/名称 端口 2个从节点
sentinel monitor master localhost 6379 2
#指定Master节点的ip和端口(从) sentinel monitor mymaster 127.0.0.1 6379 2
sentinel monitor master master 6379 2
#哨兵每隔多久监听一次redis架构,默认为3秒,这里设置1秒好看效果
sentinel down-after-milliseconds master 10000

./conf/sentinel1.conf

#哨兵需要后台启动
daemonize yes
#指定Master节点的ip和端口(主) 哨兵 监视 主节节点 主节点IP/名称 端口 2个从节点
sentinel monitor master localhost 6379 2
#哨兵每隔多久监听一次redis架构,默认为3秒,这里设置1秒好看效果
sentinel down-after-milliseconds master 10000

./conf/sentinel2.conf

#哨兵需要后台启动
daemonize yes
#指定Master节点的ip和端口(从) sentinel monitor mymaster 127.0.0.1 6379 2
sentinel monitor master master 6379 2
#哨兵每隔多久监听一次redis架构,默认为3秒,这里设置1秒好看效果
sentinel down-after-milliseconds master 10000

./conf/sentinel3.conf

#哨兵需要后台启动
daemonize yes
#指定Master节点的ip和端口(从) sentinel monitor mymaster 127.0.0.1 6379 2
sentinel monitor master master 6379 2
#哨兵每隔多久监听一次redis架构,默认为3秒,这里设置1秒好看效果
sentinel down-after-milliseconds master 10000

修改docker-compose.yml和增加sentinel的配置文件之后重新构建容器。

docker-compose down
docker-compose up -d

在Redis容器内部启动sentinel即可,三个容器分别进入启动。

redis-sentinel sentinel.conf

启动成功之后,如果我们down掉redis1这个容器,集群会自动选举redis2或者redis3为Master.

 

5.6 Redis集群

Redis集群在保证主从加哨兵的基本功能之外,还能提升Redis存储数据的能力。

image-20201025090045480

搭建集群

#docker-compose.yml
version: "3.1"
services:
  redis1:
    image: daocloud.io/library/redis:5.0.7
    #容器总是从新启动
    restart: always
    container_name: redis1
    environment:
      - TZ=Asia/Shanghai
    ports:
      - 7001:7001
      - 17001:17001
    volumes:
      - ./conf/redis1.conf:/usr/local/redis/redis.conf
    command: ["redis-server","/usr/local/redis/redis.conf"]
  redis2:
    image: daocloud.io/library/redis:5.0.7
    #容器总是从新启动
    restart: always
    container_name: redis2
    environment:
      - TZ=Asia/Shanghai
    ports:
      - 7002:7002
      - 17002:17002
    volumes:
      - ./conf/redis2.conf:/usr/local/redis/redis.conf
    command: ["redis-server","/usr/local/redis/redis.conf"]
  redis3:
    image: daocloud.io/library/redis:5.0.7
    #容器总是从新启动
    restart: always
    container_name: redis3
    environment:
      - TZ=Asia/Shanghai
    ports:
      - 7003:7003
      - 17003:17003
    volumes:
      - ./conf/redis3.conf:/usr/local/redis/redis.conf
    command: ["redis-server","/usr/local/redis/redis.conf"]
  redis4:
    image: daocloud.io/library/redis:5.0.7
    #容器总是从新启动
    restart: always
    container_name: redis4
    environment:
      - TZ=Asia/Shanghai
    ports:
      - 7004:7004
      - 17004:17004
    volumes:
      - ./conf/redis4.conf:/usr/local/redis/redis.conf
    command: ["redis-server","/usr/local/redis/redis.conf"]
  redis5:
    image: daocloud.io/library/redis:5.0.7
    #容器总是从新启动
    restart: always
    container_name: redis5
    environment:
      - TZ=Asia/Shanghai
    ports:
      - 7005:7005
      - 17005:17005
    volumes:
      - ./conf/redis5.conf:/usr/local/redis/redis.conf
    command: ["redis-server","/usr/local/redis/redis.conf"]
  redis6:
    image: daocloud.io/library/redis:5.0.7
    #容器总是从新启动
    restart: always
    container_name: redis6
    environment:
      - TZ=Asia/Shanghai
    ports:
      - 7006:7006
      - 17006:17006
    volumes:
      - ./conf/redis6.conf:/usr/local/redis/redis.conf
    command: ["redis-server","/usr/local/redis/redis.conf"]

#redis.conf
# 指定redis的端口号
port 7001
#开启redis集群
cluster-enabled yes
# 集群信息的文件
cluster-config-file nodes-7001.conf
# 集群的对外ip地址
cluster-announce-ip 192.168.102.11
# 集群的对外端口号
cluster-announce-port 7001
#集群的总线端口号
cluster-announce-bus-port 17001

启动6个Redis的节点。

随便跳转到一个容器内部,使用redis-cli管理集群,他会自动分配好主从节点以及hash槽

redis-cli --cluster create 192.168.102.11:7001 192.168.102.11:7002 192.168.102.11:7003 192.168.102.11:7004 192.168.102.11:7005 192.168.102.11:7006 --cluster-replicas 1

测试

使用redis-cli -h 192.168.102.11 -p 7001 连接指定一个redis节点,此时set key可能设置不进去,因为通过计算key应该在另外的节点。如果需要在客户端连接,但是set数据能跳转到其他节点set,连接命令需要加-c,如下

redis-cli -h 192.168.102.11 -p 7001 -c

 

5.7 Java连接Redis集群

使用JedisCluster对象连接Redis集群

public class Demo5 {

    public void clusterTest(){
        //创建Set<HostAndPort>
        Set<HostAndPort> nodes = new HashSet<>();
        nodes.add(new HostAndPort("192.168.102.11",7001));
        nodes.add(new HostAndPort("192.168.102.11",7002));
        nodes.add(new HostAndPort("192.168.102.11",7003));
        nodes.add(new HostAndPort("192.168.102.11",7004));
        nodes.add(new HostAndPort("192.168.102.11",7005));
        nodes.add(new HostAndPort("192.168.102.11",7006));
        //创建jedisCluster集群对象
        JedisCluster jedisCluster = new JedisCluster(nodes);

        String value = jedisCluster.get("a");
        System.out.println(value);
    }
}

六 Redis常见问题

6.1 key的生存时间到了,Redis会立即删除吗?

不会立即删除

  1. 定期删除:

    Redis每隔一段时间就会去查看Redis设置了过期时间的key,会在大概100ms的间隔中默认查看3个key.

  2. 惰性删除

    当去查询一个已经过了生存时间的key时,Redis会先查看当前key的生存时间是否已经到了,直接删除当前key,并且给用户返回一个空值。

 

6.2 Redis的淘汰机制

在Redis内存已经满的时候,添加一个新的数据,就会执行淘汰策略。

  1. volatile-lru:在内存不足时,Redis会在设置了过期时间的key中淘汰掉一个最近最少使用的key

  2. allkeys-lru:在内存不足时,Redis会在全部的key中淘汰掉一个最近最少使用的key

  3. volatile-lfu:在内存不足时,Redis会在设置了过期时间的key中淘汰掉一个最近最少频次使用的key

  4. allkeys-lfu:在内存不足时,Redis会在全部的key中淘汰掉一个最近最少频次使用的key

  5. volatile-random:在内存不足时,Redis会在设置了过期时间的key中随机淘汰掉一个key

  6. allkeys-random:在内存不足时,Redis会在全部的key中随机淘汰掉一个key

  7. volatile-ttl:在内存不足时,Redis会在设置了过期时间的key中随机淘汰掉一个剩余生存时间最少的key

  8. noeviction:(默认):在内存不足时,直接报错

指定淘汰机制的方式:maxmemory-policy noeviction(具体策略)

设置Redis最大内存:maxmemory <bytes>

 

6.3 缓存的常见问题

缓存穿透

问题出现的原因:查询的数据,Redis中没有,数据库中也没有。如何解决?

  1. 根据Id查询时,如果id是自增的,将id的最大值放到Redis中,在查询数据库之前,直接比较一下id.

  2. 如果id不是整形的,可以将全部id放到set中,在用户查询之前,去set中查看一些是否有这个id.

  3. 获取客户端的ip地址,可以将ip的访问添加限制。

  4. 将访问的key直接在Redis中缓存一个空值,下次访问的时候可直接查redis放回空值

  5. 根据缓存数据Key的设计规则,将不符合规则的key采用布隆过滤器进行过滤

缓存击穿

问题出现的原因:缓存中的热点数据,突然到期了,造成大量的请求都去访问数据库,造成数据库宕机

  1. 在访问缓存中没有的时候,添加一个锁,让几个请求去访问数据库,避免数据库宕机

  2. 去掉热点数据的生存时间

缓存雪崩

问题出现的原因:当大量缓存同时到期时,最终大量的同时去访问数据库,导致数据库宕机

  1. 将缓存中的数据设置不同的生存时间,例如设置为30~60分钟的要给随机时间

缓存倾斜

问题出现的原因:热点数据放在一个Reids节点上,导致Redis节点无法承受住大量的请求,最终导致Redis宕机。

  1. 扩展主从架构,搭建多个从节点,缓解Redis的压力

  2. 可以在Tomcat中做JVM缓存,在查询Redis之前,先去查询Tomcat中的缓存。

 

好了,本次Redis学习就到这里了。相关的示例代码已上传码云,学习文档也已放百度云,仓库地址和文档地址可关注"良辰"公众号,回复"学无止境"或者"redis"获取

学无止境,关注我,我们一起进步。如果觉得文章还可以,点个赞,点个在看呗,谢谢~我们下期见。

img

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章