Redis高级

发布订阅

redis 的发布订阅系统有点类似于我们生活中的电台,电台可以在某一个频率上发送广播,而我们可以接收任何一个频率的广播,Android 中的 broadcast 也和这类似。

订阅消息的方式如下:

127.0.0.1:6379> SUBSCRIBE c1 c2 c3
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "c1"
3) (integer) 1
1) "subscribe"
2) "c2"
3) (integer) 2
1) "subscribe"
2) "c3"
3) (integer) 3

这个表示接收 c1,c2,c3 三个频道传来的消息,发送消息的方式如下:

127.0.0.1:6379> PUBLISH c1 "hello redis!"
(integer) 1

当 c1 这个频道上有消息发出时,此时在消息订阅控制台可以看到如下输出:

1) "message"
2) "c1"
3) "hello redis!"

在 redis 中,我们也可以使用模式匹配订阅,如下:

127.0.0.1:6379> PSUBSCRIBE c*
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "c*"
3) (integer) 1

此时可以接收到所有以 c 开头的频道发来的消息。

tips

redis 中的发布订阅系统在某些场景下还是非常好用的,但是也有一些问题需要注意:由于网络在传输过程中可能会遭遇断线等意外情况,断线后需要进行重连,然而这会导致断线期间的数据丢失。

事务

既然 redis 是一种 NoSQL 数据库,那它当然也有事务的功能,不过这里的事务和我们关系型数据库中的事务有一点点差异。

redis 中事务的用法非常简单,我们通过 MULTI 命令开启一个事务,如下:

127.0.0.1:6379> MULTI
OK

在 MULTI 命令执行之后,我们可以继续发送命令去执行,此时的命令不会被立马执行,而是放在一个队列中,如下:

127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED

当所有的命令都输入完成后,我们可以通过 EXEC 命令发起执行,也可以通过 DISCARD 命令清空队列,如下:

127.0.0.1:6379> EXEC
1) OK
2) OK
3) OK

事务中的异常情况

redis 中事务的异常情况总的来说分为两类:

  1. 进入队列之前就能发现的错误,比如命令输错;
  2. 执行 EXEC 之后才能发现的错误,比如给一个非数字字符加 1 ;

那么对于这两种不同的异常,redis 中有不同的处理策略。对于第一种错误,服务器会对命令入队失败的情况进行记录,并在客户端调用 EXEC 命令时,拒绝执行并自动放弃这个事务(这个是2.6.5之后的版本做法,之前的版本做法小伙伴可以参考官方文档)。如下:

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set kv1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k3 v3 3 3
QUEUED
127.0.0.1:6379> set k4 v4
QUEUED
127.0.0.1:6379> EXEC
1) OK
2) OK
3) (error) ERR syntax error
4) OK
127.0.0.1:6379> keys *
1) "k4"
2) "k2"
3) "kv1"

而对于第二种情况,redis 并没有对它们进行特别处理, 即使事务中有某个/某些命令在执行时产生了错误, 事务中的其他命令仍然会继续执行。如下:

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set k1 vv
QUEUED
127.0.0.1:6379> INCR k1
QUEUED
127.0.0.1:6379> EXEC
1) OK
2) (error) ERR value is not an integer or out of range
127.0.0.1:6379> GET k1
"vv"

不同于关系型数据库,redis 中的事务出错时没有回滚,对此,官方的解释如下:

Redis 命令只会因为错误的语法而失败(并且这些问题不能在入队时发现),或是命令用在了错误类型的键上面:这也就是说,从实用性的角度来说,失败的命令是由编程错误造成的,而这些错误应该在开发的过程中被发现,而不应该出现在生产环境中。因为不需要对回滚进行支持,所以 Redis 的内部可以保持简单且快速。

WATCH 命令

事务中的 WATCH 命令可以用来监控一个 key,通过这种监控,我们可以为 redis 事务提供(CAS)行为。 如果有至少一个被 WATCH 监视的键在 EXEC 执行之前被修改了,那么整个事务都会被取消,EXEC 返回 nil-reply 来表示事务已经失败。如下:

