Redis配置文件

修改yml信息,将配置文件映射出来,方便我们修改使用。

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
  volumes:
      - /docker/redis/conf/redis.conf:/usr/local/redis/redis.conf
  command: ["redis-server","/usr/local/redis/redis.conf"]

Redis的AUTH

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

#redis.conf
requirepass 密码

方式二:在不修改Redis配置文件的前提下,在第一次连接redis时,输入命名 Config set requirepass 密码 后再次操作操作redis时,需要做auth校验。auth 123456

设置密码后,客户端连接方式:

  1. redis-cli :auth 密码
  2. 图形化界面,在连接Redis信息中添加密码
  3. Jedis客户端:

    1. jedis.auth(password)
    2. 使用JedisPool中的有参构造

Redis的事务

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

步骤:

  1. 监听key:watch key
  2. 开启事务:multi
  3. 输入要执行的命令 -> 被放入一个队列中
  4. 执行事务:exec
  5. 取消事务:discard

Redis的事务向发挥功能,需要配置watch监听机制。

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

Redis持久化

RDB是Redis默认的持久化机制

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

    1. save 900 1 在900秒内,有1个key改变了,就执行RDB持久化。
    2. save 300 10 在300秒内,有10个key改变了,就执行RDB持久化。
    3. save 60 10000 在60秒内,有10000个key改变了,就执行RDB持久化。
  3. RDB无法保证数据的绝对安全。

相关配置文件内容:

save 900 1 
save 300 10 
save 60  10000
# 开启RDB持久化的压缩
rdbcompression yes
# RDB持久化文件的文件名
dbfilename dump.rdb

修改 docker-compose.yml ,将RDB持久化文件映射出来

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
        volumes:
            - /docker/redis/conf/redis.conf:/usr/local/redis/redis.conf
            - /docker/redis/data:/data #新添加
        command: ["redis-server","/usr/local/redis/redis.conf"]

AOF持久化默认是关闭的,Redis官方推荐同时开启RDB和AOF持久化,更安全,避免数据丢失。

Redis 支持同时开启RDB和AOF持久化,这样可以让数据更加安全。

  1. AOF持久化的速度,相对RDB较慢,存储的是一个文本文件,到了后期文件比较大,传输困难。
  2. 传输时机

    1. appendfsync always 每执行一个写操作,立即执行到AOF文件中,性能比较低。
    2. appendfsync everysec 每秒执行一次持久化。
    3. appendfsync no 会根据你操作系统不同,环境不同,在一定时间内执行一次持久化。
  3. AOF相对于RDB更安全,推荐同时开启AOF和RDB。

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

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

# 开启AOF持久化,默认为no
appendonly yes
# AOF文件名称
appendfilename "appendonly.aof"
# AOF持久化执行时机
appendfsync always
appendfsync everysec
appendfsync no

Redis主从架构

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

使用docker-compose来一键启动三个redis,注意 links 属性,可以让从redis跟主redis通讯

主从架构,主要让从机去寻找主机。

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
    volumes:
      - /docker/redis/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:6379
    volumes:
      - /docker/redis/conf/redis2.conf:/usr/local/redis/redis.conf
    links:
      - redis1:master
    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:6379
    volumes:
      - /docker/redis/conf/redis3.conf:/usr/local/redis/redis.conf
    links:
      - redis1:master
    command: ["redis-server","/usr/local/redis/redis.conf"]        

在redis2、redis3的配置文件中(redis.conf) 加入,注意:6379为docker容器内部的端口号,而不是映射出来的7001

# 从节点配置
replicaof master 6379

进入redis1容器,使用 info 命令,可以看到role角色为master,子节点有两个。

在redis1中,我们执行 set name xn2001

在redis2、redis3中,我们执行 get name,是可以读取出数据的。这便实现了读写分离的主从架构。

Redis哨兵

哨兵模式是一种特殊的模式,首先Redis提供了哨兵的命令,哨兵是一个独立的进程,作为进程,它会独立运行。其原理是哨兵通过发送命令,等待Redis服务器响应,从而监控运行的多个Redis实例。

