Redis

雪崩、击穿、穿透

在这里插入图片描述

缓存雪崩

大量数据同时过期(失效)、Redis故障宕机、大量请求访问到数据库、导致系统崩溃

在这里插入图片描述

双⼗⼀期间,所有⽤户⼀打开淘宝就是进⼊⾸⻚,⾸⻚的压⼒⾮常⼤,为了提⾼并发,将⽹站⾸⻚数据都缓 存到 Redis ⾥,所有的 Redis key 失效时间都是3⼩时。 双⼗⼀当天⼤量⽤户剁⼿狂欢,这时候3个⼩时过去了, Redis ⾥⾸⻚的 key 缓存全部失效,这时候 Redis ⾥查询不到数据了,只能去数据库中查询,造成数据库⽆法响应挂掉。 ⽤户进不去⾸⻚没法剁⼿了,⻢爸爸就不开⼼了,这个程序员就外派到⾮洲了。

⼀句话总结 在⾼并发下,⼤量缓存 key 在同⼀时间失效,⼤量请求直接落在数据库上,导致数据库宕机

大量数据同时过期
  • 均匀设置过期时间
  • 互斥锁
  • 双key策略
  • 后台更新缓存
均匀更新过期时间

如果要给缓存数据设置过期时间,应该避免将⼤量的数据设置成同⼀个过期时间。我们可以在对缓存数据设置过期 时间时,给这些数据的过期时间加上⼀个随机数,这样就保证数据不会在同⼀时间过期

互斥锁

当处理用户请求时、如果发现访问的数据不在Redis中、就加一个互斥锁、保证同一时间只有一个请求来构建缓存、其他请求等该请求缓存构建完成、释放锁后、再直接从缓存中获取数据。

注意:设置互斥锁时应设置过期时间、避免第⼀个请求拿到了锁后因意外一直发生阻塞、其他请求也拿不到锁。

双key策略

我们对缓存数据可以使⽤两个 key ,⼀个是主 key ,会设置过期时间,⼀个是备 key ,不会设置过期,它们只是 key 不⼀样,但是 value 值是⼀样的,相当于给缓存数据做了个副本。

当业务线程访问不到 主key 的缓存数据时,就直接返回 备key 的缓存数据,然后在更新缓存的时候,同时更新 主 key 和 备key 的数据

后台更新策略

业务线程不负责更新缓存、缓存不设置有效期、将缓存更新的任务交给后台线程定时更新。

Redis故障宕机
服务熔断或请求限流机制

因为 Redis 故障宕机⽽导致缓存雪崩问题时,我们可以启动服务熔断机制,暂停业务应⽤对缓存服务的访问,直接 返回错误,不⽤再继续访问数据库,从⽽降低对数据库的访问压⼒,保证数据库系统的正常运⾏,然后等到 Redis 恢复正常后,再允许业务访问缓存服务。

服务熔断机制是保护数据库的正常允许,但是暂停了业务应⽤访问缓存系统,全部业务都⽆法正常⼯作。

为了减少对业务的影响,我们可以启动请求限流机制,只将少部分发送到数据库进⾏处理,再多的请求就在⼊⼝直 接拒绝服务,等到 Redis 恢复正常并把缓存预热完后,再解除请求限流的机制。

构建 Redis 缓存⾼可靠集群

服务熔断或请求限流机制是缓存雪崩发⽣后的应对⽅案,我们最好通过主从节点的⽅式构建 Redis 缓存⾼可靠集 群。 如果 Redis 缓存的主节点故障宕机,从节点可以切换成主节点,继续提供缓存服务,避免了由于 Redis 故障宕 机⽽导致缓存雪崩问题。

缓存击穿

在这里插入图片描述

热点数据过期、大量请求访问热点数据、就⽆法从缓存中读取,直接访问数据库, 数据库很容易就被⾼并发的请求冲垮,这就是缓存击穿的问题。如秒杀活动

应对缓存击穿可以采取前⾯说到的两种⽅案

  • 互斥锁⽅案,保证同⼀时间只有⼀个业务线程更新缓存,未能获取互斥锁的请求,要么等待锁释放后重新读取 缓存,要么就返回空值或者默认值。

  • 不给热点数据设置过期时间,由后台异步更新缓存,或者在热点数据准备要过期前,提前通知后台线程更新缓 存以及重新设置过期时间

缓存穿透

用户要访问的数据即不在缓存中、也不在数据库中、导致无法构造缓存、请求一直命中数据库

在这里插入图片描述

