大家好,我是热心的大肚皮,皮哥。我们又多了一个系列-redis。
redis是互联网技术架构在存储系统中使用最多的中间件,也是面试必问的技能之一。希望通过自己实战经验,能帮助更多后端开发者更深更快的掌握redis。不多说了,开整。
什么是redis?
redis是"Remote Dictionary Service"(远程字典服务)的首字母缩写。具有超高的性能、完美的文档、简洁易懂的源码和丰富的客户端在开源中间件领域中广受好评。
redis有几种数据结构呢?
redis有以下5种数据结构。
今天我们先通关第一种string。
string
数据结构
redis中的字符串也叫做"SDS",也就是Simple Dynamic String。是一个带长度信息的字节数组。如下图。
直接上源码。
// 对象属性
struct SDS<T> {
// 数组容量
T capacity;
// 数组长度
T len;
// 特殊标志位,暂时不用管
byte flags;
//数组内容
byte[] content;
}
//追加 SDS 字符串
sds sdscatlen(sds s, const void *t, size_t len){
// 原字符串的长度
size_t curlen = sdslen(s);
//按需调整空间,如果capacity不够容纳追加d的内容,就会从新分配
//字节数据,并将原内容复制到新数组中
s = sdsMakeRoomFor(s, len);
//内存不足
if(s == NULL) return NULL;
//追加目标字符串内容到字节数组
memcpy(s+curlen, t, len);
//设置追加后的字符串长度
sdssetlen(s, curlen + len);
//让字符串以\0结尾,便于调试打印。
s[curlen+len]='\0';
return s;
}
/*空间调整,注意只是调整空间,后续自己组装字符串*/
sds sdsMakeRoomFor(sds s, size_t addlen) {
void *sh, *newsh;
// 当前剩下的空间
size_t avail = sdsavail(s);
size_t len, newlen;
char type, oldtype = s[-1] & SDS_TYPE_MASK;
int hdrlen;
/* 空间足够 */
if (avail >= addlen) return s;
// 长度
len = sdslen(s);
// 真正的数据体
sh = (char*)s-sdsHdrSize(oldtype);
// 新长度
newlen = (len+addlen);
// < 1M 2倍扩容
if (newlen < SDS_MAX_PREALLOC)
newlen *= 2;
// > 1M 扩容1M
else
newlen += SDS_MAX_PREALLOC;
// 获取sds 结构类型
type = sdsReqType(newlen);
// type5 默认转成 type8
if (type == SDS_TYPE_5) type = SDS_TYPE_8;
// 头长度
hdrlen = sdsHdrSize(type);
if (oldtype==type) { // 长度够用 并且 数据结构不变
newsh = s_realloc(sh, hdrlen+newlen+1);
if (newsh == NULL) return NULL;
s = (char*)newsh+hdrlen;
} else {
// 重新申请内存
newsh = s_malloc(hdrlen+newlen+1);
if (newsh == NULL) return NULL;
memcpy((char*)newsh+hdrlen, s, len+1);
s_free(sh);
s = (char*)newsh+hdrlen;
s[-1] = type;
sdssetlen(s, len);
}
sdssetalloc(s, newlen);
return s;
}
细心的小伙伴有没有发现,为什么capacity与len 不用int 而是 T呢?原因是当字符串比较短时,capacity与len可以用short 或者byte来表示,也是为了对内存极致的优化。
扩容规则
每次创建时capacity与len一样大,点那个字符串长度小于1MB时,每次扩容都是加倍现有的空间,如果长度大于1MB,则每次只会扩容1MB的空间,注意字符串在这里最大长度为512MB。
存储方式
分为embstr与raw两种。当字符串超过44字节,则采用raw存储。那么为什么是44字节呢?首先我们要了解,在redis中,每一个对象都有一个对象头结构。
struct RedisObject {
//不同的对象都有不同的类型 4bits
int4 type;
//同一个类型会有不同的存储形式 4bits
int4 encoding;
//对象d的lru信息,使用24bits
int24 lru;
//引用计数,为0时则对象会被销毁 4bytes
int32 refcount;
//指向对象内容的具体存储位置,8bytes
void *ptr;
}
一个字符串在内存的结构如下图。
我们可以看出来对象头RedisObject需要16个字节的空间。er内存分配器jemalloc、tcmalloc分配内存大小都是2/4/8/16/32/64 字节,而字符串不算内容最少需要19个字节,redis的作者考虑到性能,将64字节作为分界线,这样计算,也就是当字符串长度等于 64-19=45,但是字符串又是以null作为结尾,所以边界则是44字节。具体的存储方式为连续内存与,非连续内存,如下图。
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有