目录
AOF(append only file )模式 -- 只追加文件持久化
原由
Redis中的数据都在内存中,如果断电宕机则内存数据丢失,其中数据应该持久化保存,不允许丢失。
持久化策略
按照配置的时间,定期将内存数据保存到redis中的持久化文件中,当redis服务器宕机之后重启时,首先读取指定的持久化文件,恢复内存数据。
RDB(redis database)模式-- 快照持久化
概念
RDB模式是Redis中默认的持久化策略,将某一时间点的数据打包生成一个.rdb的文件,保存在磁盘中,当我们重启redis服务的时候,将会从该rdb文件恢复数据。保存的是redis的内存快照,占用的资源少,持久化效率最高。
RDB特点
- RDB模式能够定期持久化,但是有丢失数据的风险。如:非正常gu,到上个保存点之间的数据将会丢失
- Redis中默认的持久化策略,保存数据比AOF快
- RDB模式做内存的快照,效率高
- 存储的文件是紧凑的,占用磁盘空间较小
- 启动子线程备份,父进程无需执行IO操作,从而最大化Redis的性能。当数据量大和cpu性能不够时,服务器可能是卡顿
- 适合容灾恢复,持久化文件可在其他服务器恢复
-
使用save持久化时,如果执行持久化操作,则程序陷入阻塞,用户不能执行set
RDB配置
- 持久化文件名称
dbfilename dump.rdb
- 持久化文件目录
- 持久化策略
save 900 1 | 用户在900秒内做1次set操作时持久化1次 |
save 300 10 | 用户在300秒内做10次set操作时持久化1次 |
save 60 10000 | 用户在60秒内做10000次set操作时持久化1次 |
AOF(append only file )模式 -- 只追加文件持久化
概念
AOF模式是将被执行的写命令添加到aof文件的末尾,该文件被保留在磁盘中,当重启redis服务的时候会优先(相对于rdb文件而言)读取aof文件,把所有的写操作重新执行 一遍,完成对redis数据的恢复。
AOF特点
- AOF模式可以实现数据的实时持久化
- 记录的是用户的操作过程,是一个日志追加文件,不需要定位,就算断电也没有损坏问题
- 持久化文件会比较大,对于同样的数据集,AOF文件通常要大于RDB文件
- 持久化效率较RDB低
- AOF模式默认是关闭的
- AOF模式持久化是异步的
AOF配置
- 开启AOF模式和修改持久化文件名称
- 持久化文件目录
- 持久化策略
appendfsync always | 只要用户执行”更新”命令则持久化一次 |
appendfsync everysec | 每秒持久化一次 |
appendfsync no | 由默认的操作系统决定什么时候持久化 |
缓存引发三大问题
缓存穿透
概念
高并发查询一个不存在的key或者key已经过期,缓存不起作用,所有的请求会穿透到后台数据库,有宕机的风险
解决
- 采用Bloom Filter(布隆过滤器):使用一个足够大的Bitmaps ,用于存储可能访问的key,不存在的key直接被过滤
- 缓存空对象:对于访问的key未在DB存在值,将空值作为value写进缓存,并设置较短过期时间
缓存雪崩
概念
高并发查询时,如果缓存服务器宕机或缓存失效(大量的key设置了相同的过期时间,导致同一时刻全部失效),则查询数据库,压力骤增,可能导致数据库宕机
解决
- 高可用性:搭建集群,保证缓存层服务高可用性
- 设置不同过期时间:在设置过期时间时加上一个随机值时间,使得每个key的过期时间分散,防止同一时刻失效
缓存击穿
概念
高并发查询时,当某一个存在的热点key,超时或者失效时,DB有宕机的风险
解决
- 不设置过期时间:对于某些热点key,不进行过期时间的设置
- 互斥锁 (mutex key):只允许一个线程重建缓存,其他线程等待重建缓存的线程执行完,再从缓存获取数据
String getValue(String key) {
//redis中获取key
String value = redis.get(key);
//value为空则开始重构缓存
if (value == null) {
//只允许一个线程重构缓存,使用nx,并设置过期时间ex
String mutexKey = "mutex:key" + key;
if (redis.set(mutexKey, "1", "ex 180", "nx")) {//tag=====flag
//说明此时没有其他线程重建缓存,那么当前线程执行缓存构建逻辑
//从数据库获取数据
value = dbDao.getValueByKey(key);
//回写redis并设置过期时间
redis.set(key, value, timeout);
//删除mutexKey
redis.del(mutexKey);
} else {
//说明此时已经有其他线程正在执行构建缓存的工作,
// 那么当前线程将休息指定时间 (例如50 毫秒) 后,
// 重新执行函数,直到获取到数据。
Thread.sleep(50);
get(key);
}
}
return value;
}
该tag可由以下实现
/**
* @param key
* @param value
* @param nxxx NX|XX, NX——仅当键不存在时才设置它。XX——只设置键
* 如果它已经存在。
* @param expx EX|PX,过期时间单位:EX =秒;PX =毫秒
* @param time过期时间,单位为{@param #expx}
*/
jedis.set("key","key","NX","EX",10);
Redis内存机制
内存算法
LRU算法
Least Recently Used,即最近最少使用 ,淘汰规则是基于访问时间。内存管理的一种页面置换算法,对于在内存中但又不用的数据块(内存块)叫做LRU,操作系统会根据哪些数据属于LRU而将其移出内存而腾出空间来加载另外的数据。
LFU算法
Least Frequently Used,即最不经常使用,淘汰规则是基于访问次数 。要求在页置换时置换引用计数最小的页,因为经常使用的页应该有一个较大的引用次数。但是有些页在开始时使用次数很多,但以后就不再使用,这类页将会长时间留在内存中,因此可以将引用计数寄存器定时右移一位,形成指数衰减的平均使用次数。
内存优化策略
volatile-lru | 采用LRU算法删除数据(只针对设定超时时间的数据) |
allkeys-lru | 所有的数据采用LRU算法删除数据 |
volatile-lfu | 采用LFU算法删除数据(只针对设定超时时间的数据) |
allkeys-lfu | 所有的数据采用LFU算法删除数据 |
volatile-random | 随机删除(只针对设定超时时间的数据) |
allkeys-random | 所有数据随机删除 |
volatile-ttl | 设定了超时时间的数据排序,将马上要超时的数据提前删除 |
noeviction | 不删除数据,如果内存数据存满则报错返回,该策略是默认策略 |
- 最大内存 maxmemory
- 内存策略 maxmemory-policy
- 随机抽取key的数量 maxmemory-samples
redis中并不会准确的删除所有键中最近最少使用的键,而是随机抽取n个键,删除这n个键中最近最少使用的键。maxmemory-samples的默认配置为5,如果增加,会提高LRU或TTL的精准度。但redis作者测试的结果是当这个配置为10时已经非常接近全量LRU的精准度了,如果增加maxmemory-samples会导致在主动清理时消耗更多的CPU时间。