内容简介:如果在
set
用来存储string的类型数据
> set key hello OK 复制代码
get
来获取string类型的值
> get key "hello" 复制代码
如果在 set
执行的时候,key已经存在,则会覆盖原有key的值
> set key anotherValue OK > get key "anotherValue" 复制代码
set命令执行追踪
redis.c
中数组 redisCommandTable 为所有暴漏出去的命令列表,以及实现命令的函数指针
struct redisCommand redisCommandTable[] = { ... {"get",getCommand,2,"rF",0,NULL,1,1,1,0,0}, {"set",setCommand,-3,"wm",0,NULL,1,1,1,0,0}, ... } 复制代码
从这里可以看到 setCommand 即为 set方法的入口。
Code.SLICE.source("c->argv[2] = tryObjectEncoding(c->argv[2]);") .interpretation("在对set的格式做完语法校验,同时取得相应的命令属于 NX/XX/EX/PX/直接set之后,根据value来获取编码"); Code.SLICE.source("setGenericCommand(c,flags,c->argv[1],c->argv[2],expire,unit,NULL,NULL);") .interpretation("根据实际情况存储k-v对"); 复制代码
在执行Set之前,redis并不是直接将原有传入的string储存,而是先选择了做一层编码,编码之后再来存
Code.SLICE.source("len = sdslen(s);") .interpretation("获取要存储的字符串值的长度,s取值即 redisObject指向的 数据字节指针"); Code.SLICE.source("if (len <= 20 && string2l(s,len,&value))") .interpretation("判断字符串的长度如果小于20并且能够转成long 类型,执行转成long 的逻辑,并结果存储到value"); //... Code.SLICE.source(" o->encoding = OBJ_ENCODING_INT;\n" + " o->ptr = (void*) value;") .interpretation("判定好是可以转成long则设定编码方式为int,同时数据指针就直接存储值"); //... Code.SLICE.source("if (len <= OBJ_ENCODING_EMBSTR_SIZE_LIMIT) ") .interpretation("如果字符串长度满足emb的长度条件(44),使用emb编码,使得通过一次内存分配函数的调用就可以拿到连续的内存空间存储 redisObject和 数据 sdshdr"); //... Code.SLICE.source(" emb = createEmbeddedStringObject(s,sdslen(s));") .interpretation("将值使用emb编码后再返回"); //... Code.SLICE.source("if (o->encoding == OBJ_ENCODING_RAW &&\n" + " sdsavail(s) > len/10)\n" + " {\n" + " o->ptr = sdsRemoveFreeSpace(o->ptr);\n" + " }") .interpretation("如果超过了emb限制,则尽量的去较少浪费的空间,将原始的内容直接返回"); //... 复制代码
对于 string 来说,编码是根据value的长度来按照不同的编码方式处理
- 小于等于20 并且能够转换成long,则存储成long类型的数字,指定编码为 OBJ_ENCODING_INT
- 如果长度小于44,则创建EmbeddedString,指定编码为 OBJ_ENCODING_EMBSTR
- 其它情况,指定编码为 OBJ_ENCODING_RAW
在转码过程中,传进来的数据会被转成 redisObject
typedef struct redisObject { unsigned type:4; //指string/list/hash/zset/set unsigned encoding:4; //数据自己的编码格式 unsigned lru:LRU_BITS; /* LRU time (relative to global lru_clock) or * LFU data (least significant 8 bits frequency * and most significant 16 bits access time). */ int refcount; //数据被引用的次数,为0表示可以安全回收这个对象 void *ptr; //对象数据 } robj; 复制代码
实际存储的时候会去检查是否已经有同名的key
Code.SLICE.source(" if (lookupKeyWrite(db,key) == NULL) {\n" + " dbAdd(db,key,val);\n" + " } else {\n" + " dbOverwrite(db,key,val);\n" + " }") .interpretation("如果之前没有存过,就直接添加,否则去覆盖"); 复制代码
每次在查找key的时候,同时也会去检查key是不是已经过期了,满足过期条件的key会被删除,然后再将传进来的string创建 sds 对象,存储起来
//... Code.SLICE.source("char type = sdsReqType(initlen);") .interpretation("根据要新建的字符串获取不同的类型,类型就是宏定义的 0 1 2 3 4这5个取值的类型,代表不同的 sdshdr 结构\n"); //... Code.SLICE.source(" switch(type) {\n" + " case SDS_TYPE_5: {\n" + " *fp = type | (initlen << SDS_TYPE_BITS);\n" + " break;\n" + " }\n" + " case SDS_TYPE_8: {\n" + " SDS_HDR_VAR(8,s);\n" + " sh->len = initlen;\n" + " sh->alloc = initlen;\n" + " *fp = type;\n" + " break;\n" + " }\n" + " case SDS_TYPE_16: {\n" + " SDS_HDR_VAR(16,s);\n" + " sh->len = initlen;\n" + " sh->alloc = initlen;\n" + " *fp = type;\n" + " break;\n" + " }\n" + " case SDS_TYPE_32: {\n" + " SDS_HDR_VAR(32,s);\n" + " sh->len = initlen;\n" + " sh->alloc = initlen;\n" + " *fp = type;\n" + " break;\n" + " }\n" + " case SDS_TYPE_64: {\n" + " SDS_HDR_VAR(64,s);\n" + " sh->len = initlen;\n" + " sh->alloc = initlen;\n" + " *fp = type;\n" + " break;\n" + " }\n" + " }") .interpretation("类型不同创建不同的结构"); 复制代码
字节长度不同创建的结构大小也不同,以 shshdr8 为例
Code.SLICE.source("struct __attribute__ ((__packed__)) sdshdr8 {\n" + " uint8_t len; /* 已经使用的长度 */\n" + " uint8_t alloc; /* 分配的长度 */\n" + " unsigned char flags; /* 3 lsb of type, 5 unused bits */\n" + " char buf[];\n" + "};") .interpretation("len表示使用了的长度,alloc表示分配的空间长度,flags的最低三个bit用来表示header的类型,类型比如 sdshdr8") .interpretation("1:uint8_t指的是 unsigned char ,大小为1字节 char buf[]本身不计算大小,只是真实数据存储的时候,会在 buf最后添加 1个 \0,为了和C做兼容,方便利用C的一些函数") .interpretation("2:__attribute__ ((__packed__)) 是为了告诉编译器,以紧凑的方式存放,不做对齐,redis这样做方便获取数据,比如要拿到flag只需要获取 buf的前一个地址即可"); 复制代码
不同的结构,header 占据空间也就不一样
总结
在读到set命令之后,对于传进来的数据会转换成redisObject,而根据string value长度的不同使用不同的编码,同时存储的结构也会不一样,以达到优化内存的目的
附录
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- LevelDB 源码分析(二):主体结构
- OkHttp源码分析 初识结构篇
- 【以太坊源码解析】-区块数据结构
- mybatis 源码分析(一)框架结构概览
- Python 2.7 源码 - pyc 的结构
- Redis 字符串内部结构源码分析
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Mathematica Cookbook
Sal Mangano / O'Reilly Media / 2009 / GBP 51.99
As the leading software application for symbolic mathematics, Mathematica is standard in many environments that rely on math, such as science, engineering, financial analysis, software development, an......一起来看看 《Mathematica Cookbook》 这本书的介绍吧!