Redis中String类型数据扩容原理分析

大家好,我是 V 哥。在 Java 中,我们有动态数组ArrayList,当插入新元素空间不足时,会进行扩容,好奇 Redis 中的 String 类型,C 语言又是怎样的实现策略,带着疑问,咱们来了解一下。

最适合Java 新手入门的教程:http://t.csdnimg.cn/3auFZ

在Redis中,String类型数据的扩容主要涉及到SDS(Simple Dynamic String)的内存分配机制。SDS是Redis用来存储字符串的数据结构,它在C语言的字符数组基础上进行了封装,以支持动态扩展长度的功能。

当对一个String类型的值进行修改操作(如增加内容)时,如果现有的空间不足以容纳新的数据,Redis就会进行扩容。

在Redis中,sdsMakeRoomFor 函数是用来扩展SDS字符串的缓冲区的。这个函数的目的是确保SDS字符串有足够的空间来追加新的数据。以下是sdsMakeRoomFor函数的实现逻辑:

  1. 检查现有空间:首先,函数会检查SDS字符串的现有空闲空间(由sdshdr结构的free属性记录)是否足够容纳额外的数据。如果足够,函数直接返回,不需要进行扩容。

  2. 计算新长度:如果现有空间不足,函数会计算出需要的新长度。这通常是现有长度加上要添加的数据的长度。

  3. 确定扩容策略:Redis采用一种预分配策略来优化内存使用和提高性能。如果新长度小于SDS_MAX_PREALLOC(通常为1MB),那么Redis会将新长度扩大两倍,以减少频繁的内存分配操作。如果新长度大于或等于SDS_MAX_PREALLOC,则会一次性分配足够的空间,避免每次扩容都只增加少量空间,导致性能下降。

  4. 内存分配:根据新的扩容策略,Redis会使用s_realloc_usable(如果类型未变)或s_malloc_usable(如果类型变化,需要移动数据)来分配新的内存空间。

  5. 更新SDS头部:在新的内存空间分配完成后,Redis会更新SDS的头部信息,包括长度、空闲空间等,并复制原有数据到新的内存位置。

  6. 处理类型变化:如果扩容导致SDS的类型发生变化(例如,从SDS_TYPE_8变为SDS_TYPE_16),Redis还需要更新SDS的编码类型,并可能需要移动数据到新的内存位置。

在Redis 7.0版本中,SDS的内存布局有所变化,不再使用free属性,而是使用alloc属性来记录分配的空间总长度,len属性记录已使用的字符串长度。因此,alloclen的差值就代表了空闲空间的大小。这种设计使得SDS在内存布局上更加紧凑,取消了编译器的对齐,以节省内存空间。

sdsMakeRoomFor函数的具体实现如下:

sds _sdsMakeRoomFor(sds s, size_t addlen, int greedy) {
    void *sh, *newsh;
    size_t avail = sdsavail(s);
    size_t len, newlen, reqlen;
    char type, oldtype = s[-1] & SDS_TYPE_MASK;
    int hdrlen;
    size_t usable;

    /* 如果有足够的剩余空间,直接返回 */
    if (avail >= addlen) return s;

    len = sdslen(s);
    sh = (char*)s-sdsHdrSize(oldtype);

    reqlen = newlen = (len+addlen);
    assert(newlen > len);   /* Catch size_t overflow */
    //判断是否为greedy模式(为1表示greedy模式)
    //是将新长度翻倍还是额外增加`SDS_MAX_PREALLOC`
    if (greedy == 1) {
        if (newlen < SDS_MAX_PREALLOC)
            newlen *= 2;
        else
            newlen += SDS_MAX_PREALLOC;
    }
    type = sdsReqType(newlen);

    /* 如果类型是SDS_TYPE_5,但是用户正在追加字符串,那么使用SDS_TYPE_8 */
    if (type == SDS_TYPE_5) type = SDS_TYPE_8;

    hdrlen = sdsHdrSize(type);
    assert(hdrlen + newlen + 1 > reqlen);  /* Catch size_t overflow */

    if (oldtype == type) {
        newsh = s_realloc_usable(sh, hdrlen+newlen+1, &usable);
        if (newsh == NULL) return NULL;
        s = (char*)newsh+hdrlen;
    } else {
        newsh = s_malloc(hdrlen+newlen+1);
        if (newsh == NULL) return NULL;
        memcpy((char*)newsh+hdrlen, s, len+1);
        s_free(sh);
        s = (char*)newsh+hdrlen;
        s[-1] = type;
       sdssetlen(s, len);
    }
    usable = usable-hdrlen-1;
    if (usable > sdsTypeMaxSize(type))
        usable = sdsTypeMaxSize(type);
    sdssetalloc(s, usable);
    return s;
}

