用过Redis的都知道,Redis有两种持久化方式:RDB和AOF,他们的区别大家应该都清楚,所以今天主要想分享一下这两种持久化方式的底层原理以及实现。

如果让你手写一个持久化(架构级)的功能,你没有思路的话,那希望这个文章可以给你灵感。

1. RDB持久化

1.1 创建

简单回顾下RDB文件的创建。

有两种创建方式:

  1. save.阻塞进程去处理(期间不处理别的请求)

  2. bgsave.派生一个子进程去处理

1.2 载入

在redis服务启动时,如果检测到RDB文件,会进行自动载入。

如果RDB文件和AOF都存在,优先载入谁?

如果开启了AOF,则会优先AOF

1.3 save的底层实现

save 900 1
save 300 10
save 60 10000

这是redis.conf配置文件中关于RDB save时机的配置,它映射在redisServer结构体的saveparams字段中:

struct redisServer{
....
// 保存了redis.conf配置的属性
struct saveparam *saveparams; // 记录上一次save的时间
time_t lastsave; // 修改计数器
long long dirty;
...
};

那来看看它怎么保存的:

struct saveparam {
// 秒数
time_t seconds;
// 修改次数
int changes;
};

redis自己有一个定时任务每100毫秒执行一次,其中有一个任务就是检查save条件是否满足,如何判断的呢?就是用lastsavesaveparam.seconds比较时间是否满足,dirtychanges比较修改次数是否满足。

那bgsave如何实现呢,new一个子线程,然后拷贝个数据副本,然后和save一样处理。

好了,到这里,用Java写一个这应该是没问题了,那RDB的文件结构如何设计呢?我们来看看redis的设计。

1.4 RDB文件结构

REDIS+数据库版本号+数据类型+数据+EOF(表示数据结束)(377)+检验和

我们知道java中Class文件结构很复杂,因为它包含了常量、接口、类、父类、字段等面向对象的信息,而RDB的就比较简单了,因为它只需要存放数据即可。

和class结构一样,它的开头也是文件标识REDIS+版本号标识.

[root@izuf6i2jk9azj2te13kjx8z redis-4.0.9]# od -c dump.rdb
0000000 R E D I S 0 0 0 8 372 t r e d i s
0000020 - v e r 005 4 . 0 . 9 372 n r e d i
0000040 s - b i t s 300 @ 372 005 c t i m e 302
0000060 231 ; 017 ] 372 b u s e d - m e m 302 310
0000100 p r 0 372 f a o f - p r e a m b l
0000120 e 300 0 376 0 373 ( 0 0 006 k - 7 5 9 9
0000140 006 v - 7 5 9 9 0 022 c p t : 254 355 0
0000160 005 t 0 a g e t O n e 4 303 L 220 ^ 303
0000200 037 254 355 0 005 s r 0 % c o m . f a n
0000220 t . c o r e . r e s p o n s e .
0000240 S 005 e r v e r R 240 016 030 222 224 e 250 :
0000260 035 323 ? 002 0 003 I 0 006 s t a t u s L
0000300 0 004 d a t 031 0 022 L j a v a / l
0000320 a n g / O b j e c t ; L 0 003 m s
0000340 g 340 005 032 f S t r i n g ; x p 0 0
0000360 0 310 y 0 036 340 005 y 037 p o j o . C
0000400 o m p e t i t i o n Z 276 231 334 b 025 ...
0140540 a 004 j a v a 377 v 006 n u m b e r 024
0140560 002 0 0 0 006 0 0 0 001 0 002 0 003 0 004 0
0140600 005 0 006 0 016 004 l i s t 001 027 027 0 0 0
0140620 024 0 0 0 006 0 0 362 002 363 002 364 002 365 002 366
0140640 002 367 377 377 - 022 036 ] 367 332 257 _

分析:

R   E   D   I   S:RDB文件标志
0 0 0 8:版本号
372:结束符
r e d i s
0000020 - v e r 005 4 . 0 . 9:redis-version4.0.9
r e d i
0000040 s - b i t s 300 @:redis的位数64或32
c t i m e 302 0000060 231 ; 017 ]:时间戳
u s e d - m e m 302 310 0000100 p r 0:redis使用内存的大小
374:RDB_OPCODE_EXPIRETIME_MS(带有过期时间标识)
0: 表示字符串
最后8字节为校验和