缓存穿透发⽣的情况
  • 业务误操作,缓存中的数据和数据库中的数据都被误删除了,所以导致缓存和数据库中都没有数据。

  • ⿊客恶意攻击,故意⼤量访问某些读取不存在数据的业务。 应对缓存穿透的⽅案 应对缓存穿透的⽅案,

常⻅的⽅案有三种:
  • ⾮法请求的限制。

  • 缓存空值或者默认值。

  • 使⽤布隆过滤器快速判断数据是否存在,避免通过查询数据库来判断数据是否存在。

⾮法请求的限制

当有⼤量恶意请求访问不存在数据的时候,也会发⽣缓存穿透,因此在 API ⼊⼝处我们要判断请求参数是否合 理,请求参数是否含有⾮法值、请求字段是否存在,如果判断出是恶意请求就直接返回错误,避免进⼀步访问缓存 和数据库.

缓存空值或者默认值

当我们线上业务发现缓存穿透的现象时,可以针对查询的数据,在缓存中设置⼀个空值或者默认值,这样后续请求 就可以从缓存中读取到空值或者默认值,返回给应⽤,⽽不会继续查询数据库

总结

雪崩是⼤⾯积的 key 缓存失效;穿透是 Redis ⾥不存在这个缓存 key ;击穿是 Redis 某⼀个热点 key 突然 失效,最终的受害者都是数据库

Redis持久化

持久化

将数据写入持久化内存,如硬盘

1、RDB

2、AOF

3、无持久化(不建议)

4、RDB+AOF

持久化的意义

核心是实现数据备份、数据恢复、高可用

在一般场景中会将数据库部分数据放到redis中进行缓存、减小数据库库=压力、同时也会放入一些非数据库数据。实现数据备份时一般将redis数据给磁盘写一份、再将磁盘数据上传到云服务器上。当redis坏掉之后、也可以从云服务器拿到数据给redis。这样可以保证不会产生数据全部丢失的情况。

如果不使用持久化进行数据备份。会产生当redis坏掉后。数据全部丢失。此时就要从数据库重新获取数据。就会产生、雪崩、击穿、穿透等导致数据库压力过大宕机。此时数据就会彻底丢失。无法恢复。

RDB

在指定时间间隔内将内存数据写入磁盘,即时间快照 数据恢复时将快照文件读取到内存中。

Redis会单独创建一个fork一个子进程来进行持久化、先将数据写入到一个临时文件( dump.rdb )中,待持久化过程结束后,用本次的临时文件替换上次持久化后的文件。

fork 函数的作⽤是复制⼀个与当前进程⼀样的进程,新进程的所有数据数值都和原进程⼀致,但是⼀个全新的进 程,并作为原进程的⼦进程。

在这里插入图片描述

Redis 服务器在处理 bgsave 采⽤⼦线程进⾏ IO 写⼊,⽽主进程仍然可以接收其他请求,但创建⼦进程是同步 阻塞的,此时不接受其他请求

RDB的优缺点

优点:
  • 数恢复快
  • 体积小
  • 数据备份使用子进程、对Redis服务性能影响小
缺点:
  • 在一定时间间隔内进行备份、当Redis意外宕机,会丢失最后一次修改的数据、无法做到秒级持久化
  • fork进程时,会占用一定内存空间
  • RDB文件是二进制文件、没有可读性

AOF

将客户端的每一个写操作命令以日志的形式记录下来追加到 appendonly.aof 的⽂件末尾,在 Redis 服务器 重启时,会加载 aof ⽂件中的所有命令,来达到数据恢复的⽬的
在这里插入图片描述

AOF重写机制

AOF 持久化,会把每次写命令都追加到 appendonly.aof ⽂件中,当⽂件过⼤, Redis 的数据恢复时间就会变 ⻓,因此加⼊重写策略对 aof ⽂件进⾏重写,⽣成⼀个恢复当前数据的最少命令集。

#⽐如对同⼀个key进⾏多次写命令
set key 5
incr key
incrby key 500
#重写后就变为 set key 50

AOF重写流程

主进程fork 出⼀个⼦进程进⾏ AOF ⽂件的重写,⼦进程重写完毕后,主进程把⼦进程重写期间,其他客户端产 ⽣的写请求,追加到 AOF⽂件中,替换旧⽂件。 AOF 的rewirte重写和 RDB 的bgsave都是由⽗进程 fork 出⼀个⼦进程来执⾏的。重写是直接把当前内存 的数据⽣成对应的命令,⽽不是读取旧 AOF ⽂件进⾏命令合并
在这里插入图片描述

AOF优缺点

优点

  • 数据安全性高、不易丢失数据
  • AOF文件有序保存了所有写操作、可读性强

缺点

  • AOF方式生成文件体积大
  • 数据恢复速度比RDB

Redis事务

由于 Redis 在执⾏多条命令时,可能会被其他命令插队,从⽽影响预期结果。所以 Redis 中为保证完成某个功 能的⼀系列指令串在执⾏的过程中不被其他指令串所影响,提供了事务处理机制

一个指令执行队列、将一系列预定义指令包装为一个整体、在执行的时候按照顺序执行、不允许其他命令执行、redis事务没有回滚

开启事务

multi、设置事务的开始位置、在这个指令后所有的指令都会加入到redis事务中、形成一个事务队列

执行事务(结束事务)

exec、用于结束并执行事务队列中的指令、与multi成对使用。
注意:加入事务队列的指令并不会立刻执行、而是加入到了执行队列中、只有事务结束时才会执行

取消事务

dicard、必须在multi后执行 exec前执行。用于取消当前事务。

在这里插入图片描述

注意事项

注意1:当事务中出现语法错误、自动取消当前事务。

127.0.0.1:6380> multi
OK
127.0.0.1:6380> set name abc
QUEUED
127.0.0.1:6380> sets age 20 #语法错误,取消事务
(error) ERR unknown command 'sets'
127.0.0.1:6380> exec
(error) EXECABORT Transaction discarded because of previous errors

注意2:出现语意错误非语法错误、事务不会停止、也不会回滚、会执行除了该语意错误的其他指令。

127.0.0.1:6380> multi
OK
127.0.0.1:6380> set name admin
QUEUED
127.0.0.1:6380> get name
QUEUED
127.0.0.1:6380> set age 20
QUEUED
127.0.0.1:6380> get age
QUEUED
127.0.0.1:6380> lpush age aaa bbb ccc
QUEUED
127.0.0.1:6380> get age
QUEUED
127.0.0.1:6380> exec
1) OK
2) "admin"
3) OK
4) "20"
5) (error) WRONGTYPE Operation against a key holding the wrong kind of value #执
⾏错误
6) "20"

Redis 锁

watch监控锁

当一个数据需要改变时,可能会出现多人同时改变数据的情况。如果其中一人改变了,则其他人就不能再改变数据(即数据只能被更改一次)此时我们就需要使⽤监控锁,对要修改的数据监控起来,⼀旦数据发⽣改变(已被其他⼈修改),则 ⾃动终⽌事务的执⾏

对指定 key 添加监控锁,在执⾏事务前如果 key 的值发⽣改变,⾃动终⽌事务的执⾏

watch key[key1 key2 .....]

注意:watch监控锁只是监控,并不能像java中一样对数据进行锁定,只在事务中有作用。

分布式锁

当多个线程需要同时操作一个数据时,为避免数据异常,一个线程操作时,将数据锁起来,使用结束后将锁打开。此时其他线程才可以继续进行操作。Redis中使用分布式锁实现该场景。

Redis 中并没有分布式锁的实现,我们可以通过 setnx 来设计⼀个分布式锁

# 添加⼀个key,该key为分布式锁,我们知道setnx在设置数据时如果数据存在则返回0
# 设置该数据为锁,其他客户端要操作数据前先通过该指令的返回值检测如果返回值为0则表示当前数据已被锁定不能
操作,如果返回值为1表示加锁,然后操作
setnx lock-num 1
# 对加锁的数据使⽤后要解锁,通过del lock-num移除数据的⽅式实现解锁过程
del lock-num

死锁

由于模拟分布式锁机制可以实现排他性。但是使用结束后必须解锁(谁加锁,谁解锁),如果数据使用结束后没有解锁,就会形成死锁,

设计分布式锁时不允许出现死锁,设置分布式锁时就应使用失效时间进行设置,到达时间后自动解锁。

# 设置分布式锁
set key value [ex seconds] [px milliseconds] [nx

加锁,然后操作
setnx lock-num 1

对加锁的数据使⽤后要解锁,通过del lock-num移除数据的⽅式实现解锁过程

del lock-num


### 死锁

由于模拟分布式锁机制可以实现排他性。但是使用结束后必须解锁(谁加锁,谁解锁),如果数据使用结束后没有解锁,就会形成死锁,

设计分布式锁时不允许出现死锁,设置分布式锁时就应使用失效时间进行设置,到达时间后自动解锁。

```java
# 设置分布式锁
set key value [ex seconds] [px milliseconds] [nx

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值