用过Redis的都知道,Redis有两种持久化方式:RDB和AOF,他们的区别大家应该都清楚,所以今天主要想分享一下这两种持久化方式的底层原理以及实现。
如果让你手写一个持久化(架构级)的功能,你没有思路的话,那希望这个文章可以给你灵感。
1. RDB持久化
1.1 创建
简单回顾下RDB文件的创建。
有两种创建方式:
-
save.阻塞进程去处理(期间不处理别的请求)
-
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条件是否满足,如何判断的呢?就是用lastsave
与saveparam.seconds
比较时间是否满足,dirty
与changes
比较修改次数是否满足。
那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 r e d i s 0000020 - v e r 005 4 . 0 . 9 372 r e d i 0000040 s - b i t s 300 @ 372 005 c t i m e 302 0000060 231 ; 017 ] 372 u s e d - m e m 302 310 0000100 p 372 f a o f - p r e a m b l 0000120 e 300 376 373 ( 006 k - 7 5 9 9 0000140 006 v - 7 5 9 9 022 c p t : 254 355 0000160 005 t a g e t O n e 4 303 L 220 ^ 303 0000200 037 254 355 005 s r % 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 003 I 006 s t a t u s L 0000300 004 d a 031 022 L j a v a / l 0000320 a n g / O b j e c t ; L 003 m s 0000340 g 340 005 032 f S t r i n g ; x p 0000360 310 y 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 006 001 002 003 004 0140600 005 006 016 004 l i s t 001 027 027 0140620 024 006 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 :redis使用内存的大小 374:RDB_OPCODE_EXPIRETIME_MS(带有过期时间标识) : 表示字符串 最后8字节为校验和
更详细的可以查看http://redisbook.com/preview/rdb/rdb_struct.html
手写过Jedis的朋友都熟悉RESP协议,RDB的数据段和它的排版方式很相似。比如: