数据库
首页 > 数据库> > Redis RDB持久化原理解析

Redis RDB持久化原理解析

作者:互联网

Redis版本:Redis 4.0.1 Redis是一个键值对数据库服务器,存储在内存中,如果重启没进行持久化,数据会丢失。所以需要持久化策略RDB和AOF。 涉及Redis源码解析: https://gitee.com/lidishan/redis-source-code-analysis/blob/master/src/rdb.c   前提知识归纳 RDB用于保存和恢复数据库所有键值对 使用save或bgsave报错,其中bgsave是fork子进程不会造成阻塞 rdb文件是一个二进制文件 不同类型键,会有不同数据结构存储   RDB会采用save、bgsave进行rdb保存 save:生成RDB,会导致Redis阻塞 bgsave:生成RDB,fork子进程处理 备注:RDB是在服务器启动的时候自动载入的(如果有rdb文件)。但如果开启了AOF(更新·频率更高),服务器就会 优先使用AOF文件加载数据。   其中是否执行bgsave这些是根据 RedisServer#saveparams 来判断,其数据结构如下: struct redisServer { ....     // 保存触发 rdb 文件存储的参数数组     struct saveparam * saveparams ; /* Save points array for RDB */ } // 在seconds秒内数据库修改changes次就触发RDB文件生成 struct saveparam {     time_t seconds ;// 秒数     int changes ;// 数据库修改次数 }; 符合上面的参数生成RDB,举个例子:   bgsave是fork子进程进行处理,其触发方式大概有如下两种: 手动触发:执行bgsave命令手动触发   自动触发:根据上面的saveparams命令定时触发 或者 主从节点同步 sync指令或psync(这个做主从分析的时候再解析)   下面举个定时触发的例子源码(serverCron定时触发,然后扫描saveparams参数) /** * 作为时间事件运行 */ int serverCron ( struct aeEventLoop *eventLoop , long long id , void *clientData) {     ....     // 单位时间内修改次数达到上限     if (server. dirty >= sp-> changes &&         server.unixtime-server.lastsave > sp->seconds &&  (server.unixtime-server.lastbgsave_try >  CONFIG_BGSAVE_RETRY_DELAY ||  server. lastbgsave_status == C_OK))  {         // 打印保存进行中的日志         serverLog(LL_NOTICE , "%d changes in %d seconds. Saving..." ,         sp-> changes , ( int )sp->seconds) ;         rdbSaveInfo rsi , *rsiptr ;         rsiptr = rdbPopulateSaveInfo(&rsi) ;         // 进行 bgsave         rdbSaveBackground(server. rdb_filename , rsiptr) ;         break;     }     .... }   /** * rdbbgsave ,会 fork 一个子进程处理 **/ int rdbSaveBackground ( char *filename , rdbSaveInfo *rsi) {     pid_t childpid ; // 子进程     // 判断是否有活跃的子进程在拷贝,如果有则终止 server.child_pid != -1     if (hasActiveChildProcess()) return C_ERR ;      // 记录当前 bgsave 前的数据库被修改次数 和 最后修改的时间     server. dirty_before_bgsave = server. dirty ;     server.lastbgsave_try = time(NULL) ;     // fork 子进程     if ((childpid = redisFork(CHILD_TYPE_RDB)) == 0 ) {         int retval ;           /* Child */         redisSetProcTitle( "redis-rdb-bgsave" ) ; // 设置标题         redisSetCpuAffinity(server. bgsave_cpulist ) ; // 设置 CPU 亲缘属性         retval = rdbSave(filename , rsi) ; // 开始保存         if (retval == C_OK) { // 发送保存数据             sendChildCowInfo( CHILD_INFO_TYPE_RDB_COW_SIZE , "RDB" ) ;         }         exitFromChild((retval == C_OK) ? 0 : 1 ) ; // 结束子进程保存     } else {         /* Parent */         // fork 子进程失败         if (childpid == - 1 ) {             server. lastbgsave_status = C_ERR ;             serverLog(LL_WARNING , "Can't save in background: fork: %s" ,             strerror(errno)) ;             return C_ERR ;         }         // 保存状态         serverLog(LL_NOTICE , "Background saving started by pid %ld" , ( long ) childpid) ;         server.rdb_save_time_start = time(NULL) ;         server. rdb_child_type = RDB_CHILD_TYPE_DISK ;         return C_OK ;     }     return C_OK ; /* unreached */ }  
上面解释了RDB是如何装载进来的,那下面来讲解一下RDB的文件结构。如何存储?如何压缩? RDB文件大致组成结构 - REDIS:标识RDB文件开头的字符'REDIS' - db_version:4字节。记录RDB文件版本号 - database:包含0-N个数据库 - EOF:标记RDB结束 - check_sum:保存前面四个的校验和,用于做签名校验   其中database存储结构如下 - SELECTDB:database存储的开始标识 - db_number:1/2/5字节。数据库号码 - key_value_pairs:存储当前数据库中所有的键值对数据, 根据类型不同而结构不同   key_value_pairs结构为 不带过期时间的键值对 - TYPE:记录value类型,主要含string、list、zset、hash、list_zipskiplist等 - key:redis key,总是string类型 - value:redis对应的值 带过期时间的键值对 - EXPIRETIME_MS:1字节。开始标识 - ms:8字节。UNIX时间戳 - TYPE:记录value类型,主要含string、list、zset、hash、list_zipskiplist等 - key:redis key,总是string类型 - value:redis对应的值   接下来讲解一下 value是怎么根据TYPE编码来生成RDB的对应值数据(大致讲解几个即可,知道什么回事,没必要纠结) String字符串 字符串<=20字节:len记录原来长度 字符串>20字节:origin_len记录原来长度,compresesed_len记录压缩后长度   List列表 记录长度,然后item一个接一个拼起来(每个元素记录长度)   set集合 跟list一样   hash哈希 记录有多少个键值对,比如下面:2个键值对,"a"->"apple","b"->"banana"   zset有序集合 记录分数和item      

标签:fork,Redis,RDB,bgsave,server,键值,rdb,解析
来源: https://blog.csdn.net/qq_28666081/article/details/117192030