工作中常会用到redis做存储型的kv存储,如何合理地估算所占空间呢?
首先要看看redis的存储原理,redis存储时候kv都会被处理成字符串或者说字符数组,redis实现了一种方便进行append,rehash等操作的sds类型。
在sds.h
可以看到typedef char *sds;
这样的定义。光这样和普通的字符串看上去毫无区别,事实上sds有一个header的概念,同样在sds.h里面
/* 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[];
};
注意这段注释,sdshdr5不用,但是size小于2^5-1的字符串都会使用SDS_TYPE_5。
在各个header的定义中最后有一个char buf[]。这是柔性数组(flexible array member),起到一个标记的作用,表示在flags字段后面就是一个字符数组,它并不占用内存空间。
看到这里SDS_TYPE5的头只占1个字节,而SDS_TYPE_8则占3个字节
在sds之上还封装了robj,定义如下
typedef struct redisObject {
unsigned type:4;
unsigned encoding:4;
unsigned lru:REDIS_LRU_BITS; /* lru time (relative to server.lruclock) */
int refcount;
void *ptr;
} robj;
而为了能够快速存储读取,所有的kv保存在dict结构里,而每个dictEntry是这种形式:
typedef struct dictEntry {
void *key;
union {
void *val;
uint64_t u64;
int64_t s64;
double d;
} v;
struct dictEntry *next;
} dictEntry;
key是一个”aaa”的sds指针,v是一个指向包含”bbb”的robj的指针
也就是一个dictEntry占24字节,一个key作为sds占sdsheader+本身内容+结束符(1),而一个v作为一个robj占sds和robj的空间,约12字节。
举例来说,我有一个表存用户登录的token
占用多少空间呢?
假设用户id 19位,token50位,key可以用SDS_TYPE_5,1+19+1=21字节,value要用SDS_TYPE_8了,并包装成robj,即3+12+50+1=66,一个dictEntry24字节
总计下来111个字节,这样单条记录所占空间大致计算出来,估一下数据量就能计算了