首页 > 数据库 > NoSQL > 面试如何脱引而出?Redis字符串底层原理你掌握了吗
来源;后端开发技术
今天我们讲解字符串的底层原理,属于进阶内容,能回答出来可以秒杀80%的面试者。
首先补充一下基础知识,Redis对象由redisObject结构体表示。(这里的源码基于5.0.5版本)
typedef struct redisObject { // 类型 unsigned type:4; // 所使用的底层编码 unsigned encoding:4; // 对象最后一次被访问的时间 ,和redis 的lru算法实现有关 unsigned lru:REDIS_LRU_BITS; /* lru time (relative to server.lruclock) */ // 引用计数 int refcount; // 重点:指向对象的底层实现数据结构 void *ptr;} robj;Redis中的每个键值对的键和值都是一个redisObject。共有五种类型的对象:字符串(String)、列表(List)、哈希(Hash)、集合(Set)、有序集合(SortedSet),源码server.h如下定义:
#define OBJ_STRING 0 /* String object. */字符串#define OBJ_LIST 1 /* List object. */list#define OBJ_SET 2 /* Set object. */集合#define OBJ_ZSET 3 /* Sorted set object. */有序集合#define OBJ_HASH 4 /* Hash object. */哈希
#define OBJ_ENCODING_RAW 0 /* SDS简单动态字符串 Raw representation */#define OBJ_ENCODING_INT 1 /* 整数 Encoded as integer */#define OBJ_ENCODING_HT 2 /* 字典Encoded as hash table */#define OBJ_ENCODING_ZIPMAP 3 /* map Encoded as zipmap */#define OBJ_ENCODING_LINKEDLIST 4 /* No longer used: old list encoding. */#define OBJ_ENCODING_ZIPLIST 5 /* 压缩列表 Encoded as ziplist */#define OBJ_ENCODING_INTSET 6 /*整数集合 Encoded as intset */#define OBJ_ENCODING_SKIPLIST 7 /* 跳跃表Encoded as skiplist */#define OBJ_ENCODING_EMBSTR 8 /* 嵌入式SDS Embedded sds string encoding */#define OBJ_ENCODING_QUICKLIST 9 /* Encoded as linked list of ziplists */#define OBJ_ENCODING_STREAM 10 /* Encoded as a radix tree of listpacks */
Redis的String类型在5.0.8版本中主要有三种编码实现,分别是整数、简单动态字符、嵌入式字符串。
我们可能以为redis在内部存储string都是用sds的数据结构实现的,其实在整个redis的数据存储过程中为了提高性能,内部做了很多优化。整体选择顺序应该是:
•整数,存储字符串长度小于21且能够转化为整数的字符串。
•EmbeddedString,存储字符串长度小于44的字符串。
•SDS,剩余情况使用sds进行存储。
// EG:sdshdr8struct __attribute__ ((__packed__)) sdshdr8 { uint8_t len; /* 字符数组现有长度*/ uint8_t alloc; /* 字符数组的已分配空间,不包括结构体和\0结束字符*/ unsigned char flags; /* SDS类型*/ char buf[]; /*字符数组*/};
typedef char *sds;
#define OBJ_ENCODING_EMBSTR_SIZE_LIMIT 44robj *createStringObject(const char *ptr, size_t len) { //创建嵌入式字符串,字符串长度小于等于44字节 if (len <= OBJ_ENCODING_EMBSTR_SIZE_LIMIT) return createEmbeddedStringObject(ptr,len); //创建普通字符串,字符串长度大于44字节 else return createRawStringObject(ptr,len);}首先,createEmbeddedStringObject 函数会分配一块连续的内存空间,这块内存空间的大小等于 redisObject 结构体的大小、SDS 结构头 sdshdr8 的大小和字符串大小的总和,并且再加上 1 字节。注意,这里最后的 1 字节是 SDS 中加在字符串最后的结束字符“\0”。
在 Redis 源码中,createStringObject 函数会根据要创建的字符串的长度,决定具体调用哪个函数来完成创建。那么针对这个 createStringObject 函数来说,它的参数是字符串 ptr 和字符串长度 len。当 len 的长度大于 OBJ_ENCODING_EMBSTR_SIZE_LIMIT 这个宏定义时,createStringObject 函数会调用 createRawStringObject 函数,否则就调用 createEmbeddedStringObject 函数。而在我们分析的 Redis 5.0.8 源码版本中,这个 OBJ_ENCODING_EMBSTR_SIZE_LIMIT 默认定义为 44 字节。
embstr和sds的区别在于内存的申请和回收
// robj *tryObjectEncoding(robj *o):尝试对字符串对象进行编码,以节约内存// OBJ_ENCODING_INT 1...// 只对长度小于或等于 20 字节,并且可以被解释为整数的字符串进行编码 if (len <= 20 && string2l(s,len,&value)) { ... o->encoding = OBJ_ENCODING_INT; o->ptr = (void*) value; return o; ...加入讨论群是升职加薪第一步!
回复:加群

点赞是一种美德,如对您有帮助,欢迎评论和分享,感谢阅读!
2023-04-04

2023-03-29

2023-03-25

来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70027826/viewspace-2946237/ ,如需转载,请注明出处,否则将追究法律责任。