Redis 数据结构与内存管理策略(下) (2)

redis 会根据我们设置的值类型动态 sizeof 出一个对应的空间大小。如果我们集合原来是 int16 ,然后往集合里添加了 int32 整数将触发升级,一旦升级成功不会触发降级操作。

压缩表(zip list)

zip list 压缩表是 listzsethash 数据类型的底层数据结构之一。它是为了节省内存通过压缩数据存储在一块连续的内存空间中。

typedef struct zlentry { unsigned int prevrawlensize, prevrawlen; unsigned int lensize, len; unsigned int headersize; unsigned char encoding; unsigned char *p; } zlentry;

它最大的优点就是压缩空间,空间利用率很高。缺点就是一旦出现更新可能就是连锁更新,因为数据在内容空间中都是连续的,最极端情况下就是可能出现顺序连锁扩张。

压缩列表会由多个 zlentry 节点组成,每一个 zlentry 记录上一个节点长度和大小,当前节点长度 lensize 和大小 len 包括编码 encoding

这取决于业务场景,redis 提供了一组配置,专门用来针对不同的场景进行阈值控制。

hash-max-ziplist-entries 512 hash-max-ziplist-value 64 list-max-ziplist-entries 512 list-max-ziplist-value 64 zset-max-ziplist-entries 128 zset-max-ziplist-value 64

上述配置分别用来配置 ziplist 作为 hashlistzset 数据类型的底层压缩阈值控制。

Redis Object 类型与映射

redis 内部每一种数据类型都是对象化的,也就是我们所说的5种数据类型其实内部都会对应到 redisObject 对象,然后在由 redisObject 来包装具体的存储数据结构和编码。

typedef struct redisObject { unsigned type:4; unsigned encoding:4; unsigned lru:REDIS_LRU_BITS; int refcount; void *ptr; } robj;

这是一个很 OO 的设计,redisObject->type5 种数据类型之一,redisObject->encoding 是这个数据类型所使用的数据结构和编码。

我们看下 redis 提供的 5 种数据类型与每一种数据类型对应的存储数据结构和编码。

/* Object types */ #define REDIS_STRING 0 #define REDIS_LIST 1 #define REDIS_SET 2 #define REDIS_ZSET 3 #define REDIS_HASH 4 #define REDIS_ENCODING_RAW 0 #define REDIS_ENCODING_INT 1 #define REDIS_ENCODING_HT 2 #define REDIS_ENCODING_ZIPMAP 3 #define REDIS_ENCODING_LINKEDLIST 4 #define REDIS_ENCODING_ZIPLIST 5 #define REDIS_ENCODING_INTSET 6 #define REDIS_ENCODING_SKIPLIST 7 #define REDIS_ENCODING_EMBSTR 8

REDIS_ENCODING_ZIPMAP 3 这个编码可以忽略了,在特定的情况下有性能问题,在 redis 2.6 版本之后已经废弃,为了兼容性保留。

Redis 数据结构与内存管理策略

上图是 redis 5 种数据类型与底层数据结构和编码的对应关系,但是这种对应关系在每一个版本中都会有可能发生变化,这也是 redisObject 的灵活性所在,有着 OO 的这种多态性。

redisObject->refcount 表示当前对象的引用计数,在 redis 内部为了节省内存采用了共享对象的方法,当某个对象被引用的时候这个 refcount 会加 1,释放的时候会减 1。

redisObject->lru 表示当前对象的 空转时长,也就是 idle time ,这个时间会是 redis lru 算法用来释放对象的时间依据。可以通过 OBJECT idletime 命令查看某个 key 的空转时长 lru 时间。

Redis 内存管理策略

redis 在服务端分别为不同的 db index 维护一个 dict 这个 dict 称为 key space 键空间 。每一个 redis client 只能属于一个 db index ,在 redis 服务端会维护每一个链接的 redisClient

typedef struct redisClient { uint64_t id; int fd; redisDb *db; } redisClient;

在服务端每一个 redis 客户端都会有一个指向 redisDb 的指针。

typedef struct redisDb { dict *dict; dict *expires; dict *blocking_keys; dict *ready_keys; dict *watched_keys; struct evictionPoolEntry *eviction_pool; int id; long long avg_ttl; } redisDb;

key space 键空间就是这里的 redisDb->dictredisDb->expires 是维护所有键空间的每一个 key 的过期时间。

键 过期时间、生存时间

对于一个 key 我们可以设置它多少秒、毫秒之后过期,也可以设置它在某个具体的时间点过期,后者是一个时间戳。

EXPIRE 命令可以设置某个 key 多少秒之后过期
PEXPIRE 命令可以设置某个 key 多少毫秒之后过期

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpzjfw.html