IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 大数据 -> Redis之字符串 -> 正文阅读

[大数据]Redis之字符串

字符串

C语言中的字符串标准形式是以NULL(十六进制的 0x\0) 作为结束符,如果要获取字符串的长度需要使用strlen标准库函数,该函数算法复杂度是O(n),需要对整个字符串进行遍历,Redis无法承受这么慢的速度,

因此Redis中的字符串在内存中是以字节数组的形式存在,叫做SDS (Simple Dynamic String),是一个带着长度信息的字节数组。

struct SDS<T> {
    T capacity; // 数组容量
    T len; // 数组长度
    byte flags; // 特殊标志位
    byte[] content; // 数组内容
}

SDS的结构很像Java中的ArrayList,content数组中存储着真实的内容,但是这个数组一般会比内容的实际长度要多一些冗余空间,

capacity表示数组的长度,len表示真实的字符串长度,因为字符串在redis中是可修改的,需要支持append操作,

如果数组没有冗余空间,append的时候就必须重新开辟一个新数组,然后将老数组中的内容复制过去,这是一个比较大的开销。

image.png

SDS中使用了泛型T,而不是直接使用int,因为当字符串比较短的时候,len和capacity可以通过byte和short表示,以此达到节省空间的作用,所以不同长度的字符串会用不同的结构体表示。

追加字符串的代码:


sds sdscatlen(sds s, const void *t, size_t len) {
    // 原字符串长度
    size_t curlen = sdslen(s);
    // 按需调整空间,如果capacity不够容纳追加的内容,就会重新分配字节数组,并将原字符串中的内容复制到新数组中
    s = sdsMakeRoomFor(s,len);
    // 内存不足
    if (s == NULL) return NULL;
    // 追加目标字符串的内容到字节数组中
    memcpy(s+curlen, t, len);
    // 设置追加后的长度值
    sdssetlen(s, curlen+len);
    // 让字符串以\0结尾
    s[curlen+len] = '\0';
    return s;
}

Redis规定字符串长度最大不能超过512MB,创建字符串时 len 和 capacity 是一样长的,不会分配冗余空间,因为大多数情况下,不会用到字符串的append。

embstr 与 raw

Redis字符串有两种存储方式,长度很短时,使用 embstr 形式存储,字节长度超过44时,使用raw形式存储。

44个字符:

127.0.0.1:6379> set hello abcdefghijklmnopqrstuvwxyz012345678912345678

OK

127.0.0.1:6379> debug object hello

Value at:0xffff9f531e40 refcount:1 encoding:embstr serializedlength:45 lru:14436129 lru_seconds_idle:5

45个字符:

127.0.0.1:6379> set hello abcdefghijklmnopqrstuvwxyz0123456789123456789

OK

127.0.0.1:6379> debug object hello

Value at:0xffff9f18d2e0 refcount:1 encoding:raw serializedlength:46 lru:14436171 lru_seconds_idle:2

上面两端内容中,encoding表示存储形式,可以看到44个字节是embstr,45个就是raw了。

对象头

Redis每个对象都有一个头结构:

struct RedisObject { 
    int4 type ;         // 4bits
    int4 encoding ;     // 4bits
    int24 lru ;         // 24bits
    int32 refcount ;    // 4bytes 
    void *ptr;          // 8bytes 
} robj ;

每个对象都有类型type、存储形式encoding,以及对象的lru信息,

还有refcount,代表对象的引用计数,为0时对象被销毁回收,ptr指针指向对象内容(body)的具体存储位置,

对象头将占用16字节的存储空间。

在字符串内容较小时,SDS结构体的内存占用空间情况如下:

struct SDS {
    int8 capacity; //1byte
    int8 len; //1byte
    int8 flags; //1byte
    byte[] content; //内容数组,长度为capacity
}

也就是说SDS对象头结构的16字节再加上SDS结构中的3个字节,意味着一个字符串至少占用19个字节,

embstr在内存中存储时,位置与对象头在一起,使用malloc方法一次性分配对象头+对象的空间,而raw不是,需要进行两次malloc分配,对象头和内容在内存上不是连续的。

image.png

因为内存分配器jemalloc、tcmalloc分配内存大小的单位都是2/4/8/16/32/64字节,为了容纳一个embstr的字符串,jemalloc一般会分配32个字节,如果字符串更长一些就是64个字节,

如果字符串总体超过64个字节的时候,Redis认为其不适合使用embstr形式存储,该使用raw了。

因为Redis对象头就占用了16个字节,再加上3个属性的总和3字节,还剩 64 - 19 = 45 个字节,字符串需要用NULL结尾,NULL又占了一个字节,所有最终只剩下44个字节用于存字符串的内容了,这就是上面看到的超过44个字节encoding就变成了raw的原因。

image.png

扩容策略

在字符串小于1MB前,拓容空间将直接翻倍,超过后,为了避免浪费,每次拓容仅追加1MB的冗余空间。

觉得有帮助请帮忙点一下,谢谢:https://developer.aliyun.com/article/852670

  大数据 最新文章
实现Kafka至少消费一次
亚马逊云科技:还在苦于ETL?Zero ETL的时代
初探MapReduce
【SpringBoot框架篇】32.基于注解+redis实现
Elasticsearch:如何减少 Elasticsearch 集
Go redis操作
Redis面试题
专题五 Redis高并发场景
基于GBase8s和Calcite的多数据源查询
Redis——底层数据结构原理
上一篇文章      下一篇文章      查看所有文章
加:2022-01-12 00:04:28  更:2022-01-12 00:05:59 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 14:42:22-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码