redis01

通过 unwatch 命令,可以取消对一个 key 的监控,如下:

redis12

redis 持久化

整体上来说,redis 持久化有两种方式,快照持久化和 AOF ,在项目中我们可以根据实际情况选择合适的持久化方式,也可以不用持久化,这关键看我们的 redis 在项目中扮演了什么样的角色。那么我将分别用两篇文章来介绍这两种不同的持久化方式,本文我们先来看看第一种方式。

快照持久化

快照持久化,顾名思义,就是通过拍摄快照的方式实现数据的持久化,redis 可以在某个时间点上对内存中的数据创建一个副本文件,副本文件中的数据在 redis 重启时会被自动加载,我们也可以将副本文件拷贝到其他地方一样可以使用。

如何配置快照持久化

redis中的快照持久化默认是开启的,redis.conf中相关配置主要有如下几项:

save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
dbfilename dump.rdb
dir ./

前面三个 save 相关的选项表示备份的频率,分别表示 900 秒内至少一个键被更改则进行快照,300 秒内至少 10 个键被更改则进行快照,60 秒内至少 10000 个键被更改则进行快照, stop-writes-on-bgsave-error 表示在快照创建出错后,是否继续执行写命令, rdbcompression 则表示是否对快照文件进行压缩, dbfilename 表示生成的快照文件的名字,dir 则表示生成的快照文件的位置,在 redis 中,快照持久化默认就是开启的。我们可以通过如下步骤验证快照持久化的效果:

1.进入 redis 安装目录,如果有 dump.rdb 文件,先将之删除。如下:

p299

2.启动 redis ,随便向 redis 中存储几个数据,然后关闭redis并退出,如下:

[root@localhost redis-4.0.8]# redis-server redis.conf
[root@localhost redis-4.0.8]# redis-cli
127.0.0.1:6379> set k1 v1
OK
127.0.0.1:6379> set k2 v2
OK
127.0.0.1:6379> SHUTDOWN
not connected> exit

3.退出来后,我们发现刚刚删掉的 dump.rdb 文件又回来了,这就是生成的备份文件。
4.此时再次启动 redis 并进入,发现刚刚存储的数据都还在,这是因为 redis 在启动时加载了 dump.rdb 中的数据。好了,关闭 redis 并退出。
5.将 redis 目录下的 dump.rdb 文件删除。
6.再次启动 redis 并进入到控制台,所有的数据都不存在了。

快照持久化操作流程

通过上面的介绍,小伙伴们对快照持久化都有一个大致的认识了,那么这个东西到底是怎么运行的?持久化的时机是什么?我们来仔细扒一扒。

1.在 redis 运行过程中,我们可以向 redis 发送一条 save 命令来创建一个快照,save 是一个阻塞命令,redis 在接收到 save 命令之后,开始执行备份操作之后,在备份操作执行完毕之前,将不再处理其他请求,其他请求将被挂起,因此这个命令我们用的不多。save 命令执行如下:

127.0.0.1:6379> SAVE
OK

2.在 redis 运行过程中,我们也可以发送一条 bgsave 命令来创建一个快照,不同于 save 命令,bgsave 命令会 fork 一个子进程,然后这个子进程负责执行将快照写入硬盘,而父进程则继续处理客户端发来的请求,这样就不会导致客户端命令阻塞了。如下:

127.0.0.1:6379> BGSAVE
Background saving started

3.如果我们在 redis.conf 中配置了如下选项:

save 900 1
save 300 10
save 60 10000

那么当条件满足时,比如 900 秒内有一个 key 被操作了,那么 redis 就会自动触发 bgsava 命令进行备份。我们可以根据实际需求在 redis.conf 中配置多个这种触发规则。

4.还有一种情况也会触发 save 命令,那就是我们执行 shutdown 命令时,当我们用 shutdown 命令关闭 redis 时,此时也会执行一个 save 命令进行备份操作,并在备份操作完成后将服务器关闭。

