开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 5 天,点击查看活动详情
持久化与线程模型
Redis是一个基于内存的缓存中间件,但是如果机器宕机就会造成数据丢失,因此我们需要把Redis的数据持久化在磁盘上,本节将分析Redis的持久化方案,包括RDB和AOF,同时还会对Redis的线程模型进行分析。
线程模型
Reids很多人都认为它是单线程的,但是这不完全对。准确来讲,Redis对于数据的处理,比如对数据的增删改查是单线程的,单线程处理数据可以避免并发问题,由于是在内存中处理,因此速度会很快,不会因为单线程而导致效率低下。
但是这并不是Redis唯一的线程,像关闭文件,AOF刷盘和释放内存这些非常耗时的任务都会创建独立的后台线程。这些后台线程就像是消费者一样,生产者将任务丢进消费队列中,消费者BIO轮询队列,拿出任务后进行消费。
Redis的瓶颈并不在于数据处理上,而是在网络IO上,因此在Redis6.0后多个IO线程去处理网络请求,但是在数据的处理上仍然是单线程的。
RDB
在默认情况下,Redis会将缓存快照存储到“dump.rdb”中,RDB中记录的是二进制数据。所谓的快照指的就是在某一瞬间的全量数据。
Redis会提供两种命令去保存RDB,分别是save和bgsave,执行save命令时Redis服务器会被阻塞,在完成save命令前所有收到的指令都会被拒绝。
而bgsave则会通过写时复制技术(COW),创建子进程区完成保存RDB文件任务,可以避免堵塞主线程。在使用bgsave后,会通过fork()创建子进程,子进程会复制父进程的页表,也就是说父进程和子进程指向的都是同一个物理内存。
如果这个时候父进程发生修改内存数据的情况时,会在物理内存中复制一份,然后父进程页表指向新复制的数据,也就是说子进程记录的只是执行bgsave那瞬间的数据。子进程在完成任务后会替换原有的RDB文件。
bgsave和save各有优缺点。bgsave不会阻塞主线程,但是由于会有数据副本,会消耗更多的内存空间。而save会阻塞主线程,但是并不会消耗内存空间。
AOF
AOF和RDB不同,AOF保存的是修改数据的指令。在执行完一条指令后,会将该指令存入aof_buf中,具体什么时候写入到磁盘中,会有不同的策略。
一共有三种策略,分别是Always,Everysec和No。
Always:就是总是,所以它的意思是每次执行完指令后都会将AOF日志写入磁盘中。虽然可以保证数据不丢失,但是会极大地影响
Everysec:每秒写入磁盘一次,如果发生宕机也只会丢失一秒的数据,这种策略可以兼顾速度和安全性,也是Redis默认的策略。
No:意味着Redis不控制写入磁盘,而是将写入时机交给操作系统。虽然性能优秀,但是由于写入磁盘的时机不可控,无法确定丢失多少数据,因此一般不会选择。
如果说任由AOF增加下去的话,AOF文件会变得很大
incr count
-1
incr count
-2
incr count
-3
incr count
-4
如果说是AOF文件,那么它会记录这四条指令,但是RDB只会记录最后一个值,因此AOF需要进行重写,可以通过这两个配置控制重写
//aof文件到达64mb时进行重写
auto-aof-rewrite-min-size 64mb
//当aof文件自上一次重写增长了100%则再次触发
auto-aof-rewrite-percentage 100
也可以通过指令bgrewriteof手动进行重写。整个重写的过程和RDB一样创建子进程,同样也是使用COW的技术,因此和保存RDB文件过程是相似的,并不会阻塞主线程。
混合持久化
RDB和AOF两种持久化各有特点,RDB体积更小,但是有可能会损失更多的数据,而AOF则相反。因此在Redis4.0加入了混合持久化选项。
//开启混合持久化
aof-use-rdb-preamble yes
如果开启了混合持久化,那么在AOF重写时不再是单纯地记录指令,而是在重写的那一刻还会记录RDB的快照内容,同时把在保存文件期间的指令写入AOF缓存区中,最后将这RDB快照和重写区中的指令都写入到一个AOF文件中,并替换旧文件。
总结
本节介绍了Redis的线程模型,Redis并不是完全的单线程,只是在处理数据时的主线程是单线程的。同时也介绍了RDB以及AOF的持久化策略,希望看完后有所收获。
感谢观看!