这个函数首先检查是否有足够的空间来追加数据,如果没有,则根据当前的字符串长度和需要追加的数据长度来计算新的总长度。如果启用了greedy模式,它会根据是否超过SDS_MAX_PREALLOC来决定是将新长度翻倍还是额外增加SDS_MAX_PREALLOC。然后,它会根据新的总长度来确定新的SDS类型,并分配新的内存空间。如果SDS的类型没有变化,它会使用s_realloc_usable来扩展现有的内存空间;如果类型变化了,它会使用s_malloc来分配新的内存空间,并将旧数据复制到新位置。最后,它会更新SDS头部信息,包括长度和分配的空间大小。

注意一下哈,在Redis 7.0版本之前的SDS实现和7.0版本之后的实现有哪些变化呢?

在Redis 7.0版本之前,SDS(Simple Dynamic String)的实现主要包括一个头部结构struct sdshdr,其中包含了记录已使用空间的len字段,记录未使用空间的free字段,以及一个字符数组buf用于存储字符串。这种设计允许SDS在O(1)时间复杂度内获取字符串长度,并且通过维护free字段来减少内存重分配的次数,提高性能。

然而,在Redis 7.0版本中,SDS的实现发生了一些变化。首先,引入了一个新的字段flags,它是一个单字节的字段,用于存储SDS的类型信息。这使得SDS的结构更加紧凑,取消了编译器的对齐,节省了内存空间。其次,free字段被移除,取而代之的是alloc字段,它表示SDS的总分配空间。因此,alloclen的差值就代表了空闲空间的大小。这种设计使得SDS在内存布局上更加紧凑,同时保持了动态扩展长度的功能。

在Redis 7.0版本中,SDS的类型被定义为以下几种:

  • SDS_TYPE_5:长度小于32的字符串,使用flags的5个最高位存储长度。
  • SDS_TYPE_8:长度在1到255之间的字符串,使用1个字节存储长度。
  • SDS_TYPE_16:长度在256到65535之间的字符串,使用2个字节存储长度。
  • SDS_TYPE_32:长度在65536到4294967295之间的字符串,使用4个字节存储长度。
  • SDS_TYPE_64:长度大于4294967295的字符串,使用8个字节存储长度。

这种设计允许SDS根据字符串的实际长度选择最合适的头部类型,从而节省内存。例如,对于短字符串,可以使用SDS_TYPE_5类型的头部,它不包含单独的长度和分配字段,而是将这些信息存储在flags字段中。

此外,Redis 7.0版本中的SDS实现还包括了一些其他的优化,例如,使用__attribute__ ((__packed__))来确保结构体在内存中紧凑排列,以及通过s_mallocs_realloc等函数来管理内存分配,确保内存对齐的同时,也提供了灵活的内存管理。

咱们很显然可以看出,Redis 7.0版本对SDS的实现进行了优化,使其更加紧凑和高效,同时也保持了SDS的动态扩展和二进制安全的特性。这些改进有助于提高Redis在处理大量数据时的性能和资源利用率。关注威哥爱编程,学习代码乐无边
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/899792.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python酷库之旅-第三方库Pandas(167)

目录 一、用法精讲 766、pandas.Interval.open_left属性 766-1、语法 766-2、参数 766-3、功能 766-4、返回值 766-5、说明 766-6、用法 766-6-1、数据准备 766-6-2、代码示例 766-6-3、结果输出 767、pandas.Interval.open_right属性 767-1、语法 767-2、参数 …

[LeetCode] 78. 子集

题目描述&#xff1b; 给你一个整数数组 nums &#xff0c;数组中的元素 互不相同 。返回该数组所有可能的子集&#xff08;幂集&#xff09;。 解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。 示例 1&#xff1a; 输入&#xff1a;nums [1,2,3] 输出&#xff1…

Windows通过netsh控制安全中心防火墙和网络保护策略

Windows通过netsh控制安全中心防火墙和网络保护策略 1. 工具简介 【1】. Windows安全中心 【2】. netsh工具 netsh(Network Shell) 是一个Windows系统本身提供的功能强大的网络配置命令行工具。 2. 开启/关闭防火墙策略 在设置端口&#xff08;禁用/启用&#xff09;前&am…

传输层协议UDP详解

目录 一. 知识准备 1.1 传输层 1.2 重识端口号 二. UDP协议 三. UDP协议特点 一. 知识准备 1.1 传输层 前面已经讲过&#xff0c;HTTP协议是应用层协议&#xff0c;在此之前&#xff0c;我们短暂的认为HTTP是直接通过应用层与外界通信的。但是我们要知道&…

DOTween动画插件超详解(保姆级巨细)

文章目录 一、前言二、DOTween简介与安装&#xff08;一&#xff09;什么是DOTween&#xff1f;&#xff08;二&#xff09;下载安装 三、DOTween 的使用&#xff08;基础&#xff09;&#xff08;一&#xff09;使用前注意事项1. 引入命名空间2. 进行初始化3. 清除遗留4. 设置…

基于Java的电商书城系统源码带本地搭建教程

技术框架&#xff1a;jQuery MySQL5.7 mybatis jsp shiro 运行环境&#xff1a;jdk8 IntelliJ IDEA maven3 宝塔面板 系统功能介绍 该系统分为前台展示和后台管理两大模块&#xff0c;前台主要是为消费者服务。该子系统实现了注册&#xff0c;登录&#xff0c; 以及…

asp.net core mvc发布时输出视图文件Views

var builder WebApplication.CreateBuilder(args); builder.Services.AddRazorPages();builder.Services.AddControllersWithViews(ops > {//全局异常过滤器&#xff0c;注册ops.Filters.Add<ExceptionFilter>(); })// Views视图文件输出到发布目录&#xff0c;视图文…

使用 VSCode 通过 Remote-SSH 连接远程服务器详细教程

使用 VSCode 通过 Remote-SSH 连接远程服务器详细教程 在日常开发中&#xff0c;许多开发者需要远程连接服务器进行代码编辑和调试。Visual Studio Code&#xff08;VSCode&#xff09;提供了一个非常强大的扩展——Remote-SSH&#xff0c;它允许我们通过 SSH 协议直接连接远程…

背包九讲——完全背包问题

目录 完全背包问题 问题定义 动态规划解法 状态转移方程 初始化 遍历顺序 三种解法&#xff1a; 朴素版——枚举k 进阶版——dp正推&#xff08;一维滚动数组&#xff09; 背包问题第三讲——完全背包问题 背包问题是一类经典的组合优化问题&#xff0c;通常涉及在限定…

kafka 的高可用机制是什么?

大家好&#xff0c;我是锋哥。今天分享关于【kafka 的高可用机制是什么&#xff1f;】面试题&#xff1f;希望对大家有帮助&#xff1b; kafka 的高可用机制是什么&#xff1f; 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 Apache Kafka 是一个分布式消息系统&am…

《性能之巅:洞悉系统、企业与云计算》读书笔记-Part 1

本文是读书笔记第一部分&#xff0c;包括原书第一、二章。 绪论 性能是一门令人激动的&#xff0c;富于变化同时又充满挑战的学科。 系统性能 单台服务器上的通用系统软件栈 人员 系统性能是一项需要多类人员参与的工程。 事情 关于性能的理想执行顺序排列如下&#x…

8个方法教会你提高企业培训效率

培训成本是企业中的一个复杂问题。它完全取决于课程内容、培训方法以及成本效益。在计算培训费用时&#xff0c;公司会面临许多关于包括哪些内容、如何进行以及假设情景的问题。 企业员工培训的每个方面都会产生自己的成本。例如&#xff1a; 地点&#xff1a;我们专门找个培训…

【重拾算法第一天】质数约数欧拉筛 埃氏筛GCD

1.素数 素数&#xff08;Prime Number&#xff09;是指大于1的自然数&#xff0c;只有两个正因数&#xff1a;1和它自身。换句话说&#xff0c;素数是不能被其他自然数整除的数。 1.1小素数的判定 判定一个数是否为素数 &#xff0c;当N ≤ 时&#xff0c; 用试除法 &#…

Redis 命令集 (超级详细)

目录 Redis 常用命令集 string类型 hash类型 list类型 set类型 zset类型 bitmap 类型 geo 类型 GEOADD (添加地理位置的坐标) GEOPOS (获取地理位置的坐标) GEODIST (计算两个位置之间的距离) GEOHASH (返回一个或多个位置对象的 geohash 值) GEORADIUS (根据用户…

DAF-Net:一种基于域自适应的双分支特征分解融合网络用于红外和可见光图像融合

题目&#xff1a;DAF-Net: A Dual-Branch Feature Decomposition Fusion Network with Domain Adaptive for Infrared and Visible Image Fusion 作者&#xff1a;JianXu发表时间&#xff1a;2024年9月 面临的问题&#xff1a;红外图像擅长捕捉热辐射&#xff0c;特别是在低…

国家能源集团携手海康威视研发攻克融合光谱煤质快检技术

10月24日&#xff0c;在国家能源集团准能集团黑岱沟露天煤矿&#xff0c;安装于准能选煤厂785商品煤胶带机中部的煤质快检核心设备&#xff0c;正在对当天装车外运的商品煤煤质进行实时检测。仅两分钟后&#xff0c;涵盖发热量、水分、灰分、硫分等多项指标的数据信息已传输到到…

前端方案:播放的视频加水印或者文字最佳实践

前言&#xff1a; 很多时候&#xff0c;视频的转码工作在后端&#xff0c;我们前端是拿到可以播放的链接进行播放即可。但是总是会出现一些定制化的需求&#xff0c;比如在视频的某个区域贴上水印、标识或者文字。这个时候大部分是由前端来操作的。 直接去修改播放器里的东西…

c语言指针详解2

c语言指针详解2 1.数组名理解 数组名其实是地址&#xff0c;是数组首元素的地址&#xff08;详解1有提及&#xff09; 我们可以根据打印来确认 我们发现数组名和数组⾸元素的地址打印出的结果⼀模⼀样&#xff0c;数组名就是数组⾸元素(第⼀个元素)的地址。 但是上述结论有…

DataX简介及使用

目录 一、DataX离线同步工具DataX3.0介绍 1.1、 DataX 3.0概览 1.2、特征 1.3、DataX3.0框架设计 1.4、支持的数据元 1.5、DataX3.0核心架构 1.6、DataX 3.0六大核心优势 1.6.1、可靠的数据质量监控 1.6.2、丰富的数据转换功能 1.6.3、精准的速度控制 1.6.4、强劲的…

轻松清理 PC 微信文件,释放存储空间

软件介绍 微信在我们的日常生活中使用频率极高&#xff0c;但随着时间的推移&#xff0c;它占用的存储空间也越来越大。以一个使用了两年时间的微信为例&#xff0c;它可能会占用多达几十G的存储空间。其中大部分都是与自己无关的各大群聊中的文件、视频、图片等内容&#xff…