5.还有一种特殊情况也会触发 bgsave 命令,就是在主从备份的时候。当从机连接上主机后,会发送一条 sync 命令来开始一次复制操作,此时主机会开始一次 bgsave 操作,并在 bgsave 操作结束后向从机发送快照数据实现数据同步。

快照持久化的缺点

快照持久化有一些缺点,比如 save 命令会发生阻塞,bgsave 虽然不会发生阻塞,但是 fork 一个子进程又要耗费资源,在一些极端情况下,fork 子进程的时间甚至超过数据备份的时间。定期的持久化也会让我们存在数据丢失的风险,最坏的情况我们可能丢失掉最近一次备份到当下的数据,具体丢失多久的数据,要看我们项目的承受能力,我们可以根据项目的承受能力配饰 save 参数。

AOF 持久化

与快照持久化不同,AOF 持久化是将被执行的命令写到 aof 文件末尾,在恢复时只需要从头到尾执行一遍写命令即可恢复数据,AOF 在 redis 中默认也是没有开启的,需要我们手动开启,开启方式如下:

打开 redis.conf 配置文件,修改 appendonly 属性值为 yes ,如下:

appendonly yes

另外几个和 AOF 相关的属性如下:

appendfilename "appendonly.aof"
# appendfsync always
appendfsync everysec
# appendfsync no
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

这几个属性的含义分别如下:

1.appendfilename 表示生成的 AOF 备份文件的文件名。
2.appendfsync 表示备份的时机,always 表示每执行一个命令就备份一次,everysec 表示每秒备份一次,no 表示将备份时机交给操作系统。
3.no-appendfsync-on-rewrite 表示在对 aof 文件进行压缩时,是否执行同步操作。
4.最后两行配置表示 AOF 文件的压缩时机,这个我们一会再细说。

同时为了避免快照备份的影响,我们将快照备份关闭,关闭方式如下:

save ""
# save 900 1
# save 300 10
# save 60 10000

此时,当我们在 redis 中进行数据操作时,就会自动生成 AOF 的配置文件 appendonly.aof ,如下:

p300

注意此时没有 dump.rdb 文件,这时我们将 redis 关闭并重启,会发现之前的数据都还在,这就是 AOF 备份的结果。

AOF 备份的几个关键点

1.通过上面的介绍,小伙伴们了解到 appendfsync 的取值一共有三种,我们在项目中首选 everysec,always 选项会严重降低 redis 性能。
2.使用 everysec ,最坏的情况下我们可能丢失1秒的数据。

AOF 文件的重写与压缩

AOF 备份有很多明显的优势,当然也有劣势,那就是文件大小。随着系统的运行,AOF 的文件会越来越大,甚至把整个电脑的硬盘填满,AOF 文件的重写与压缩机制可以在一定程度上缓解这个问题。
当 AOF 的备份文件过大时,我们可以向 redis 发送一条 bgrewriteaof 命令进行文件重写,如下:

127.0.0.1:6379> BGREWRITEAOF
Background append only file rewriting started
(0.71s)

bgrewriteaof 的执行原理和我们上文说的 bgsave 的原理一致,这里我就不再赘述,因此 bgsave 执行过程中存在的问题在这里也一样存在。

bgrewriteaof 也可以自动执行,自动执行时间则依赖于 auto-aof-rewrite-percentage 和 auto-aof-rewrite-min-size 配置,auto-aof-rewrite-percentage 100 表示当目前 aof 文件大小超过上一次重写时的 aof 文件大小的百分之多少时会再次进行重写,如果之前没有重写,则以启动时的 aof 文件大小为依据,同时还要求 AOF 文件的大小至少要大于 64M(auto-aof-rewrite-min-size 64mb)。