哨兵有两个作用

  • 通过发送命令,让Redis服务器返回监控其运行状态,包括主服务器和从服务器。
  • 当哨兵监测到master宕机,会自动将slave切换成master,然后通过发布订阅模式通知其他的从服务器,修改配置文件,让它们切换主机。

然而一个哨兵进程对Redis服务器进行监控,可能会出现问题,我们可以使用多个哨兵进行监控。各个哨兵之间还会进行监控,这样就形成了多哨兵模式。

假设主服务器宕机,哨兵1先检测到这个结果,系统并不会马上进行failover过程,仅仅是哨兵1主观的认为主服务器不可用,这个现象成为主观下线。当后面的哨兵也检测到主服务器不可用,并且数量达到一定值时,那么哨兵之间就会进行一次投票,投票的结果由一个哨兵发起,进行failover操作。切换成功后,就会通过发布订阅模式,让各个哨兵把自己监控的从服务器实现切换主机,这个过程称为客观下线。这样对于客户端而言,一切都是透明的。

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

主节点的 sentinel1.conf

# 哨兵需要后台启动
daemonize yes

# 指定Master节点的IP和端口(主)
# sentinel monitor <master-name> <ip> <redis-port> <quorum>
sentinel monitor master localhost 6379 2

# 哨兵每隔多久监听一次redis架构,默认30s
sentinel failover-timeout master 30000

从节点的 sentinel2.conf、sentinel3.conf

# 哨兵需要后台启动
daemonize yes

# 指定Master节点的IP和端口(从)
# sentinel monitor <master-name> <ip> <redis-port> <quorum>
sentinel monitor master master 6379 2

# 哨兵每隔多久监听一次redis架构,默认30s
sentinel failover-timeout master 30000

修改 docker-compose.yml,把 sentinel.conf 映射进去,也就是往里面添加多一个数据卷即可。

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
    volumes:
      - /docker/redis/conf/redis1.conf:/usr/local/redis/redis.conf
      - /docker/redis/conf/sentinel1.conf:/usr/local/redis/sentinel.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:6379
    volumes:
      - /docker/redis/conf/redis2.conf:/usr/local/redis/redis.conf
      - /docker/redis/conf/sentinel2.conf:/usr/local/redis/sentinel.conf
    links:
      - redis1:master
    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:6379
    volumes:
      - /docker/redis/conf/redis3.conf:/usr/local/redis/redis.conf
      - /docker/redis/conf/sentinel3.conf:/usr/local/redis/sentinel.conf
    links:
      - redis1:master
    command: ["redis-server","/usr/local/redis/redis.conf"]   

关闭docker-compose,重新启动

docker-compose down
docker-compose up -d

进入容器内部,然后进入 /usr/local/bin,可以看到 redis-sentinel

docker exec -it redis1 bash
cd /usr/local/bin
ls -l

root@6f99cff8c6c1:/usr/local/bin# ls -l
total 24568
-rwxrwxr-x 1 root root 374 Feb 27 2020 docker-entrypoint.sh
-rwxr-xr-x 1 root root 2294944 Oct 16 2018 gosu
-rwxr-xr-x 1 root root 5940736 Feb 27 2020 redis-benchmark
lrwxrwxrwx 1 root root 12 Feb 27 2020 redis-check-aof -> redis-server
lrwxrwxrwx 1 root root 12 Feb 27 2020 redis-check-rdb -> redis-server
-rwxr-xr-x 1 root root 6484808 Feb 27 2020 redis-cli
lrwxrwxrwx 1 root root 12 Feb 27 2020 redis-sentinel -> redis-server
-rwxr-xr-x 1 root root 10423704 Feb 27 2020 redis-server

启动哨兵,后面带上指定的配置文件

redis-sentinel /usr/local/redis/sentinel.conf

28:X 09 Oct 2020 00:54:53.552 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
28:X 09 Oct 2020 00:54:53.552 # Redis version=5.0.7, bits=64, commit=00000000, modified=0, pid=28, just started
28:X 09 Oct 2020 00:54:53.552 # Configuration loaded

另外redis2、redis3也使用上面的这行命令启动哨兵。

之后你可以停止掉redis1,30s后你会发现redis2/redis3变成了master,此时你依旧可以取到key-value,这就是redis的哨兵及主从架构。

主从架构提高了我们的读写效率,哨兵让我们redis实现了高可用。但在大流量面前这是不够的,我们可能还需要搭建Redis集群用于多个Redis进行读写。注意:主从,哨兵,集群是三个不同的回事。

Redis集群

Redis集群架构

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

  1. Redis集群是无中心的。
  2. Redis集群有一个ping-pang机制。
  3. 投票机制,Redis集群节点的数量必须是 2n +1。
  4. Redis集群中默认分配了16384个hash槽,在存储数据时,就会将key进行crc16的算法,并且对16384取余,根据最终的结果,将key-value存放到执行Redis节点中,而且每一个Redis集群都在维护着相应的hash槽。
  5. 为了保证数据的安全性,每一个集群的节点,至少要跟着一个从节点。
  6. 单独的针对Redis集群中的某一个节点搭建主从。
  7. 当Redis集群中,超过半数的节点宕机之后,Redis集群就瘫痪了。

Redis集群搭建

准备 docker-compose.yml文件

# 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文件,仅展示redis1.conf,其它同理。

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

root@ubuntu-virtual-machine:/docker/redis/nodes# docker-compose up -d
Creating network "nodes_default" with the default driver
Creating redis3 ... done
Creating redis2 ... done
Creating redis5 ... done
Creating redis6 ... done
Creating redis1 ... done
Creating redis4 ... done

启动后,需要进入任意一个Redis,然后执行下面的命令,让集群通讯。

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

--cluster-replicas 1 每个节点后面有几个从节点,也就是6个redis,3主3从。

注意:这里跟之前说的主从没有任何关系,这里的从节点只负责备份。

根据结果我们看到3主3从,并且自动帮我们分配好了hash槽

测试集群效果

redis-cli -h 192.168.231.129 -p 7001 -c

root@c24ae18b259e:/data# redis-cli -h 192.168.231.129 -p 7001 -c
192.168.231.129:7001> set a a
-> Redirected to slot [15495] located at 192.168.231.129:7003
OK
192.168.231.129:7003> set b b
-> Redirected to slot [3300] located at 192.168.231.129:7001
OK
192.168.231.129:7001> set c c
-> Redirected to slot [7365] located at 192.168.231.129:7002
OK
192.168.231.129:7002> get a
-> Redirected to slot [15495] located at 192.168.231.129:7003
"a"
192.168.231.129:7003> get c
-> Redirected to slot [7365] located at 192.168.231.129:7002
"c"
192.168.231.129:7002> get b
-> Redirected to slot [3300] located at 192.168.231.129:7001
"b"

可以看到它根据取余后的结果,会自动跳转到分配好的hash槽redis节点上存储key-value

注意:redis集群在启动时必须保证本身是干净的,没有任何持久化数据。

Redis删除策略

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

不会立即删除。

  1. 定期删除:redis每隔一段时间就会去查看设置了过期时间的key,会在100ms的时间间隔里默认查看3个key。
  2. 惰性删除:当你查询一个设置了过期时间的key时,Redis会先查看当前key的生存时间是否到期,到期则直接删除该key并返回一个空值。

Redis内存淘汰机制

如果大量过期key堆积在内存里,导致redis内存块耗尽了,怎么办?

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

# maxmemory-policy noeviction  

noeviction:当内存使用达到阈值的时候,所有引起申请内存的命令会报错。(默认)

allkeys-lru:在主键空间中,优先移除最近未使用的key。

volatile-lru:在设置了过期时间的键空间中,优先移除最近未使用的key。

allkeys-random:在主键空间中,随机移除某个key。

volatile-random:在设置了过期时间的键空间中,随机移除某个key。

volatile-ttl:在设置了过期时间的键空间中,具有更早过期时间的key优先移除。

Redis缓存问题

  1. 当业务系统发起某一个查询请求时,首先判断缓存中是否有该数据;
  2. 如果缓存中存在,则直接返回数据;
  3. 如果缓存中不存在,则再查询数据库,然后保存数据到缓存,然后返回数据。

缓存穿透

