我梦笔记我梦笔记
Home
Home
  • MySQL
  • Java
  • JVM (黑马)
  • Spring
  • SpringBoot
  • Redis
  • 数据结构
  • 设计模式
  • Linux
  • Nginx
  • VUE.JS
  • Emby 伪站破解认证

Redis

doocs技术社区文档

缓存雪崩

缓存机器发生了意外,全盘宕机,此时每秒有非常多的请求打到数据库,数据库扛不住,就挂了。

缓存雪崩的事前事中事后的解决方案如下:

  • 事前:Redis 高可用,主从+哨兵,Redis cluster,避免全盘崩溃。
  • 事中:本地 ehcache 缓存 + hystrix 限流&降级,避免 MySQL 被打死。
  • 事后:Redis 持久化,一旦重启,自动从磁盘上加载数据,快速恢复缓存数据。

缓存穿透

故意请求缓存中不可能存在的数据,比如说数据库记录的id是从1开始的,结果我请求过来的ID全都是负数,那缓存中就不可能有啊,直接视缓存如无物,直接打给数据库,这种就很容易把数据库打死了。解决方法也很简单,每次系统中没查到,就写一个空值到缓存中去,这种下次来就直接找缓存了。如果它用的都是不同的负数ID,那设置空值就没有效果了,可以使用布隆过滤器。

  • 请求数据的 key 不存在于布隆过滤器中,可以确定数据就一定不会存在于数据库中,系统可以立即返回不存在。
  • 请求数据的 key 存在于布隆过滤器中,则继续再向缓存中查询。

缓存击穿

某个热点key,访问非常频繁,正处于集中式高并发访问的情况,那当这个key在失效的瞬间,大量请求直接打到数据库。

  • 如果这个数据基本上永远不会变的,可以尝试设置永不过期
  • 用定时线程主动给它续期
  • 用锁,给查询数据库的代码上锁,保证只有极少的请求能够直接访问数据库,其它请求稍后 查询缓存就可以了。

多个Redis实例并发竞争

多个Redis实例同时并发写一个key

  • 分布式锁
  • 用CAS乐观锁,在数据库保存一个时间戳字段,每次更新缓存的时候都对比一下数据库的时间戳是不是要比缓存的时间戳要新,如果是就可以写,否则就说明数据库的是旧数据,不能写缓存。

常用数据类型和使用场景

doocs 文档

Redis过期策略

Redis 过期策略是:定期删除+惰性删除。

持久化

Redis 的持久化有哪几种方式?不同的持久化机制都有什么优缺点?持久化机制具体底层是如何实现的?

Redis的持久化有两种方式,分别是

  • RDB:周期性的持久化方式,也就是每隔一段时间就持久化一次。
  • AOF:对每条写入命令作为日志,以 append-only 追加的模式写入一个日志文件中,在 Redis 重启的时候,通过回放 AOF 日志中的写入指令来重新构建整个数据。

如果同时使用了两种持久化方式,那么在Redis重启的时候,会优先使用AOF来重新构建数据,因为AOF持久化的数据更加完整,一般都是每秒保存一次,也就是说如果最多只会丢失 1 秒钟的数据。

优缺点:

  • RDB适合做冷备份,可以发送到一些远程存储系统上去,以防不测。
  • RDB恢复速度快,相对于AOF来说,RDB的恢复速度更快。
  • RDB数据丢失较多,它可能要隔很久才备份一次,而AOF是每秒钟保存一次。
  • AOF适合做灾难性的紧急恢复。
  • 两种方式的性能也都不错。

如何保证缓存和数据库双写一致性

你只要用缓存,就可能会涉及到缓存与数据库双存储双写,你只要是双写,就一定会有数据一致性的问题

分布式锁

分布式锁是什么?为什么需要分布式锁?

分布式锁是保证分布式集群环境下多线程并发安全性的一种手段。
在没有分布式锁之前,锁只能保证单个应用内多线程的并发安全。在如今全民都分布式的情况下,普通的锁能够发挥的作用比较有限了。

分布式锁的特征

  • 高性能
  • 可重入
  • 防死锁
  • 互斥性

分布式常见应用场景

  • 防止缓存击穿
  • 任务调度
  • 秒杀减库存防止超卖这种
  • 保证接口幂等性

Redis几种部署方式

  • 单机:部署简单,但是极度害怕单点故障
  • 哨兵:一主多从,主从切换,比较稳。但是锁写到Master后,还没同步到Slave呢,Master挂了Slave选举成了Master,但是Slave里没有锁,其他线程再次能上锁了。不安全。
  • 集群:制作了slot分片,锁还是只写到master上,跟哨兵模式的问题相同。
  • 红锁:它是 Redis 实现分布式锁相对安全可靠的一种手段。红锁的核心思路是:搞几个独立的Master,比如5个,然后挨个加锁,只要超过一半以上(这里是5 / 2 + 1 = 3个)就代表加锁成功,然后释放锁的时候也逐台释放。这样的好处在于一台Master挂了的话,还有其他的,所以不耽误,看起来好像完美解决了上面的问题。但是它极其麻烦,而且对性能的开销也是其他锁的 N 倍,因为需要逐个加锁,需要多次与 Redis 通信。

Redis 锁结构

锁其实是一个 redis 的 hash 结构,里面至少包含有:

  • 线程id(uuid)
  • 过期时间
  • 重入次数

读写锁的话还有 锁类型(读锁还是写锁),且读锁可以有多个线程id,而且还要为每个线程id维护一个单独的过期时间。续期的时候不仅续期整个锁的过期时间,还要续期每个线程对应的过期时间。

Redis 加锁流程

  • 非公平锁:如果资源还没有锁,就上锁,添加过期时间。如果有锁,就判断是否是当前线程持有的锁,如果是就进行锁重入,重入次数加1,锁续期。如果不是当前线程持有的锁,那就是互斥了,就阻塞等待重试嘛。

  • 公平锁:用zset和List按照加锁顺序进行排序,当持有锁的线程执行完之后,处在队列第一位的线程就能拿到锁。

Redission

WatchDog 看门狗机制

客户端第一次使用无参的lock()方法时,redis会另外启动一个线程检查锁过期时间,当时间超过三分之一的时候就会进行锁续期,续期使用的是lua脚本,保证了续期的原子性,而且这个专门用来续期的线程主要使用的是netty的时间轮容器,只需要一个线程就可以,对服务器性能消耗极小。另外一个需要注意的刚才也说到了,只有使用无参的lock()方法时才会开启看门狗续期,也就是说并不是所有lock方法都会使用看门狗续期,只有没自定义过期时间的才会开启,默认过期时间是30秒。

最近更新:: 2026/1/9 19:37
Contributors: womeng
Prev
SpringBoot
Next
数据结构