字符串对象
字符对象的三种编码可以是 int, raw 或者 embstr, 三种情况我分别来说明一下:
- 如果一个字符串对象保存的整数值,并且这个整数值可以用 long 类型来表示,那么这个字符串会将整数值保存在字符串对象结构的 ptr 属性值里面,并且字符串对象的编码设置为 int.
- 如果一个字符串保存的是一个字符值,并且这个字符串长度小于等于 32 字节,那么这个字符串讲使用 embstr 编码的形式来保存这个字符串值。
- embstr 编码是专门用来保存短字符串的一种优化编码方式这种编码和 raw 编码一样,都是使用 redisObject 中的 sdshdr 结构来表示字符串对象,但是 raw 会调用两次内存比分配函数来创建 redisObject 结构和 sdshdr 结构,而 embstr 编码则通过调用一次内存分配一款连续空间,空间依次包含 redisObject 和 sdshdr 两个结构
int 编码的字符串如下:
embstr 编码的字符如下:
raw 编码的字符如下:
下面是 redisObject 所有的类型:
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_ZIPLIST: return "ziplist"; 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"; } }
我们本处主要是一起来分析 OBJ_ENCODING_INT、OBJ_ENCODING_RAW、OBJ_ENCODING_EMBSTR 三种情况
int (OBJ_ENCODING_INT)
长度小于 20 确保在一个长整型的范围内,2^64次方恰好占用 20 位,且是数字字符串类型。
其实这里就是可以涵盖了所有 long 类型的长整型。
embstr (OBJ_ENCODING_EMBSTR)
长度小于等于 44 个字符(包括浮点数)44 个字节是只要为了适应 jemalloc 分配的 64k arena
/* Create a string object with EMBSTR encoding if it is smaller than * OBJ_ENCODING_EMBSTR_SIZE_LIMIT, otherwise the RAW encoding is * used. * * The current limit of 44 is chosen so that the biggest string object * we allocate as EMBSTR will still fit into the 64 byte arena of jemalloc. */ #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); else return createRawStringObject(ptr,len); }
这块其实可以就是理解为一个短的字符串, 且非 20 位内的整形。
raw (OBJ_ENCODING_RAW)
长度大于 44 字节的字符串(包括浮点数), 简单总结就是其他的字符串,或者字符串浮点数。下面会做详细的对比。
raw 和 embstr 对比
raw 和 embstr 对比如下:
类型 | 特点 | 优点 | 缺点 |
embstr | 1. 只分分配一次内存空间,因此 robj 和 sds 是连续的;2. 只读;3. Embstr 字符串需要修改时,会转换为 raw, 之后一直是 raw | 1. 创建和删除只需要一次;2.查找速度快 | 1. 重新分配涉及到 robj 和 sds 整个对象,因此 embstr 是只读的 |
raw | 1. robj 和 sds 非连续;2. 可以修改 |
embstr 创建过程如下:
redisObject 对象创建:
使用 append 命令修改字符串会改变字符串底层的类型:
通过这个实验,证明了两点:
- 浮点数 redis 用 embstr 存储。
- embstr 如果有 append 操作那么会转换为 raw 存储。
底层代码(embstr 转换 raw):
Redis 字符串限定
Redis 字符串约定长度不能大于 512m, 通常开发过程中字符串很难达到这么长,除非是你想存 base64 之类的, 默认值已经很大。
// 字符串最大长度限制 static int checkStringLength(client *c, long long size) { if (!(c->flags & CLIENT_MASTER) && size > server.proto_max_bulk_len) { addReplyError(c,"string exceeds maximum allowed size (proto-max-bulk-len)"); return C_ERR; } return C_OK; }
这长度我们可以在 redis.config 中配置, 配置 key proto-max-bulk-len
默认值:512mb. 如下图所示:
字符串使用场景
- 缓存功能:mysql 做数据持久化存储,redis 做少量热数据缓存;
- 计数器:如点赞次数,视频播放次数;
- 限流:见基于 redis 的分布式限流;
使用命令和其他实践可以翻阅我以前的文章,本文不在赘述:
常用操作
参考文档
- 《Redis 设计与实现》黄健宏