更详细的可以查看http://redisbook.com/preview/rdb/rdb_struct.html

手写过Jedis的朋友都熟悉RESP协议,RDB的数据段和它的排版方式很相似。比如: 003 m s g 005 h e l l o 377就表示键值对:msg(3个长度):hello(5个长度)

AOF

AOF以拼接和重写命令的方式来实现。

# 是否开启aof
appendonly yes # 文件名称
appendfilename "appendonly.aof" # 同步方式
##每次收到写命令就立即强制写入磁盘,最慢的,但是保证完全的持久化,不推荐使用
# appendfsync always
##每秒钟强制写入磁盘一次,在性能和持久化方面做了很好的折中,系统默认
appendfsync everysec
##完全依赖os,性能最好,持久化没保证
# appendfsync no # aof重写期间是否同步
no-appendfsync-on-rewrite no # 重写触发配置
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb # 加载aof时如果有错如何处理
aof-load-truncated yes # 文件重写策略
aof-rewrite-incremental-fsync yes

这一段配置中,大家着重理解同步方式的配置。redis默认采用的每秒一次写入AOF文件的策略。

实现原理

struct redisServer {

    // ...
// 存放AOF缓冲
sds aof_buf; // ...
};

当有新的命令进来,redis就会将其(协议化后)追加到aof_buf的末尾。

同理,redis的事件循环也会监听AOF的配置,如果满足配置文件中的同步方式appendfsync everysec等,就会将aof_buf中的内容保存到AOF文件里。

为什么要进行AOF重写

我们知道,redis对AOF有重写机制,用来控制AOF文件的大小。

  1. AOF体积过大不利于存储。

  2. AOF体积过大,使用AOF数据还原的时间更长。

AOF重写多个键值对的数据一定是使用一条数据完成吗

发生在重写列表、哈希表、集合、有序集合可能会带有多个元素的键时。

不是,如果它的值超过64项,则会用多条命令来完成。(避免客户端输入缓冲区溢出)

AOF谁来执行

Redis不希望AOF重写造成服务器阻塞,所以用子进程(带有数据副本)去处理。

AOF期间有新的数据进来会导致AOF文件与当前数据不一致吗

不会。为了解决这个问题,Reids设置了AOF重写缓冲区(创建子进程后开启),当Redis执行命令时,redis会同时将这个信息发送给aof_buf和AOF重写缓冲区。

扩展

过期键的删除策略

  1. 定时删除。过期键较多的情况下,大量的CPU用于删除键而影响了客户端的请求。

  2. 惰性删除。只有过期键被访问才删除,可能会导致过期键过多,造成内存浪费和溢出。

  3. 定期删除。限制时长和频率对过期键进行删除,难点在于时长和频率难以确定。

Redis的过期键删除策略

Redis采用的是惰性删除和定期删除,配合这两种策略来取得CPU和内存的平衡。

RDB和AOF文件中会包含过期键吗

不包含。

在生成RDB和AOF文件时,程序会对键进行检查,已过期的键不保存到文件中。

