文章目录
一、Redis持久化
Redis 是内存数据库,如果不将内存中的数据库状态保存到磁盘,那么一旦服务器进程退出,服务器中的数据库状态也会消失。所以 Redis 提供了持久化功能!
Redis为我们提供了两种持久化方式:
1.将当前数据状态进行保存,快照形式,存储数据结果,存储格式简单,关注点在数据(RDB)
2.将数据的操作过程进行保存,日志形式,存储操作过程,关注点在数据的操作过程(AOF)
1.RDB
RDB(Redis Database)是 Redis 数据库的一种持久化方式,用于将 Redis 数据保存到磁盘上,以便在 Redis 重启时恢复数据。RDB 是一种快速而高效的备份和恢复机制,它通过周期性地将内存中的数据快照保存到磁盘文件中,以实现持久化。
- RDB手动:
save指令:手动执行一次保存操作
bgsave指令:手动启动后台保存操作,但不是立即执行
- RDB自动
在redis.windows.conf文件中进行配置开启
配置 :save second changes
second:监控时间范围
changes:监控key的变化量
作用 : 满足限定时间范围内key的变化数量达到指定数量即进行持久化
2.AOF
AOF(append only file)持久化:以独立日志的方式记录每次写命令,重启时再重新执行AOF文件中命令达到恢复数据的目的;与RDB相比可以简单描述为改记录数据为记录数据产生的过程AOF的主要作用是解决了数据持久化的实时性,目前已经是Redis持久化的主流方式。
AOF写数据三种策略(appendfsync):
- always(每次)
每次写入操作均同步到AOF文件中,数据零误差,性能较低; - everysec(每秒)
每秒将缓冲区中的指令同步到AOF文件中,数据准确性较高,性能较高在系统突然宕机的情况下丢失1秒内的数据; - no(系统控制)
由操作系统控制每次同步到AOF文件的周期,整体过程不可控;
RDB与AOF区别:
持久化方式 | RDB | AOF |
---|---|---|
`占用存储空间 | 小(数据级:压缩) | 大(指令级:重写) |
存储速度 | 慢 | 快 |
恢复速度 | 快 | 慢 |
数据安全性 | 会丢失数据 | 依据策略决定 |
资源消耗 | 高/重量级 | 低/轻量级 |
启动优先级 | 低 | 高 |
二、Redis 删除策略
1.过期数据
Redis是一种内存级数据库,所有数据均存放在内存中,Redis的数据是可以指定生存时间的,内存中的数据可以通过TTL指令获取其状态。
Redis数据的三种状态:
- XX :具有时效性的数据
- -1 :永久有效的数据
- -2 :已经过期的数据或被删除的数据或未定义的数据
在Redis中,过期的数据不一定是真的删除了,对于Redis过期数据的删除策略如下:
2.数据删除策略
-
定时删除:创建一个定时器,当key设置有过期时间,且过期时间到达时,由定时器任务立即执行对键的删除 操作。
优点:节约内存,到时就删除,快速释放掉不必要的内存占用;
缺点:CPU压力很大,无论CPU此时负载量多高,均占用CPU,会影响redis服务器响应时间和指令吞吐量;
总结:用处理器性能换取存储空间(拿时间换空间) -
惰性删除:数据到达过期时间,不做处理。等下次访问该数据时,如果未过期,返回数据;发现已过期,删除,返回不存在。
优点:节约CPU性能,发现必须删除的时候才删除;
缺点:内存压力很大,出现长期占用内存的数据;
总结:用存储空间换取处理器性能(拿空间换时间) -
定期删除:
定时删除和惰性删除两种方案要么对内存损耗大,要么对性能影响大,两种方案都走极端,而定期删除则是一种比较折中的方案。
定期删除:周期性轮询redis库中的时效性数据,采用随机抽取的策略,利用过期数据占比的方式控制删除频度。
优点:CPU性能占用设置有峰值,检测频度可自定义设置,内存压力不是很大,长期占用内存的冷数据会被持续清理
总结:周期性抽查存储空间 (随机抽查,重点抽查) -
redis定期删除执行流程:
1.Redis启动服务器初始化时,读取配置server.hz的值,默认为10;
2.每秒钟执行server.hz次**serverCron()**中的方法—databasesCron()—activeExpireCycle()
3.activeExpireCycle()对每个expires[]逐一进行检测,每次执行250ms/server.hz
4.对某个expires[]检测时,随机挑选W个key检测 :
如果key超时,删除key 如果一轮中删除的key的数量>W * 25%,循环该过程;
如果一轮中删除的key的数量≤W * 25%,检查下一个expires[],0-15次循环,遍历整个缓存;
W取值=ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP属性值
5.参数current_db用于记录activeExpireCycle() 进入哪个expires[] 执行;
6.如果activeExpireCycle()执行时间到期,下次从current_db继续向下执行;
3.逐出算法
当新数据进入redis时,如果内存不足时就会用到逐出算法;
Redis使用内存存储数据,在执行每一个命令前,会调用freeMemoryIfNeeded()检测内存是否充足。如果内存不满足新加入数据的最低存储要求,redis要临时删除一些数据为当前指令清理存储空间。清理数据的策略称为逐出算法。
注意:逐出数据的过程不是100%能够清理出足够的可使用的内存空间,如果不成功则反复执行。
当对所有数据尝试完毕后,如果不能达到内存清理的要求,将出现错误信息。
抛出异常:(error) OOM command not allowed when used memory >‘maxmemory’;
逐出算法的相关配置:
maxmemory最大可使用内存:
占用物理内存的比例,默认值为0,表示不限制,生产环境中根据需求设定,通常设置在50%以上。
maxmemory-samples每次选取待删除数据的个数:
选取数据时并不会全库扫描,导致严重的性能消耗,降低读写性能。因此采用随机获取数据的方式
作为待检测删除数据。
maxmemory-policy删除策略:
检测易失数据(可能会过期的数据集server.db[i].expires )
① volatile-lru:挑选最近最少使用的数据淘汰
② volatile-lfu:挑选最近使用次数最少的数据淘汰
③ volatile-ttl:挑选将要过期的数据淘汰
④ volatile-random:任意选择数据淘汰
检测全库数据(所有数据集server.db[i].dict )
⑤ allkeys-lru:挑选最近最少使用的数据淘汰
⑥ allkeys-lfu:挑选最近使用次数最少的数据淘汰
⑦ allkeys-random:任意选择数据淘汰
放弃数据驱逐
⑧ no-enviction(驱逐):禁止驱逐数据(redis4.0中默认策略),会引发错误OOM(Out Of
Memory)达到最大内存后的,对被挑选出来的数据进行删除的策略
三、Redis企业级解决方案
1.缓存预热
服务器启动后迅速宕机,服务器在刚启动后缓存中没有任何数据,因为大量并发请求的压力下可能会导致服务器宕机,所以在服务器上线之前,应做好缓存预热。
缓存预热就是系统启动前,提前将相关的缓存数据直接加载到缓存系统。避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据。
解决方案:
1.日常例行统计数据访问记录,统计访问频度较高的热点数据;
2.将统计结果中的数据分类,根据级别,redis优先加载级别较高的热点数据;
2.缓存雪崩
缓存雪崩是指在同一时段大量的缓存key同时失效或者Redis服务宕机,导致大量请求访问Redis缓存未命中,从而到达数据库,带来巨大压力。
解决方案:
1.给不同的Key的TTL添加随机值
2.利用Redis集群提高服务的可用性
3.给缓存业务添加降级限流策略
4.给业务添加多级缓存
3.缓存击穿
缓存击穿问题也叫热点Key问题,就是一个被高并发访问并且缓存重建业务较复杂的key突然失效了,无数的请求访问会在瞬间给数据库带来巨大的冲击。
解决方案:
1.互斥锁
假设现在线程1过来访问,他查询缓存没有命中,但是此时他获得到了锁的资源,那么线程1就会一个人去执行逻辑,假设现在线程2过来,线程2在执行过程中,并没有获得到锁,那么线程2就可以进行到休眠,直到线程1把锁释放后,线程2获得到锁,然后再来执行逻辑,此时就能够从缓存中拿到数据了。
互斥锁方案:由于保证了互斥性,所以数据一致,且实现简单,因为仅仅只需要加一把锁而已,也没其他的事情需要操心,所以没有额外的内存消耗,缺点在于有锁就有死锁问题的发生,且只能串行执行性能肯定受到影响
2.逻辑过期
我们之所以会出现这个缓存击穿问题,主要原因是在于我们对key设置了过期时间,逻辑过期的思想就在于不设置过期时间,那么key在某种意义上就不会过期。我们把过期时间设置在 redis的value中。注意:这个过期时间并不会直接作用于redis,而是我们后续通过逻辑去处理。
逻辑过期方案: 线程读取过程中不需要等待,性能好,有一个额外的线程持有锁去进行重构数据,但是在重构数据完成前,其他的线程只能返回之前的数据,(可能会引发脏读)且实现起来麻烦;
4.缓存穿透
缓存穿透 :缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在,这样缓存永远不会生效,这些请求都会打到数据库。
解决方案:
1.缓存空对象:
优点:实现简单,维护方便;
缺点:额外的内存消耗、可能造成短期的不一致;
2.布隆过滤:
优点:内存占用较少,没有多余key;
缺点:实现复杂、存在误判可能;
布隆过滤实现机制:
布隆过滤会通过一个叫做布隆过滤器的空间,这个空间中存放redis缓存中所有key的哈希值,当用户发起请求时,会先通过布隆过滤器,布隆过滤器会先对请求的key进行判断,如果存在,则放行,去redis中获取缓存数据,不存在,直接拒绝请求,这样做的好处在于不会让请求涌入到数据库,不会引发数据库的宕机,但是哈希冲突时不可避免的。
四、Redis线程模型
Redis 单线程指的是「接收客户端请求->解析请求->进行数据读写等操作->发送数给客户端]这个过程是由一个线程(主线程)来完成的,这也就是说Redis是单线程的原因。
但是,Redis程序并不是单线程的,Redis在启动的时候,是会启动后台线程(BIO)的:
Redis在2.6版本,会启动2个后台线程,分别处理关闭文件、AOF刷盘这两个任务; Redis
在4.0版本之后,新增了一个新的后台线程,用来异步释放Redis内存,也就是 lazyfree线程。
之所以Redis为「关闭文件、AOF刷盘、释放内存」这些任务创建单独的线程来处理,是因为这些任务的操作都是很耗时的,如果把这些任务都放在主线程来处理,那么Redis 主线程就很容易发生阻塞,这样就无法处理后续的请求了。
后台线程相当于一个消费者,生产者把耗时任务丢到任务队列中,消费者(BIO)不停轮询这个队列,拿出任务就去执行对应的方法即可。
并且,关闭文件、AOF刷盘、释放内存这三个任务都有各自的任务队列:
BIO CLOSF FILE关闭文件任务队列:当队列有任务后,后台线程会调用close(fd),将文件关闭;
BlO_AOF_FSYNC,AOF刷盘任务队列:当AOF日志配置成everysec选项后,主线程会把AOF 写日志操作封装成一个任务,也放到队列中。当发现队列有任务后,后台线程会调用fsync(fd),将AOF文件刷盘;
BlO_LAZY_FREE,lazy free任务队列:当队列有任务后,后台线程会free(obj)释放对象/free(dict)删除数据库所有对象/free(skiplist)释放跳表对象。
五、Redis为什么这么快?
- Redis的大部分操作都在内存中完成,并且采用了高效的数据结构,因此 Redis瓶颈可能是机器的内存或者网络带宽,而并非CPU,既然 CPU不是瓶颈,那么自然就采用单线程的解决方案了;
- Redis采用单线程模型可以避免了多线程之间的竞争,省去了多线程切换带来的时间和性能上的开销,而且也不会导致死锁问题。
- Redis 采用了V/O多路复用机制处理大量的客户端Socket 请求,IO多路复用机制是指一个线程处理多个IO流,就是我们经常听到的select/epoll机制。简单来说,在Redis 只运行单线程的情况下,该机制允许内核中,同时存在多个监听Socket和已连接Socket。内核会一直监听这些Socket上的连接请求或数据请求。一旦有请求到达,就会交给Redis线程处理,这就实现了一个Redis线程处理多个IO流的效果。
总结
- Redis为我们提供了两种持久化方式:RDB和AOF;
- Redis删除策略各有优缺点,可以根据需求自己选择;
- Redis缓存数据很重要,一旦缓存数据塌陷,将会对数据库造成毁灭性的打击;