Redis 源码分析字符串对象(z_string)

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: Redis 源码分析字符串对象(z_string)

字符串对象



字符对象的三种编码可以是 int, raw 或者 embstr, 三种情况我分别来说明一下:


  1. 如果一个字符串对象保存的整数值,并且这个整数值可以用 long 类型来表示,那么这个字符串会将整数值保存在字符串对象结构的 ptr 属性值里面,并且字符串对象的编码设置为 int.


  1. 如果一个字符串保存的是一个字符值,并且这个字符串长度小于等于 32 字节,那么这个字符串讲使用 embstr 编码的形式来保存这个字符串值。


  1. embstr 编码是专门用来保存短字符串的一种优化编码方式这种编码和 raw 编码一样,都是使用 redisObject 中的 sdshdr 结构来表示字符串对象,但是 raw 会调用两次内存比分配函数来创建 redisObject 结构和 sdshdr 结构,而 embstr 编码则通过调用一次内存分配一款连续空间,空间依次包含 redisObject 和 sdshdr 两个结构


int 编码的字符串如下:


image.png


embstr 编码的字符如下:


image.png


raw 编码的字符如下:


image.png


下面是 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 位,且是数字字符串类型。


image.png


其实这里就是可以涵盖了所有 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 创建过程如下:


image.png


redisObject 对象创建:


image.png


使用 append 命令修改字符串会改变字符串底层的类型:


image.png


通过这个实验,证明了两点:


  1. 浮点数 redis 用 embstr 存储。


  1. embstr 如果有 append 操作那么会转换为 raw 存储。


底层代码(embstr 转换 raw):


image.png


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. 如下图所示:


image.png


字符串使用场景



  • 缓存功能:mysql 做数据持久化存储,redis 做少量热数据缓存;


  • 计数器:如点赞次数,视频播放次数;


  • 限流:见基于 redis 的分布式限流;


使用命令和其他实践可以翻阅我以前的文章,本文不在赘述:



常用操作



image.png


参考文档



  • 《Redis 设计与实现》黄健宏


相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
相关文章
|
10天前
|
Python
Python中的f-string:更简洁的字符串格式化
Python中的f-string:更简洁的字符串格式化
151 92
|
2月前
|
自然语言处理 Java Apache
在Java中将String字符串转换为算术表达式并计算
具体的实现逻辑需要填写在 `Tokenizer`和 `ExpressionParser`类中,这里只提供了大概的框架。在实际实现时 `Tokenizer`应该提供分词逻辑,把输入的字符串转换成Token序列。而 `ExpressionParser`应当通过递归下降的方式依次解析
163 14
|
4月前
|
消息中间件 缓存 NoSQL
基于Spring Data Redis与RabbitMQ实现字符串缓存和计数功能(数据同步)
总的来说,借助Spring Data Redis和RabbitMQ,我们可以轻松实现字符串缓存和计数的功能。而关键的部分不过是一些"厨房的套路",一旦你掌握了这些套路,那么你就像厨师一样可以准备出一道道饕餮美食了。通过这种方式促进数据处理效率无疑将大大提高我们的生产力。
161 32
|
4月前
|
存储 NoSQL 算法
Redis设计与实现——数据结构与对象
Redis 是一个高性能的键值存储系统,其数据结构设计精妙且高效。主要包括以下几种核心数据结构:SDS、链表、字典、跳跃表、整数集合、压缩列表。此外,Redis 对象通过类型和编码方式动态转换,优化内存使用,并支持引用计数、共享对象和淘汰策略(如 LRU/LFU)。这些特性共同确保 Redis 在性能与灵活性之间的平衡。
|
6月前
|
数据处理
鸿蒙开发:ArkTs字符串string
字符串类型是开发中非常重要的一个数据类型,除了上述的方法概述之外,还有String对象,正则等其他的用处,我们放到以后得篇章中讲述。
271 19
|
6月前
|
Java 程序员
课时16:String字符串
课时16介绍了Java中的String字符串。在Java中,字符串使用`String`类表示,并用双引号定义。例如:`String str = &quot;Hello world!&quot;;`。字符串支持使用“+”进行连接操作,如`str += &quot;world&quot;;`。需要注意的是,当“+”用于字符串与其他数据类型时,其他类型会先转换为字符串再进行连接。此外,字符串中可以使用转义字符(如`\t`、`\n`)进行特殊字符的处理。掌握这些基本概念对Java编程至关重要。
|
机器学习/深度学习 JSON NoSQL
牛逼!Redis 的字符串是这样实现的…
之前本人在找工作面试时在Redis相关问题上可栽了跟头。
牛逼!Redis 的字符串是这样实现的…