当前位置: 首页 > 后端技术 > PHP

【Redis5源码学习】redis命令dump分析

时间:2023-03-29 18:32:19 PHP

葡萄官方文档DUMPkey序列化给定的key,返回序列化后的值。使用RESTORE命令将此值反序列化为Redis键。序列化生成的值有以下特点:它有一个64位的校验和用于检测错误,RESTORE会在反序列化之前检查校验和。该值的编码格式与RDB文件一致。RDB版本将编码在序列化值中。如果由于Redis版本不同导致RDB格式不兼容,Redis将拒绝反序列化该值。序列化值不包含任何生存时间信息。可用版本:>=2.6.0时间复杂度:查找给定的键是O(1),序列化键是O(N*M),其中N是组成键号的Redis对象的数量,M是这些对象的平均大小。如果序列化的对象是一个比较小的字符串,那么复杂度就是O(1)。返回值:如果key不存在,则返回nil。否则,返回序列化后的值。redis>SET问候“你好,倾倒世界!”OKredis>DUMPgreeting"\x00\x15hello,dumpingworld!\x06\x00E\xa0Z\x82\xd8r\xc1\xde"redis>DUMPnot-exists-key(nil)我们可以看到dump命令是序列化给定的钥匙。那么什么是序列化呢?先看一下序列化的定义:序列化是将对象的状态信息转换成可以存储或传输的形式的过程。在序列化期间,对象将其当前状态写入临时或持久存储。稍后,可以通过从存储中读取或反序列化对象的状态来重新创建对象。目的是实现对象的跨平台存储和网络传输。该命令的使用很简单,就是dumpkey,dump通常与RESTORE、序列化和反序列化结合使用。如果想深入了解连载,推荐阅读:连载通俗易懂。源码分析首先我们贴上源码:/*DUMPkeyname*DUMP实际上并没有被RedisCluster使用,但它是RESTORE的明显*补充,可以用于不同的应用程序。*/voiddumpCommand(client*c){robj*o,*dumpobj;里约有效载荷;/*检查键是否存在*/if((o=lookupKeyRead(c->db,c->argv[1]))==NULL){addReply(c,shared.nullbulk);返回;}/*创建转储负载。*/createDumpPayload(&payload,o);/*传输给客户端*/dumpobj=createObject(OBJ_STRING,payload.io.buffer.ptr);addReplyBulk(c,dumpobj);decrRefCount(dumpobj);return;}接下来我们慢慢分析。dump命令的核心是创建dumpload,所以我们的核心就在这个过程中。首先我们简单描述一下大概的流程:首先检查我们要序列化的key是否存在,如果存在则创建一个dumpload然后传给client。检查key是否存在主要通过lookupkeyRead实现,大致就是检查redisDB中是否存在key,存在则向下执行,否则发送信息给客户端。通常当我们转储一个不存在的键时,我们会得到一个nil结果。创建转储负载,这是转储命令的核心。具体实现代码如下:voidcreateDumpPayload(rio*payload,robj*o){unsignedcharbuf[2];uint64_tcrc;/*以类似RDB的格式序列化对象。它由一个对象类型字节和后跟序列化对象组成。RESTORE理解这一点。该对象以类似rdb的格式序列化。它由对象类型字节和序列化对象组成。*/rioInitWithBuffer(payload,sdsempty());/*将给定的对象类型写入rdb,失败时报错*/serverAssert(rdbSaveObjectType(payload,o));/*将给定对象写入rdb,失败并出现错误*/serverAssert(rdbSaveObject(payload,o));/*写页脚,这是它的样子:--------------+--------------------+--------------+...RDB负载|2字节RDB版本|8字节CRC64|--------------+--------------------+--------------+RDBversion和CRC都是littleendian。/*RDB版本存储在两个字节中,表示为0-65535*/buf[0]=RDB_VERSION&0xff;buf[1]=(RDB_VERSION>>8)&0xff;/*sdscatlen函数是扩展长度,追加字符串payload->io.buffer.ptr=sdscatlen(payload->io.buffer.ptr,buf,2);/*计算CRC校验码,共8个字节*/crc=crc64(0,(unsignedchar*)payload->io.buffer.ptr,sdslen(payload->io.buffer.ptr));/*目标机器是bigendianendian机器进行字节码转换,提供16byte、32byte、64byte字节转换。在intset\ziplist\zipmap这三种数据结构中使用,使得不同字节序的机器生成的rdb文件格式统一(littleendian字节序),易于兼容。*/memrev64ifbe(&crc);payload->io.buffer.ptr=sdscatlen(payload->io.buffer.ptr,&crc,8);}最终序列化后的对象格式为如下格式:+------------+--------------------+--------------+|RDB负载|2字节RDB版本|8字节CRC64|+------------+--------------------+————————+向客户端发送信息,最后一块我们可以认为是消息传递,将排序后的信息传递给客户端。后面我会写一个专题来介绍这个区块。如果读者有兴趣,一定要亲自尝试gdb!!扩展阅读rdb持久化redis转换大小端分析