最佳实践

  1. 如果 redis 只做缓存服务器,那么可以不使用任何持久化方式。
  2. 同时开启两种持久化方式,在这种情况下,当 redis 重启的时候会优先载入 AOF 文件来恢复原始的数据, 因为在通常情况下 AOF 文件保存的数据集要比 RDB 文件保存的数据集要完整;RDB 的数据不完整时,同时使用两者时服务器重启也只会找 AOF 文件。那要不要只使用 AOF 呢? 作者建议不要,因为 RDB 更适合用于备份数据库( AOF 在不断变化不好备份), 快速重启,而且不会有 AOF 可能潜在的 bug ,留着作为一个万一的手段。
  3. 因为 RDB 文件只用作后备用途,建议只在 slave 上持久化 RDB 文件,而且只要 15 分钟备份一次就够了,只保留 save 900 1 这条规则。
  4. 如果 Enalbe AOF,好处是在最恶劣情况下也只会丢失不超过两秒数据,启动脚本较简单只 load 自己的 AOF 文件就可以了。代价一是带来了持续的 IO,二是 AOF rewrite 的最后将 rewrite 过程中产生的新数据写到新文件造成的阻塞几乎是不可避免的。只要硬盘许可,应该尽量减少 AOF rewrite 的频率,AOF 重写的基础大小默认值 64M 太小了,可以设到 5G 以上。默认超过原大小 100% 大小时重写可以改到适当的数值。
  5. 如果不 Enable AOF ,仅靠 Master-Slave Replication 实现高可用性也可以。能省掉一大笔 IO 也减少了 rewrite 时带来的系统波动。代价是如果 Master/Slave 同时倒掉,会丢失十几分钟的数据,启动脚本也要比较两个 Master/Slave 中的 RDB 文件,载入较新的那个。

主从复制

主从复制可以在一定程度上扩展 redis 性能,redis 的主从复制和关系型数据库的主从复制类似,从机能够精确的复制主机上的内容。实现了主从复制之后,一方面能够实现数据的读写分离,降低 master 的压力,另一方面也能实现数据的备份。

配置方式

假设我有三个 redis 实例,地址分别如下:

192.168.248.128:6379  
192.168.248.128:6380  
192.168.248.128:6381

即同一台服务器上三个实例,配置方式如下:

1.将 redis.conf 文件更名为 redis6379.conf ,方便我们区分,然后把 redis6379.conf 再复制两份,分别为 redis6380.conf 和 redis6381.conf 。如下:

img

2.打开 redis6379.conf ,将如下配置均加上 6379,(默认是 6379 的不用修改),如下:

port 6379
pidfile /var/run/redis_6379.pid
logfile "6379.log"
dbfilename dump6379.rdb
appendfilename "appendonly6379.aof"

3.同理,分别打开 redis6380.conf和redis6381.conf 两个配置文件,将第二步涉及到 6379 的分别改为 6380 和 6381 。
4.输入如下命令,启动三个 redis 实例:

[root@localhost redis-4.0.8]# redis-server redis6379.conf
[root@localhost redis-4.0.8]# redis-server redis6380.conf
[root@localhost redis-4.0.8]# redis-server redis6381.conf

5.输入如下命令,分别进入三个实例的控制台:

[root@localhost redis-4.0.8]# redis-cli -p 6379
[root@localhost redis-4.0.8]# redis-cli -p 6380
[root@localhost redis-4.0.8]# redis-cli -p 6381

此时我就成功配置了三个 redis 实例了。

6.假设在这三个实例中,6379 是主机,即 master,6380 和 6381 是从机,即 slave,那么如何配置这种实例关系呢,很简单,分别在 6380 和 6381 上执行如下命令:

127.0.0.1:6381> SLAVEOF 127.0.0.1 6379
OK

这一步也可以通过在两个从机的 redis.conf 中添加如下配置来解决:

slaveof 127.0.0.1 6379

OK,主从关系搭建好后,我们可以通过如下命令可以查看每个实例当前的状态,如下:

127.0.0.1:6379> INFO replication
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6380,state=online,offset=56,lag=1
slave1:ip=127.0.0.1,port=6381,state=online,offset=56,lag=0
master_replid:26ca818360d6510b717e471f3f0a6f5985b6225d
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:56
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:56

我们可以看到 6379 是一个主机,上面挂了两个从机,两个从机的地址、端口等信息都展现出来了。如果我们在 6380 上执行 INFO replication ,显示信息如下:

127.0.0.1:6380> INFO replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:up
master_last_io_seconds_ago:6
master_sync_in_progress:0
slave_repl_offset:630
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:26ca818360d6510b717e471f3f0a6f5985b6225d
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:630
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:630

我们可以看到 6380 是一个从机,从机的信息以及它的主机的信息都展示出来了。

7.此时,我们在主机中存储一条数据,在从机中就可以 get 到这条数据了。

主从复制注意点

  1. 如果主机已经运行了一段时间了,并且了已经存储了一些数据了,此时从机连上来,那么从机会将主机上所有的数据进行备份,而不是从连接的那个时间点开始备份。
  2. 配置了主从复制之后,主机上可读可写,但是从机只能读取不能写入(可以通过修改 redis.conf 中 slave-read-only 的值让从机也可以执行写操作)。
  3. 在整个主从结构运行过程中,如果主机不幸挂掉,重启之后,他依然是主机,主从复制操作也能够继续进行。

复制原理

每一个 master 都有一个 replication ID ,这是一个较大的伪随机字符串,标记了一个给定的数据集。每个 master 也持有一个偏移量,master 将自己产生的复制流发送给 slave 时,发送多少个字节的数据,自身的偏移量就会增加多少,目的是当有新的操作修改自己的数据集时,它可以以此更新 slave 的状态。复制偏移量即使在没有一个 slave 连接到 master 时,也会自增,所以基本上每一对给定的 Replication ID, offset 都会标识一个 master 数据集的确切版本。当 slave 连接到 master 时,它们使用 PSYNC 命令来发送它们记录的旧的 master replication ID 和它们至今为止处理的偏移量。通过这种方式,master 能够仅发送 slave 所需的增量部分。但是如果 master 的缓冲区中没有足够的命令积压缓冲记录,或者如果 slave 引用了不再知道的历史记录 (replication ID) ,则会转而进行一个全量重同步:在这种情况下,slave 会得到一个完整的数据集副本,从头开始(参考 redis 官网)。

简单来说,就是以下几个步骤:

  1. slave 启动成功连接到 master 后会发送一个 sync 命令。
  2. Master 接到命令启动后台的存盘进程,同时收集所有接收到的用于修改数据集命令。
  3. 在后台进程执行完毕之后,master 将传送整个数据文件到 slave ,以完成一次完全同步。
  4. 全量复制:而 slave 服务在接收到数据库文件数据后,将其存盘并加载到内存中。
  5. 增量复制:Master 继续将新的所有收集到的修改命令依次传给 slave ,完成同步。
  6. 但是只要是重新连接 master ,一次完全同步(全量复制)将被自动执行。

一场接力赛

在上篇文章中,我们搭建的主从复制模式是下面这样的:

img

实际上,一主二仆的主从复制,我们可以搭建成下面这种结构:

img

搭建方式很简单,在前文基础上,我们只需要修改 6381 的 master 即可,在 6381 实例上执行如下命令,让 6381 从 6380 实例上复制数据,如下:

127.0.0.1:6381> SLAVEOF 127.0.0.1 6380
OK

此时,我们再看 6379 的 slave ,如下:

127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=127.0.0.1,port=6380,state=online,offset=0,lag=1
master_replid:4a38bbfa37586c29139b4ca1e04e8a9c88793651
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:0

只有一个 slave,就 6380 ,我们再看 6380 的信息,如下:

127.0.0.1:6380> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:up
master_last_io_seconds_ago:1
master_sync_in_progress:0
slave_repl_offset:70
slave_priority:100
slave_read_only:1
connected_slaves:1
slave0:ip=127.0.0.1,port=6381,state=online,offset=70,lag=0
master_replid:4a38bbfa37586c29139b4ca1e04e8a9c88793651
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:70
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:70

6380 此时的角色是一个从机,它的主机是 6379,但是 6380 自己也有一个从机,那就是 6381 .此时我们的主从结构如下图:

img

哨兵模式

结合上篇文章,我们一共介绍了两种主从模式了,但是这两种,不管是哪一种,都会存在这样一个问题,那就是当主机宕机时,就会发生群龙无首的情况,如果在主机宕机时,能够从从机中选出一个来充当主机,那么就不用我们每次去手动重启主机了,这就涉及到一个新的话题,那就是哨兵模式。

所谓的哨兵模式,其实并不复杂,我们还是在我们前面的基础上来搭建哨兵模式。假设现在我的 master 是 6379 ,两个从机分别是 6380 和 6381 ,两个从机都是从 6379 上复制数据。先按照上文的步骤,我们配置好一主二仆,然后在 redis 目录下打开 sentinel.conf 文件,做如下配置:

sentinel monitor mymaster 127.0.0.1 6379 1

其中 mymaster 是给要监控的主机取的名字,随意取,后面是主机地址,最后面的 2 表示有多少个 sentinel 认为主机挂掉了,就进行切换(我这里只有一个,因此设置为1)。好了,配置完成后,输入如下命令启动哨兵:

redis-sentinel sentinel.conf

然后启动我们的一主二仆架构,启动成功后,关闭 master,观察哨兵窗口输出的日志,如下:

img

小伙伴们可以看到,6379 挂掉之后,redis 内部重新举行了选举,6380 重新上位。此时,如果 6379 重启,也不再是扛把子了,只能屈身做一个 slave 了。

注意问题

由于所有的写操作都是先在 Master 上操作,然后同步更新到 Slave 上,所以从 Master 同步到 Slave 机器有一定的延迟,当系统很繁忙的时候,延迟问题会更加严重,Slave 机器数量的增加也会使这个问题更加严重。因此我们还需要集群来进一步提升 redis 性能,这个问题我们将在后面说到。

缓存穿透、缓存雪崩、缓存击穿的区别和解决方案

一、正常处理流程

先读cache,如果数据命中则返回;如果数据未命中则读db;将db中读取出来的数据入缓存。

private Map cache = new ConcrrentHashMap();
Object getFromCache(String key){
	Object value = cache.get(key);
	if(value==null){
		value = getByKey(key);
		cache.put(key,value);
	}java
	return value;
}

二、常见问题

1、缓存穿透

访问一个不存在的key,缓存不起作用,请求会穿透到DB,流量大时DB会挂掉。

解决方案

  1. 采用布隆过滤器,使用一个足够大的bitmap,用于存储可能访问的key,不存在的key直接被过滤;
  2. 拦截器,id<=0的直接拦截。
  3. 从cache和db都取不到,可以将key-value写为key-null,设置较短过期时间,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击。
  4. 利用互斥锁,缓存失效的时候,先去获得锁,得到锁了,再去请求数据库。没得到锁,则休眠一段时间重试
  5. 采用异步更新策略,无论key是否取到值,都直接返回。value值中维护一个缓存失效时间,缓存如果过期,异步起一个线程去读数据库,更新缓存。需要做缓存预热(项目启动前,先加载缓存)操作。

2、缓存击穿

一个存在的key,在缓存过期的一刻,同时有大量的请求,这些请求都会击穿到DB,造成瞬时DB请求量大、压力骤增。

解决方案

  1. 设置热点数据永远不过期。
  2. 加互斥锁。

3、缓存雪崩

​ 大量的key设置了相同的过期时间,导致在缓存在同一时刻全部失效,造成瞬时DB请求量大、压力骤增,引起雪崩。

解决方案

  1. 缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。
  2. 如果缓存数据库是分布式部署,将热点数据均匀分布在不同的缓存数据库中。
  3. 设置热点数据永远不过期。
  4. 使用互斥锁,但是该方案吞吐量明显下降了。
  5. 双缓存。我们有两个缓存,缓存A和缓存B。缓存A的失效时间为20分钟,缓存B不设失效时间。自己做缓存预热操作。然后细分以下几个小点:
  • 从缓存A读数据,有则直接返回
  • A没有数据,直接从B读数据,直接返回,并且异步启动一个更新线程。
  • 更新线程同时更新缓存A和缓存B。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值