|
@@ -0,0 +1,205 @@
|
|
|
+## redis过期删除策略
|
|
|
+
|
|
|
+Redis 是可以对 key 设置过期时间的,因此需要有相应的机制将已过期的键值对删除,而做这个工作的就是过期键值删除策略。
|
|
|
+
|
|
|
+在说 Redis 过期删除策略之前,先跟大家介绍下,常见的三种过期删除策略:
|
|
|
+
|
|
|
+### 定时删除
|
|
|
+
|
|
|
+在设置 key 的过期时间时,同时创建一个定时事件,当时间到达时,由事件处理器自动执行 key 的删除操作**
|
|
|
+
|
|
|
+定时删除策略的**优点**:
|
|
|
+
|
|
|
+- 可以保证过期 key 会被尽快删除,也就是内存可以被尽快地释放。因此,定时删除对内存是最友好的。
|
|
|
+
|
|
|
+定时删除策略的**缺点**:
|
|
|
+
|
|
|
+- 在过期 key 比较多的情况下,删除过期 key 可能会占用相当一部分 CPU 时间,在内存不紧张但 CPU 时间紧张的情况下,将 CPU 时间用于删除和当前任务无关的过期键上,无疑会对服务器的响应时间和吞吐量造成影响。所以,定时删除策略对 CPU 不友好。
|
|
|
+
|
|
|
+### 惰性删除
|
|
|
+
|
|
|
+不主动删除过期键,每次从数据库访问 key 时,都检测 key 是否过期,如果过期则删除该 key。**
|
|
|
+
|
|
|
+惰性删除策略的**优点**:
|
|
|
+
|
|
|
+- 因为每次访问时,才会检查 key 是否过期,所以此策略只会使用很少的系统资源,因此,惰性删除策略对 CPU 时间最友好。
|
|
|
+
|
|
|
+惰性删除策略的**缺点**:
|
|
|
+
|
|
|
+- 如果一个 key 已经过期,而这个 key 又仍然保留在数据库中,那么只要这个过期 key 一直没有被访问,它所占用的内存就不会释放,造成了一定的内存空间浪费。所以,惰性删除策略对内存不友好。
|
|
|
+
|
|
|
+### 定期删除:
|
|
|
+每隔一段时间「随机」从数据库中取出一定数量的 key 进行检查,并删除其中的过期key。
|
|
|
+
|
|
|
+定期删除策略的**优点**:
|
|
|
+
|
|
|
+- 通过限制删除操作执行的时长和频率,来减少删除操作对 CPU 的影响,同时也能删除一部分过期的数据减少了过期键对空间的无效占用。
|
|
|
+
|
|
|
+定期删除策略的**缺点**:
|
|
|
+
|
|
|
+- 内存清理方面没有定时删除效果好,同时没有惰性删除使用的系统资源少。
|
|
|
+- 难以确定删除操作执行的时长和频率。如果执行的太频繁,定期删除策略变得和定时删除策略一样,对CPU不友好;如果执行的太少,那又和惰性删除一样了,过期 key 占用的内存不会及时得到释放。
|
|
|
+
|
|
|
+**Redis 选择「惰性删除+定期删除」这两种策略配和使用**
|
|
|
+
|
|
|
+## redis内存淘汰策略
|
|
|
+
|
|
|
+*1、不进行数据淘汰的策略*
|
|
|
+
|
|
|
+**noeviction**(Redis3.0之后,默认的内存淘汰策略) :它表示当运行内存超过最大设置内存时,不淘汰任何数据,这时如果有新的数据写入,则会触发 OOM,但是如果没用数据写入的话,只是单纯的查询或者删除操作的话,还是可以正常工作。
|
|
|
+
|
|
|
+*2、进行数据淘汰的策略*
|
|
|
+
|
|
|
+针对「进行数据淘汰」这一类策略,又可以细分为「在设置了过期时间的数据中进行淘汰」和「在所有数据范围内进行淘汰」这两类策略。
|
|
|
+
|
|
|
+在设置了过期时间的数据中进行淘汰:
|
|
|
+
|
|
|
+- **volatile-random**:随机淘汰设置了过期时间的任意键值;
|
|
|
+- **volatile-ttl**:优先淘汰更早过期的键值。
|
|
|
+- **volatile-lru**(Redis3.0 之前,默认的内存淘汰策略):淘汰所有设置了过期时间的键值中,最久未使用的键值;
|
|
|
+- **volatile-lfu**(Redis 4.0 后新增的内存淘汰策略):淘汰所有设置了过期时间的键值中,最少使用的键值
|
|
|
+
|
|
|
+## Redis 持久化
|
|
|
+
|
|
|
+Redis 共有三种数据持久化的方式:
|
|
|
+
|
|
|
+- **AOF 日志**:每执行一条写操作命令,就把该命令以追加的方式写入到一个文件里;
|
|
|
+- **RDB 快照**:将某一时刻的内存数据,以二进制的方式写入磁盘;
|
|
|
+- **混合持久化方式**:Redis 4.0 新增的方式,集成了 AOF 和 RBD 的优点;
|
|
|
+
|
|
|
+### AOF
|
|
|
+
|
|
|
+![img](assets/98987d9417b2bab43087f45fc959d32a-20230309232253633.png)
|
|
|
+
|
|
|
+> AOF 日志过大,会触发什么机制?
|
|
|
+
|
|
|
+Redis 为了避免 AOF 文件越写越大,提供了 **AOF 重写机制**,当 AOF 文件的大小超过所设定的阈值后,Redis 就会启用 AOF 重写机制,来压缩 AOF 文件。
|
|
|
+
|
|
|
+AOF 重写机制是在重写时,读取当前数据库中的所有键值对,然后将每一个键值对用一条命令记录到「新的 AOF 文件」,等到全部记录完后,就将新的 AOF 文件替换掉现有的 AOF 文件。
|
|
|
+
|
|
|
+### RDB
|
|
|
+
|
|
|
+RDB 快照就是记录某一个瞬间的内存数据,记录的是实际数据,
|
|
|
+
|
|
|
+Redis 提供了两个命令来生成 RDB 文件,分别是 save 和 bgsave,他们的区别就在于是否在「主线程」里执行:
|
|
|
+
|
|
|
+- 执行了 save 命令,就会在主线程生成 RDB 文件,由于和执行操作命令在同一个线程,所以如果写入 RDB 文件的时间太长,**会阻塞主线程**;
|
|
|
+- 执行了 bgsave 命令,会创建一个子进程来生成 RDB 文件,这样可以**避免主线程的阻塞**;
|
|
|
+
|
|
|
+Redis 还可以通过配置文件的选项来实现每隔一段时间自动执行一次 bgsave 命令,默认会提供以下配置:
|
|
|
+
|
|
|
+```c
|
|
|
+save 900 1
|
|
|
+save 300 10
|
|
|
+save 60 10000
|
|
|
+```
|
|
|
+
|
|
|
+执行 bgsave 过程中,Redis 依然**可以继续处理操作命令**的。关键的技术就在于**写时复制技术**
|
|
|
+
|
|
|
+如果主线程(父进程)要修改共享数据里的某一块数据(比如键值对 A)时,就会发生写时复制,于是这块数据的物理内存就会被复制一份(键值对 A'),然后主线程在这个数据副本(键值对 A')进行修改操作。与此同时,bgsave 子进程可以继续把原来的数据(键值对 A)写入到 RDB 文件。
|
|
|
+
|
|
|
+### 为什么会有混合持久化?
|
|
|
+
|
|
|
+Redis 4.0 提出了**混合使用 AOF 日志和内存快照**
|
|
|
+
|
|
|
+AOF 文件的**前半部分是 RDB 格式的全量数据,后半部分是 AOF 格式的增量数据**。
|
|
|
+
|
|
|
+![img](assets/f67379b60d151262753fec3b817b8617-20230309232312657.png)
|
|
|
+
|
|
|
+## Redis安全控制
|
|
|
+
|
|
|
+### 缓存穿透--没有key
|
|
|
+
|
|
|
+产生的背景:
|
|
|
+
|
|
|
+缓存穿透是指使用不存在的key进行大量的高并发查询,导致缓存无法命中,每次请求都要都要穿透到后端数据库查询(同时也查不到),使得数据库的压力非常大,甚至导致数据库服务压死;**既不在缓存中,也不在数据库中**
|
|
|
+
|
|
|
+解决方案:
|
|
|
+
|
|
|
+1. 接口层实现api限流、用户授权、id检查等;
|
|
|
+2. 从缓存和数据库都取不到数据的话,一样将数据库空值放入缓存中,设置30s有效期避免使用同一个id对数据库攻击压力大;
|
|
|
+3. 布隆过滤器
|
|
|
+
|
|
|
+### 缓存击穿--单个key
|
|
|
+
|
|
|
+产生背景:
|
|
|
+
|
|
|
+在高并发的情况下,当一个缓存key过期时,因为访问该key请求较大,多个请求同时发现缓存过期,因此对多个请求同时数据库查询、同时向Redis写入缓存数据,这样会导致数据库的压力非常大;
|
|
|
+
|
|
|
+解决方案:
|
|
|
+
|
|
|
+1. 使用分布式锁
|
|
|
+
|
|
|
+保证在分布式情况下,使用分布式锁保证对于每个key同时只允许只有一个线程查询到后端服务,其他没有获取到锁的权限,只需要等待即可;这种高并发压力直接转移到分布式锁上,对分布式锁的压力非常大。
|
|
|
+
|
|
|
+2. 使用本地锁
|
|
|
+
|
|
|
+使用本地锁与分布式锁机制一样,只不过分布式锁适应于服务集群、本地锁仅限于单个服务使用。
|
|
|
+
|
|
|
+3. 软过过期
|
|
|
+
|
|
|
+设置热点数据永不过期或者异步延长过期时间;
|
|
|
+
|
|
|
+4. 布隆过滤器
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+### 缓存雪崩--多个key
|
|
|
+
|
|
|
+缓存雪崩指缓存服务器重启或者大量的缓存集中在某个时间段失效,突然给数据库产生了巨大的压力,甚至击垮数据库的情况。
|
|
|
+
|
|
|
+解决思路:对不用的数据使用不同的失效时间,加上随机数
|
|
|
+
|
|
|
+## 数据库和缓存如何保证一致性?
|
|
|
+
|
|
|
+### 先更新数据库,再更新缓存
|
|
|
+
|
|
|
+![图片](assets/8febac10b14bed16cb96d1d944cd08da.png)
|
|
|
+
|
|
|
+**出现了缓存和数据库中的数据不一致的现象**。
|
|
|
+
|
|
|
+### 先更新缓存,再更新数据库
|
|
|
+
|
|
|
+![图片](assets/454a8228a6549176ad7e0484fba3c92b.png)
|
|
|
+
|
|
|
+**出现了缓存和数据库中的数据不一致的现象**。
|
|
|
+
|
|
|
+### 先删除缓存,再更新数据库
|
|
|
+
|
|
|
+![图片](assets/cc208c2931b4e889d1a58cb655537767.png)
|
|
|
+
|
|
|
+**先删除缓存,再更新数据库,在「读 + 写」并发的时候,还是会出现缓存和数据库的数据不一致的问题**。
|
|
|
+
|
|
|
+### 先更新数据库,再删除缓存
|
|
|
+
|
|
|
+![图片](assets/1cc7401143e79383ead96582ac11b615.png)
|
|
|
+
|
|
|
+## 说说常见的缓存更新策略?
|
|
|
+
|
|
|
+### Cache Aside(旁路缓存)策略
|
|
|
+
|
|
|
+**写策略的步骤:**
|
|
|
+
|
|
|
+- 先更新数据库中的数据,再删除缓存中的数据。
|
|
|
+
|
|
|
+**读策略的步骤:**
|
|
|
+
|
|
|
+- 如果读取的数据命中了缓存,则直接返回数据;
|
|
|
+- 如果读取的数据没有命中缓存,则从数据库中读取数据,然后将数据写入到缓存,并且返回给用户。
|
|
|
+
|
|
|
+### Read/Write Through(读穿 / 写穿)策略
|
|
|
+
|
|
|
+***1、Read Through 策略***
|
|
|
+
|
|
|
+先查询缓存中数据是否存在,如果存在则直接返回,如果不存在,则由缓存组件负责从数据库查询数据,并将结果写入到缓存组件,最后缓存组件将数据返回给应用。
|
|
|
+
|
|
|
+***2、Write Through 策略***
|
|
|
+
|
|
|
+当有数据更新的时候,先查询要写入的数据在缓存中是否已经存在:
|
|
|
+
|
|
|
+- 如果缓存中数据已经存在,则更新缓存中的数据,并且由缓存组件同步更新到数据库中,然后缓存组件告知应用程序更新完成。
|
|
|
+- 如果缓存中数据不存在,直接更新数据库,然后返回;
|
|
|
+
|
|
|
+### Write Back(写回)策略;
|
|
|
+
|
|
|
+Write Back(写回)策略在更新数据的时候,只更新缓存,同时将缓存数据设置为脏的,然后立马返回,并不会更新数据库。对于数据库的更新,会通过批量异步更新的方式进行。
|