家装公司网站开发方案,永久免费无代码开发平台,html做的网站怎么发布,工业设计排名前十的大学文章目录 1. 前提回顾2. RedisObject三大数据类型简介3. SDS字符串4. SDS字符串源码分析5. 总结 1. 前提回顾
前面我们说到redis的String数据结构在底层有多种编码方式。例如我们执行下面两条语句
set k1 v1
set age 17我们查看类型#xff0c;发现这类型都是String类型 我们… 文章目录 1. 前提回顾2. RedisObject三大数据类型简介3. SDS字符串4. SDS字符串源码分析5. 总结 1. 前提回顾
前面我们说到redis的String数据结构在底层有多种编码方式。例如我们执行下面两条语句
set k1 v1
set age 17我们查看类型发现这类型都是String类型 我们现在查看底层的编码类型发现编码类型一个是embstr一个是int类型虽然都是String字符串但redis根据value的不同类型设置了不同的编码方式 redisObject内部对应3大物理编码int、embstr和raw 2. RedisObject三大数据类型简介
int
它保存long型长整型的64位8个字节有符号整数9223372036854775807最高19位。 只有整数才会使用int类型去编码如果是浮点数redis内部其实先将浮点数转化为字符串值然后再保存。 emstr
代表embstr格式的SDSSimple Dynamic String 简单动态字符串保存长度小于44字节的字符串embstr即embedded String表示嵌入式的String
raw
保存长度大于44字节的字符串
下面给演示一下各个类型的使用 首先长度小于19位的数字类型为int 浮点数底层为emstr 长度超过19的数字类型为emstr 普通字符串是emstr 长度超过44的字符串为raw类型 长度超过44的浮点数为raw类型 3. SDS字符串
我们知道c语言底层表示字符串本质上是用的一个char数组。但是redis没有直接复用C语言的字符串的底层实现而是新建了属于自己的结构—SDS在redis数据库里包含字符串值的键值对都是由SDS实现的redis中所有的键都是由字符串对象实现的即底层是由SDS实现Redis所有的值对象中包含的字符串对象底层也是SDS实现的 我们看一下sds结构体的源码
typedef char *sds;/* Note: sdshdr5 is never used, we just access the flags byte directly.* However is here to document the layout of type 5 SDS strings. */
struct __attribute__ ((__packed__)) sdshdr5 {unsigned char flags; /* 3 lsb of type, and 5 msb of string length */char buf[];
};
struct __attribute__ ((__packed__)) sdshdr8 {uint8_t len; /* used */uint8_t alloc; /* excluding the header and null terminator */unsigned char flags; /* 3 lsb of type, 5 unused bits */char buf[];
};
struct __attribute__ ((__packed__)) sdshdr16 {uint16_t len; /* used */uint16_t alloc; /* excluding the header and null terminator */unsigned char flags; /* 3 lsb of type, 5 unused bits */char buf[];
};
struct __attribute__ ((__packed__)) sdshdr32 {uint32_t len; /* used */uint32_t alloc; /* excluding the header and null terminator */unsigned char flags; /* 3 lsb of type, 5 unused bits */char buf[];
};
struct __attribute__ ((__packed__)) sdshdr64 {uint64_t len; /* used */uint64_t alloc; /* excluding the header and null terminator */unsigned char flags; /* 3 lsb of type, 5 unused bits */char buf[];
};可以看到redis底层工定义了五种sds字符串的结构分别是sdshdr5、sdshdr8、sdshdr16、sdshdr32和sdshdr64。那么各个字段是什么意思
len代表存储的字符串的长度alloc存储了为字符串分配的内存空间的长度即实际分配的空间大小不包括结构体头部和字符串结束符 \0flags代表SDS字符串的类型上面介绍类型的五种之一buf[]真正存储字符串的char数组
那上面五种类型有什么不同 sdshdr5表示 2 5 2^5 25即共32个字节 sdshdr8表示 2 8 2^8 28即256个字节 sdshdr16表示 2 16 2^{16} 216即65536个字节64KB sdshdr32表示 2 32 2^{32} 232即4GB sdshdr64表示 2 64 2^{64} 264即17179869184G个字节 len表示SDS实际的长度使我们在获取字符串的时候能够在O(1)时间复杂度情况下拿到而不需要遍历一遍char数组
alloc可以用来计算free就是字符串已经分配的未使用空间这个值就可以引入预分配空间算法了而不用去考虑内存分配问题
buf字符串数组真正存数据的 alloc 字段的作用是提供了对字符串进行动态增长的能力。当字符串需要扩容时Redis 可以根据需要重新分配更大的内存空间并将原有的内容复制到新的内存中然后释放原来的内存。这样做的好处是可以减少频繁地进行内存分配和释放操作提高了性能。举个例子假设有一个 SDS 字符串其实际长度为 10但为了防止频繁的内存分配和释放操作alloc 被设置为了 20。当这个字符串需要扩容时Redis 可以直接在内存中分配一个长度为 20 的新空间然后将原字符串内容复制到新的空间中同时更新 len 和 alloc 字段。这样即使字符串长度增长也不需要每次都重新分配内存减少了系统的开销。 现在思考一个问题Redis为什么不直接使用char数组而是自己重写设计来一个SDS数据结构
这是因为C语言没有Java里面的String类型只能是靠自己的char[]来实现字符串在C语言中的存储方式如果现在我们想通过strlen命令获取value的长度我们就需要从头开始遍历知道遇到\0即可这样操作的时间复杂度是O(n)所以redis没有直接使用C语言传统的字符串标识而是自己构建了一种名为简单动态字符串类型SDS并作为redis的默认字符串类型。 4. SDS字符串源码分析 当我们输入set k1 v1 底层到底发生了什么这里我们详细分析一下 首先看t_string.c文件
/* SET key value [NX] [XX] [KEEPTTL] [GET] [EX seconds] [PX milliseconds]* [EXAT seconds-timestamp][PXAT milliseconds-timestamp] */
void setCommand(client *c) {//key的过期时间robj *expire NULL;//时间的单位是sint unit UNIT_SECONDS;int flags OBJ_NO_FLAGS;if (parseExtendedStringArgumentsOrReply(c,flags,unit,expire,COMMAND_SET) ! C_OK) {return;}//c-args[2]就是value这里调用tryObjectEncoding就是尝试堆value值进行编码编码后的结果再保存到c-args[2]中c-argv[2] tryObjectEncoding(c-argv[2]);setGenericCommand(c,flags,c-argv[1],c-argv[2],expire,unit,NULL,NULL);
}当我们在cli写set k1 v1时它底层本质上调用的是上面的setCommand命令 从上面代码代码可以看出字符串的编码和tryObjectEncoding这个函数时紧密相关的我们来分析一下底层是怎么做的
int
当字符串键值的内存可以用一个64位有符号整数表示时Redis会将键值转化为long型来存储此时即对于OBJ_ENDOCDING_INT编码类型。内存内存结构如下 OBJECT_ENCODING_INT这个时Redis定义的一个常量我们可以在object.c文件中看到
char *strEncoding(int encoding) {switch(encoding) {case OBJ_ENCODING_RAW: return raw;case OBJ_ENCODING_INT: return int;case OBJ_ENCODING_HT: return hashtable;case OBJ_ENCODING_QUICKLIST: return quicklist;case OBJ_ENCODING_LISTPACK: return listpack;case OBJ_ENCODING_INTSET: return intset;case OBJ_ENCODING_SKIPLIST: return skiplist;case OBJ_ENCODING_EMBSTR: return embstr;case OBJ_ENCODING_STREAM: return stream;default: return unknown;}
}Redis启动时会预先建立10000个分别存储09999的redisObject变量做为共享变量这意味着如果set字符串的键值在0~10000之间的话就可以直接指向功效变量而不需要再建立新对象此时键值不占内存。这和java中有些包装类设置缓存的原理一样。如下面两条命令
set k1 100
set k2 100上面的缓存可以再server.h中可以看到定义:
#define OBJ_SHARED_INTEGERS 10000 //缓存去大小struct sharedObjectsStruct {robj *ok, *err, *emptybulk, *czero, *cone, *pong, *space,*queued, *null[4], *nullarray[4], *emptymap[4], *emptyset[4],*emptyarray, *wrongtypeerr, *nokeyerr, *syntaxerr, *sameobjecterr,*outofrangeerr, *noscripterr, *loadingerr,*slowevalerr, *slowscripterr, *slowmoduleerr, *bgsaveerr,*masterdownerr, *roslaveerr, *execaborterr, *noautherr, *noreplicaserr,*busykeyerr, *oomerr, *plus, *messagebulk, *pmessagebulk, *subscribebulk,*unsubscribebulk, *psubscribebulk, *punsubscribebulk, *del, *unlink,*rpop, *lpop, *lpush, *rpoplpush, *lmove, *blmove, *zpopmin, *zpopmax,*emptyscan, *multi, *exec, *left, *right, *hset, *srem, *xgroup, *xclaim, *script, *replconf, *eval, *persist, *set, *pexpireat, *pexpire, *time, *pxat, *absttl, *retrycount, *force, *justid, *entriesread,*lastid, *ping, *setid, *keepttl, *load, *createconsumer,*getack, *special_asterick, *special_equals, *default_username, *redacted,*ssubscribebulk,*sunsubscribebulk, *smessagebulk,*select[PROTO_SHARED_SELECT_CMDS],*integers[OBJ_SHARED_INTEGERS],*mbulkhdr[OBJ_SHARED_BULKHDR_LEN], /* *value\r\n */*bulkhdr[OBJ_SHARED_BULKHDR_LEN], /* $value\r\n */*maphdr[OBJ_SHARED_BULKHDR_LEN], /* %value\r\n */*sethdr[OBJ_SHARED_BULKHDR_LEN]; /* ~value\r\n */sds minstring, maxstring;
};下面我们看看redis时如何利用这些缓存起来的对象的
/* Try to encode a string object in order to save space */
robj *tryObjectEncodingEx(robj *o, int try_trim) {long value;sds s o-ptr;size_t len;serverAssertWithInfo(NULL,o,o-type OBJ_STRING);if (!sdsEncodedObject(o)) return o;if (o-refcount 1) return o;len sdslen(s);if (len 20 string2l(s,len,value)) {if ((server.maxmemory 0 ||!(server.maxmemory_policy MAXMEMORY_FLAG_NO_SHARED_INTEGERS)) value 0 value OBJ_SHARED_INTEGERS){decrRefCount(o);return shared.integers[value];} else {if (o-encoding OBJ_ENCODING_RAW) {sdsfree(o-ptr);o-encoding OBJ_ENCODING_INT;o-ptr (void*) value;return o;} else if (o-encoding OBJ_ENCODING_EMBSTR) {decrRefCount(o);return createStringObjectFromLongLongForValue(value);}}}/* If the string is small and is still RAW encoded,* try the EMBSTR encoding which is more efficient.* In this representation the object and the SDS string are allocated* in the same chunk of memory to save space and cache misses. */if (len OBJ_ENCODING_EMBSTR_SIZE_LIMIT) {robj *emb;if (o-encoding OBJ_ENCODING_EMBSTR) return o;emb createEmbeddedStringObject(s,sdslen(s));decrRefCount(o);return emb;}/* We cant encode the object...* Do the last try, and at least optimize the SDS string inside */if (try_trim)trimStringObjectIfNeeded(o, 0);/* Return the original object. */return o;
}robj *tryObjectEncoding(robj *o) {return tryObjectEncodingEx(o, 1);
}关键是下面这几句代码
if ((server.maxmemory 0 ||!(server.maxmemory_policy MAXMEMORY_FLAG_NO_SHARED_INTEGERS)) value 0 value OBJ_SHARED_INTEGERS){decrRefCount(o);return shared.integers[value];} else {if (o-encoding OBJ_ENCODING_RAW) {sdsfree(o-ptr);o-encoding OBJ_ENCODING_INT;o-ptr (void*) value;return o;} else if (o-encoding OBJ_ENCODING_EMBSTR) {decrRefCount(o);return createStringObjectFromLongLongForValue(value);}}value OBJ_SHARED_INTEGERS判断当前值是否小于10000如何小于shared.integers[value]中直接拿值上面也是对INT类型编码的核心源码
embstr
上面我们分析了int类型是如何编码的下面我们看看embstr类型是如何编码的
#define OBJ_ENCODING_EMBSTR_SIZE_LIMIT 44
robj *createStringObject(const char *ptr, size_t len) {if (len OBJ_ENCODING_EMBSTR_SIZE_LIMIT)return createEmbeddedStringObject(ptr,len);elsereturn createRawStringObject(ptr,len);
}robj *createEmbeddedStringObject(const char *ptr, size_t len) {//创建redisObject对象并分配内存使用 zmalloc 函数分配了一块内存用于存储字符串对象。这块内存的大小包括了字符串对象结构体 robj 的大小、sdshdr8 结构体的大小以及字符串的长度 len 加上一个字节用于字符串的结束符 \0。robj *o zmalloc(sizeof(robj)sizeof(struct sdshdr8)len1);//定义了一个 sdshdr8 结构体指针 sh它指向了 robj 结构体之后的内存地址。这是因为 robj 结构体之后的内存空间被用来存储字符串数据的头信息。struct sdshdr8 *sh (void*)(o1);//类型表示为String类型o-type OBJ_STRING;//编码为embstr编码o-encoding OBJ_ENCODING_EMBSTR;//设置了 robj 结构体中的 ptr 字段为指向 sdshdr8 结构体之后的内存地址即指向字符串的实际内容。o-ptr sh1;//引用计数为1o-refcount 1;//使用次数0o-lru 0;sh-len len;sh-alloc len;sh-flags SDS_TYPE_8;if (ptr SDS_NOINIT)sh-buf[len] \0;else if (ptr) {memcpy(sh-buf,ptr,len);sh-buf[len] \0;} else {memset(sh-buf,0,len1);}return o;
}
首先len OBJ_ENCODING_EMBSTR_SIZE_LIMIT判断当前字符串的长度是否小于44这前面也讲过为什么是44如果小于则符合emstr类型则调用createEmbeddedStringObject方法。我们看到ptr指针指向的位置为sh1。我们从源码可以看出sh是robj 结构体位置这就说明一个现象即字符串SDS结构体和其对应的redisObject对象分配在同一连续内存空间就像讲sds嵌入到redisObject中。 简单来说redisObject和对应的sdshdr8是在内存汇总紧挨着的使用 embstr 编码时字符串的内容直接存储在 robj 结构体之后的内存空间中而不需要额外分配存储空间。这样做的好处是减少了内存分配和内存碎片提高了内存使用效率。 raw
我们继续回到tryEncodingEx方法如果我们对象的字节数大于44我们此时就是创建raw类型
obj *createObject(int type, void *ptr) {robj *o zmalloc(sizeof(*o));o-type type;o-encoding OBJ_ENCODING_RAW;o-ptr ptr;o-refcount 1;o-lru 0;return o;
}当字符串长度大于44的超长字符串时Redis则将键值的内部编码方式改为OBJ_ENCODING_RAW格式这与OBJ_ENCODING_EMBSTR的编码方式不同此时动态字符串sds的内存与其依赖的redisObject内存不再连续。 下面看一种特殊情况
5. 总结 redis底层会根据用户给的键值使用不同的编码格式自适应地选择较优化的内存编码格式而这一切对用户完全透明 文章转载自: http://www.morning.kybjr.cn.gov.cn.kybjr.cn http://www.morning.zmzdx.cn.gov.cn.zmzdx.cn http://www.morning.lfdmf.cn.gov.cn.lfdmf.cn http://www.morning.mmxt.cn.gov.cn.mmxt.cn http://www.morning.qnjcx.cn.gov.cn.qnjcx.cn http://www.morning.rkxk.cn.gov.cn.rkxk.cn http://www.morning.rjynd.cn.gov.cn.rjynd.cn http://www.morning.tkzqw.cn.gov.cn.tkzqw.cn http://www.morning.pnmgr.cn.gov.cn.pnmgr.cn http://www.morning.rxfbf.cn.gov.cn.rxfbf.cn http://www.morning.sfphz.cn.gov.cn.sfphz.cn http://www.morning.yhljc.cn.gov.cn.yhljc.cn http://www.morning.njfgl.cn.gov.cn.njfgl.cn http://www.morning.dhyzr.cn.gov.cn.dhyzr.cn http://www.morning.ryfqj.cn.gov.cn.ryfqj.cn http://www.morning.yhtnr.cn.gov.cn.yhtnr.cn http://www.morning.bpmtx.cn.gov.cn.bpmtx.cn http://www.morning.ggnkt.cn.gov.cn.ggnkt.cn http://www.morning.hxxyp.cn.gov.cn.hxxyp.cn http://www.morning.nynyj.cn.gov.cn.nynyj.cn http://www.morning.pndhh.cn.gov.cn.pndhh.cn http://www.morning.srjgz.cn.gov.cn.srjgz.cn http://www.morning.rkzb.cn.gov.cn.rkzb.cn http://www.morning.weiwt.com.gov.cn.weiwt.com http://www.morning.kpbgp.cn.gov.cn.kpbgp.cn http://www.morning.pnljy.cn.gov.cn.pnljy.cn http://www.morning.mtxrq.cn.gov.cn.mtxrq.cn http://www.morning.qkzdc.cn.gov.cn.qkzdc.cn http://www.morning.zmlnp.cn.gov.cn.zmlnp.cn http://www.morning.fsjcn.cn.gov.cn.fsjcn.cn http://www.morning.rqlbp.cn.gov.cn.rqlbp.cn http://www.morning.qynnw.cn.gov.cn.qynnw.cn http://www.morning.gtqx.cn.gov.cn.gtqx.cn http://www.morning.gftnx.cn.gov.cn.gftnx.cn http://www.morning.yzktr.cn.gov.cn.yzktr.cn http://www.morning.dycbp.cn.gov.cn.dycbp.cn http://www.morning.qfcnp.cn.gov.cn.qfcnp.cn http://www.morning.wjhdn.cn.gov.cn.wjhdn.cn http://www.morning.cxryx.cn.gov.cn.cxryx.cn http://www.morning.xzkgp.cn.gov.cn.xzkgp.cn http://www.morning.kdbcx.cn.gov.cn.kdbcx.cn http://www.morning.trrhj.cn.gov.cn.trrhj.cn http://www.morning.blfll.cn.gov.cn.blfll.cn http://www.morning.ryysc.cn.gov.cn.ryysc.cn http://www.morning.skbbt.cn.gov.cn.skbbt.cn http://www.morning.pffqh.cn.gov.cn.pffqh.cn http://www.morning.jlxld.cn.gov.cn.jlxld.cn http://www.morning.xsklp.cn.gov.cn.xsklp.cn http://www.morning.rfwgg.cn.gov.cn.rfwgg.cn http://www.morning.qtwd.cn.gov.cn.qtwd.cn http://www.morning.lclpj.cn.gov.cn.lclpj.cn http://www.morning.kfwqd.cn.gov.cn.kfwqd.cn http://www.morning.tsflw.cn.gov.cn.tsflw.cn http://www.morning.sqmlw.cn.gov.cn.sqmlw.cn http://www.morning.tqdqc.cn.gov.cn.tqdqc.cn http://www.morning.rbyz.cn.gov.cn.rbyz.cn http://www.morning.rfhwc.cn.gov.cn.rfhwc.cn http://www.morning.mlnzx.cn.gov.cn.mlnzx.cn http://www.morning.sfsjh.cn.gov.cn.sfsjh.cn http://www.morning.kxryg.cn.gov.cn.kxryg.cn http://www.morning.zlxrg.cn.gov.cn.zlxrg.cn http://www.morning.mlycx.cn.gov.cn.mlycx.cn http://www.morning.rrpsw.cn.gov.cn.rrpsw.cn http://www.morning.wdqhg.cn.gov.cn.wdqhg.cn http://www.morning.lmcrc.cn.gov.cn.lmcrc.cn http://www.morning.zrhhb.cn.gov.cn.zrhhb.cn http://www.morning.klrpm.cn.gov.cn.klrpm.cn http://www.morning.bpmnz.cn.gov.cn.bpmnz.cn http://www.morning.dyxlj.cn.gov.cn.dyxlj.cn http://www.morning.yfcbf.cn.gov.cn.yfcbf.cn http://www.morning.bntgy.cn.gov.cn.bntgy.cn http://www.morning.smj79.cn.gov.cn.smj79.cn http://www.morning.nkdmd.cn.gov.cn.nkdmd.cn http://www.morning.wmnpm.cn.gov.cn.wmnpm.cn http://www.morning.dtfgr.cn.gov.cn.dtfgr.cn http://www.morning.cpkcq.cn.gov.cn.cpkcq.cn http://www.morning.gbwfx.cn.gov.cn.gbwfx.cn http://www.morning.dwncg.cn.gov.cn.dwncg.cn http://www.morning.btjyp.cn.gov.cn.btjyp.cn http://www.morning.mfjfh.cn.gov.cn.mfjfh.cn