业务系统要查询的数据根本就存在!当业务系统发起查询时,按照上述流程,首先会前往缓存中查询,由于缓存中不存在,然后再前往数据库中查询。由于该数据压根就不存在,因此数据库也返回空。这就是缓存穿透。

综上所述:业务系统访问压根就不存在的数据,就称为缓存穿透。

缓存穿透的危害

如果存在海量请求查询压根就不存在的数据,那么这些海量请求都会落到数据库中,数据库压力剧增,可能会导致系统崩溃(你要知道,目前业务系统中最脆弱的就是IO,稍微来点压力它就会崩溃,所以我们要想种种办法保护它)。

为什么会发生缓存穿透?

发生缓存穿透的原因有很多,一般为如下两种:

  1. 恶意攻击,故意营造大量不存在的数据请求我们的服务,由于缓存中并不存在这些数据,因此海量请求均落在数据库中,从而可能会导致数据库崩溃。
  2. 代码逻辑错误。

缓存穿透的解决方案

下面来介绍两种防止缓存穿透的手段。

缓存空数据

之所以发生缓存穿透,是因为缓存中没有存储这些空数据的key,导致这些请求全都打到数据库上。

那么,我们可以稍微修改一下业务系统的代码,将数据库查询结果为空的key也存储在缓存中。当后续又出现该key的查询请求时,缓存直接返回null,而无需查询数据库。

缓存空对象会有两个问题:

第一,空值做了缓存,意味着缓存层中存了更多的键,需要更多的内存空间 ( 如果是攻击,问题更严重 ),比较有效的方法是针对这类数据设置一个较短的过期时间,让其自动剔除。

第二,缓存层和存储层的数据会有一段时间窗口的不一致,可能会对业务有一定影响。例如过期时间设置为 5 分钟,如果此时存储层添加了这个数据,那此段时间就会出现缓存层和存储层数据的不一致,此时可以利用消息系统或者其他方式清除掉缓存层中的空对象。

BloomFilter

第二种避免缓存穿透的方式:布隆过滤器(BloomFilter)

它需要在缓存之前再加一道屏障,里面存储目前数据库中存在的所有key。

当业务系统有查询请求的时候,首先去BloomFilter中查询该key是否存在。若不存在,则说明数据库中也不存在该数据,因此缓存都不要查了,直接返回null。若存在,则继续执行后续的流程,先前往缓存中查询,缓存中没有的话再前往数据库中的查询。

这种方法适用于数据命中不高,数据相对固定实时性低(通常是数据集较大)的应用场景,代码维护较为复杂,但是缓存空间占用少。

两种方案的比较

这两种方案都能解决缓存穿透的问题,但使用场景却各不相同。

对于一些恶意攻击,查询的key往往各不相同,而且数据贼多。此时,第一种方案就显得提襟见肘了。因为它需要存储所有空数据的key,而这些恶意攻击的key往往各不相同,而且同一个key往往只请求一次。因此即使缓存了这些空数据的key,由于不再使用第二次,因此也起不了保护数据库的作用。
因此,对于空数据的key各不相同key重复请求概率低的场景而言,应该选择第二种方案。而对于空数据的key数量有限key重复请求概率较高的场景而言,应该选择第一种方案。

缓存雪崩

缓存其实扮演了一个保护数据库的角色。它帮数据库抵挡大量的查询请求,从而避免脆弱的数据库受到伤害。

如果缓存因某种原因发生了宕机,那么原本被缓存抵挡的海量查询请求就会像疯狗一样涌向数据库。此时数据库如果抵挡不了这巨大的压力,它就会崩溃。这就是缓存雪崩。

如何避免缓存雪崩?

使用缓存集群,保证缓存高可用

飞机都有多个引擎一样,如果缓存层设计成高可用的,即使个别节点、个别机器、甚至是机房宕掉,依然可以提供服务

Redis Sentinel 和 Redis Cluster 都实现了高可用。

使用Hystrix

Hystrix是一款开源的“防雪崩工具”,它通过 熔断、降级、限流 三个手段来降低雪崩发生后的损失。

Hystrix就是一个Java类库,它采用命令模式,每一项服务处理请求都有各自的处理器。所有的请求都要经过各自的处理器。处理器会记录当前服务的请求失败率。一旦发现当前服务的请求失败率达到预设的值,Hystrix将会拒绝随后该服务的所有请求,直接返回一个预设的结果。这就是所谓的“熔断”。当经过一段时间后,Hystrix会放行该服务的一部分请求,再次统计它的请求失败率。如果此时请求失败率符合预设值,则完全打开限流开关;如果请求失败率仍然很高,那么继续拒绝该服务的所有请求。这就是所谓的“限流”。而Hystrix向那些被拒绝的请求直接返回一个预设结果,被称为“降级”

缓存击穿(热点数据集中失效)

我们一般都会给缓存设定一个失效时间,过了失效时间后,该数据库会被缓存直接删除,从而一定程度上保证数据的实时性。

但是,对于一些请求量极高的热点数据而言,一旦过了有效时间,此刻将会有大量请求落在数据库上,从而可能会导致数据库崩溃。

如果某一个热点数据失效,那么当再次有该数据的查询请求[req-1]时就会前往数据库查询。但是,从请求发往数据库,到该数据更新到缓存中的这段时间中,由于缓存中仍然没有该数据,因此这段时间内到达的查询请求都会落到数据库上,这将会对数据库造成巨大的压力。此外,当这些请求查询完成后,都会重复更新缓存。

互斥锁

此方法只允许一个线程重建缓存,其他线程等待重建缓存的线程执行完,重新从缓存获取数据即可。

当第一个数据库查询请求发起后,就将缓存中该数据上锁;此时到达缓存的其他查询请求将无法查询该字段,从而被阻塞等待;当第一个请求完成数据库查询,并将数据更新值缓存后,释放锁;此时其他被阻塞的查询请求将可以直接从缓存中查到该数据。

当某一个热点数据失效后,只有第一个数据库查询请求发往数据库,其余所有的查询请求均被阻塞,从而保护了数据库。但是,由于采用了互斥锁,其他请求将会阻塞等待,此时系统的吞吐量将会下降。这需要结合实际的业务考虑是否允许这么做。

互斥锁可以避免某一个热点数据失效导致数据库崩溃的问题,而在实际业务中,往往会存在一批热点数据同时失效的场景。那么,对于这种场景该如何防止数据库过载呢?

设置不同的失效时间

当我们向缓存中存储这些数据的时候,可以将他们的缓存失效时间错开。这样能够避免同时失效。如:在一个基础时间上加/减一个随机数,从而将这些缓存的失效时间错开。

永远不过期

“永远不过期”包含两层意思:

从缓存层面来看,确实没有设置过期时间,所以不会出现热点 key 过期后产生的问题,也就是“物理”不过期。
从功能层面来看,为每个 value 设置一个逻辑过期时间,当发现超过逻辑过期时间后,会使用单独的线程去构建缓存。

从实战看,此方法有效杜绝了热点 key 产生的问题,但唯一不足的就是重构缓存期间,会出现数据不一致的情况,这取决于应用方是否容忍这种不一致。

两种方案的比较

  • 互斥锁 (mutex key):这种方案思路比较简单,但是存在一定的隐患,如果构建缓存过程出现问题或者时间较长,可能会存在死锁和线程池阻塞的风险,但是这种方法能够较好的降低后端存储负载并在一致性上做的比较好。
  • ”永远不过期 “:这种方案由于没有设置真正的过期时间,实际上已经不存在热点 key 产生的一系列危害,但是会存在数据不一致的情况,同时代码复杂度会增大。

缓存倾斜

问题:热点数据放在了一个Redis几点上,导致Redis几点无法承受住大量的请求,最终Redis宕机。

  1. 扩展主从架构,搭建大量的从节点,缓解Redis的压力。
  2. 可以在Tomcat中做JVM缓存,在查询Redis之前,先去查询Tomcat中的缓存。

参考资料:

https://blog.csdn.net/haoxin963/article/details/83245113

https://www.bilibili.com/video/BV1FZ4y1u7ny?p=30

Last modification:October 9th, 2020 at 08:35 pm
如果觉得我的文章对你有用,请随意赞赏