Redis持久化深入理解的更多相关文章

  1. 【大厂面试06期】谈一谈你对Redis持久化的理解?

    Redis持久化是面试中经常会问到的问题,这里主要通过对以下几个问题进行分析,帮助大家了解Redis持久化的实现原理. 1.Redis持久化是什么? 2.Redis持久化有哪些策略?各自的实现原理是怎 ...

  2. 【redis持久化】redis持久化理解

    1.以下内容仅为个人理解和总结,仅供参考,万万不可全盘真信,内容会进行实时改进和修正 2.redis持久化: 参考链接1.https://redis.io/topics/persistence  -- ...

  3. 一篇文章彻底理解Redis持久化:RDB和AOF

    为什么需要持久化? Redis对数据的操作都是基于内存的,当遇到了进程退出.服务器宕机等意外情况,如果没有持久化机制,那么Redis中的数据将会丢失无法恢复.有了持久化机制,Redis在下次重启时可以 ...

  4. 5分钟彻底理解Redis持久化

    Redis持久化 RDB快照 在默认情况下,Redis将内存数据库快照保存到dump.rdb的二进制文件中. 可以对Redis进行设置,让它在"N秒内数据集至少有N个改动", 这一 ...

  5. 10分钟彻底理解Redis持久化和主从复制

    在这篇文章,我们一起了解 Redis 使用中非常重要的两个机制:Reids 持久化和主从复制. 什么是 Redis 持久化? Redis 作为一个键值对内存数据库(NoSQL),数据都存储在内存当中, ...

  6. redis——持久化篇

    众所周知,redis是内存数据库,它把数据存储在内存中,这样在加快读取速度的同时也对数据安全性产生了新的问题,即当redis所在服务器发生宕机后,redis数据库里的所有数据将会全部丢失. 为了解决这 ...

  7. redis持久化机制

    redis持久化 redis的数据存在内存中,所以存取性能好.但是存在内存中的数据存在一个问题,一旦机器重启,内存数据消失.为了解决这个问题,redis支持持久化.持久化就是为了解决内存数据丢失时恢复 ...

  8. Redis持久化实践及灾难恢复模拟

    参考资料: Redis Persistence http://redis.io/topics/persistence Google Groups https://groups.google.com/f ...

  9. Redis持久化-数据丢失及解决(转载)

    本文转载自        Redis持久化-数据丢失及解决  感谢原作者 Redis的数据回写机制 Redis的数据回写机制分同步和异步两种, 同步回写即SAVE命令,主进程直接向磁盘回写数据.在数据 ...

随机推荐

  1. Html 空格与换行

    空格   换行 <br/>   调行距 <div style="line-height:10px"></div>

  2. 运行control userpasswords2实现winXP自动登录

    原文:运行control userpasswords2实现winXP自动登录 如果你的计算机只是自己一人在用,且每次都用同一个用户名(或者你根本没在意过什么是用户名),而每次都要输入密码,是否太麻烦了 ...

  3. <转>C# 动态创建DataTable

    C# 动态创建DataTable,有时候在做些测试Demo中用来模拟一些数据比较不错.记在这里避免以后重写呵呵... DataTable dt = new DataTable(); dt.Column ...

  4. Linux ssh密钥自动登录 专题

    在开发中,经常需要从一台主机ssh登陆到另一台主机去,每次都需要输一次login/Password,很繁琐.使用密钥登陆就可以不用输入用户名和密码了 实现从主机A免密码登陆到主机B(即把主机A的pub ...

  5. 一键彻底关闭Win10自带Windows Defender杀毒软件

    1.以管理员身份打开系统的命令提示符[cmd.exe]. 2.输入以下命令: reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\ ...

  6. linux输出信息调试信息重定向

    最近在做一个android系统移植的项目,所使用的开发板com1是调试串口,就是说会有uboot和kernel的调试信息打印在com1上(ttySAC0).因为后期要使用ttySAC0作为上层应用通信 ...

  7. 基于 libevent 开发的 C++ 11 高性能网络服务器 evpp(360的作品)

    evpp是一个基于libevent开发的现代化C++11高性能网络服务器,自带TCP/UDP/HTTP等协议的异步非阻塞式的服务器和客户端库. 特性: 现代版的C++11接口 非阻塞异步接口都是C++ ...

  8. Setting up multi nodes live migration in Openstack Juno with devstack

    Setting up multi nodes live migration in Openstack Juno with devstack Summary Live migration overvie ...

  9. SimpleDateFormat之后为何多了一年,难道Java API也这么不靠谱?

    这一切的背后到底是机器故障,还是程序的bug? 难道Java API也不靠谱 朋友在我博客上发现一时间明显错误,操作时间怎么会是2016年?在同一个for循环输出到页面的时间,唯独这一个时间不对,整整 ...

  10. LockWindowUpdate

     //锁住listview防止反复刷新              LockWindowUpdate(Self.lvsearch.Handle);    貌似不太行,多用几下就卡住了  那个函数几乎不用 ...