当前位置: 首页 > news >正文

Redis之字符串类型深入之SDS底层结构

作为一名程序员不可能不知道redis

知道redis不可能不知道redis的字符串

如果你真的熟悉redis不能不知道sds,

我们探究一下redis字符串的底层结构

sds翻译过来就是动态扩容(Simple Dynamic String)、先看一下最早版本redis的sds结构体

struct sdshdr{int len; //记录数组中已经使用的字节的数量,等于所保存的字符串长度int free; //记录buf数组中未使用字节的数量char buf[]; //字节数组,用于保存字符串
};

最初版本的sds结构体的属性包含了free、len、buf,
众所周知c语言获取字符串长度的函数是strlen(char),这个函数的时间复杂度是O(N),本质其实就是一个循环,redis在结构体里定义了字符串长度属性,可以直接获取len,时间复杂度是O(1),大大提高了效率!!
这是我网上找的sds最早的原型代码、新版本为了更好的控制内存使用了多种结构体不同的类型来记录数据,并用一个字节的三个位来表示结构体类型,如果大量使用短小字符串的话,节省下来的内存也是比较可观的

/* SDSLib 2.0 -- A C dynamic strings library** Copyright (c) 2006-Present, Redis Ltd.* All rights reserved.** Licensed under your choice of the Redis Source Available License 2.0* (RSALv2) or the Server Side Public License v1 (SSPLv1).*/#ifndef __SDS_H
#define __SDS_H#define SDS_MAX_PREALLOC (1024*1024)
extern const char *SDS_NOINIT;#include <sys/types.h>
#include <stdarg.h>
#include <stdint.h>typedef char *sds;/* 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[];
};#define SDS_TYPE_5  0
#define SDS_TYPE_8  1
#define SDS_TYPE_16 2
#define SDS_TYPE_32 3
#define SDS_TYPE_64 4
#define SDS_TYPE_MASK 7
#define SDS_TYPE_BITS 3
#define SDS_HDR_VAR(T,s) struct sdshdr##T *sh = (void*)((s)-(sizeof(struct sdshdr##T)));
#define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T))))
#define SDS_TYPE_5_LEN(f) ((f)>>SDS_TYPE_BITS)static inline size_t sdslen(const sds s) {unsigned char flags = s[-1];switch(flags&SDS_TYPE_MASK) {case SDS_TYPE_5:return SDS_TYPE_5_LEN(flags);case SDS_TYPE_8:return SDS_HDR(8,s)->len;case SDS_TYPE_16:return SDS_HDR(16,s)->len;case SDS_TYPE_32:return SDS_HDR(32,s)->len;case SDS_TYPE_64:return SDS_HDR(64,s)->len;}return 0;
}static inline size_t sdsavail(const sds s) {unsigned char flags = s[-1];switch(flags&SDS_TYPE_MASK) {case SDS_TYPE_5: {return 0;}case SDS_TYPE_8: {SDS_HDR_VAR(8,s);return sh->alloc - sh->len;}case SDS_TYPE_16: {SDS_HDR_VAR(16,s);return sh->alloc - sh->len;}case SDS_TYPE_32: {SDS_HDR_VAR(32,s);return sh->alloc - sh->len;}case SDS_TYPE_64: {SDS_HDR_VAR(64,s);return sh->alloc - sh->len;}}return 0;
}static inline void sdssetlen(sds s, size_t newlen) {unsigned char flags = s[-1];switch(flags&SDS_TYPE_MASK) {case SDS_TYPE_5:{unsigned char *fp = ((unsigned char*)s)-1;*fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS);}break;case SDS_TYPE_8:SDS_HDR(8,s)->len = newlen;break;case SDS_TYPE_16:SDS_HDR(16,s)->len = newlen;break;case SDS_TYPE_32:SDS_HDR(32,s)->len = newlen;break;case SDS_TYPE_64:SDS_HDR(64,s)->len = newlen;break;}
}static inline void sdsinclen(sds s, size_t inc) {unsigned char flags = s[-1];switch(flags&SDS_TYPE_MASK) {case SDS_TYPE_5:{unsigned char *fp = ((unsigned char*)s)-1;unsigned char newlen = SDS_TYPE_5_LEN(flags)+inc;*fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS);}break;case SDS_TYPE_8:SDS_HDR(8,s)->len += inc;break;case SDS_TYPE_16:SDS_HDR(16,s)->len += inc;break;case SDS_TYPE_32:SDS_HDR(32,s)->len += inc;break;case SDS_TYPE_64:SDS_HDR(64,s)->len += inc;break;}
}/* sdsalloc() = sdsavail() + sdslen() */
static inline size_t sdsalloc(const sds s) {unsigned char flags = s[-1];switch(flags&SDS_TYPE_MASK) {case SDS_TYPE_5:return SDS_TYPE_5_LEN(flags);case SDS_TYPE_8:return SDS_HDR(8,s)->alloc;case SDS_TYPE_16:return SDS_HDR(16,s)->alloc;case SDS_TYPE_32:return SDS_HDR(32,s)->alloc;case SDS_TYPE_64:return SDS_HDR(64,s)->alloc;}return 0;
}static inline void sdssetalloc(sds s, size_t newlen) {unsigned char flags = s[-1];switch(flags&SDS_TYPE_MASK) {case SDS_TYPE_5:/* Nothing to do, this type has no total allocation info. */break;case SDS_TYPE_8:SDS_HDR(8,s)->alloc = newlen;break;case SDS_TYPE_16:SDS_HDR(16,s)->alloc = newlen;break;case SDS_TYPE_32:SDS_HDR(32,s)->alloc = newlen;break;case SDS_TYPE_64:SDS_HDR(64,s)->alloc = newlen;break;}
}sds sdsnewlen(const void *init, size_t initlen);
sds sdstrynewlen(const void *init, size_t initlen);
sds sdsnew(const char *init);
sds sdsempty(void);
sds sdsdup(const sds s);
void sdsfree(sds s);
sds sdsgrowzero(sds s, size_t len);
sds sdscatlen(sds s, const void *t, size_t len);
sds sdscat(sds s, const char *t);
sds sdscatsds(sds s, const sds t);
sds sdscpylen(sds s, const char *t, size_t len);
sds sdscpy(sds s, const char *t);sds sdscatvprintf(sds s, const char *fmt, va_list ap);
#ifdef __GNUC__
sds sdscatprintf(sds s, const char *fmt, ...)__attribute__((format(printf, 2, 3)));
#else
sds sdscatprintf(sds s, const char *fmt, ...);
#endifsds sdscatfmt(sds s, char const *fmt, ...);
sds sdstrim(sds s, const char *cset);
void sdssubstr(sds s, size_t start, size_t len);
void sdsrange(sds s, ssize_t start, ssize_t end);
void sdsupdatelen(sds s);
void sdsclear(sds s);
int sdscmp(const sds s1, const sds s2);
sds *sdssplitlen(const char *s, ssize_t len, const char *sep, int seplen, int *count);
void sdsfreesplitres(sds *tokens, int count);
void sdstolower(sds s);
void sdstoupper(sds s);
sds sdsfromlonglong(long long value);
sds sdscatrepr(sds s, const char *p, size_t len);
sds *sdssplitargs(const char *line, int *argc);
sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen);
sds sdsjoin(char **argv, int argc, char *sep);
sds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen);
int sdsneedsrepr(const sds s);/* Callback for sdstemplate. The function gets called by sdstemplate* every time a variable needs to be expanded. The variable name is* provided as variable, and the callback is expected to return a* substitution value. Returning a NULL indicates an error.*/
typedef sds (*sdstemplate_callback_t)(const sds variable, void *arg);
sds sdstemplate(const char *template, sdstemplate_callback_t cb_func, void *cb_arg);/* Low level functions exposed to the user API */
sds sdsMakeRoomFor(sds s, size_t addlen);
sds sdsMakeRoomForNonGreedy(sds s, size_t addlen);
void sdsIncrLen(sds s, ssize_t incr);
sds sdsRemoveFreeSpace(sds s, int would_regrow);
sds sdsResize(sds s, size_t size, int would_regrow);
size_t sdsAllocSize(sds s);
void *sdsAllocPtr(sds s);/* Export the allocator used by SDS to the program using SDS.* Sometimes the program SDS is linked to, may use a different set of* allocators, but may want to allocate or free things that SDS will* respectively free or allocate. */
void *sds_malloc(size_t size);
void *sds_realloc(void *ptr, size_t size);
void sds_free(void *ptr);#ifdef REDIS_TEST
int sdsTest(int argc, char *argv[], int flags);
#endif#endif

总结一下我的理解
sds动态扩容、做了三件事情
1.内存的极致控制(防止内存溢出、合理的扩容、收容机制等)
2.len记录使用长度
3.储存字符串

相关文章:

Redis之字符串类型深入之SDS底层结构

作为一名程序员不可能不知道redis 知道redis不可能不知道redis的字符串 如果你真的熟悉redis不能不知道sds, 我们探究一下redis字符串的底层结构 sds翻译过来就是动态扩容(Simple Dynamic String)、先看一下最早版本redis的sds结构体 struct sdshdr{int len; //记录数组中…...

Cesium 3dTileset 支持 uv 和 纹理贴图

原理: 使用自定义shader实现uv自动计算 贴图效果: uv效果:...

C++可变参数模板中的省略号

看可变参数模板代码时常会遇到省略号的使用&#xff0c;这类奇特的“...”出现位置还不固定&#xff0c;容易引起困惑。C最近一直不用都快废了&#xff0c;在此想对省略号的使用做个简单归纳以提醒自己。可变参数模板以两种方式使用省略号。 在参数名称的左侧&#xff0c;表示“…...

uni-ui 使用uni-icons有些图标显示不出来,如down,up图标

问题描述 我使用的是uni创建时勾选的uni-ui模板&#xff0c;一次偶然机会发现down图标显示不出&#xff0c;left&#xff0c;right等其他图标又可以。 最后发现使用uni-icons不是最新版本导致的&#xff0c;使用模板生成的icons是1.3.5版本&#xff0c;我在插件市场找到的是2.0…...

动态增删表格

期望目标&#xff1a;实现一个能通过按钮来动态增加表格栏&#xff0c;每次能添加一行&#xff0c;每行末尾有一个删减按钮。 <el-button type"text" class"primary"click"addMember()">添加</el-button> <el-table:data"m…...

Java-(乘法表之后)增强for循环

这里我们先做个了解&#xff0c;之后我会在数组中进行详细介绍Java5引入了一种主要用于数组或集合的增强型for循环Java增强型for循环语法格式如下 For(声明语句&#xff1a;表达式&#xff09;{ //代码语句 } 声明语句&#xff1a;声明新的局部变量&#xff0c;该变量的类型…...

Celery(分布式任务队列)入门学习笔记

Celery 的简单介绍 用 Celery 官方的介绍&#xff1a;它是一个分布式任务队列; 简单&#xff0c;灵活&#xff0c;可靠的处理大量消息的分布式系统; 它专注于实时处理&#xff0c;并支持任务调度。 Celery 如果使用 RabbitMQ 作为消息系统的话&#xff0c;整个应用体系就是下…...

【网络】tcp协议如何保证可靠性

TCP&#xff08;Transmission Control Protocol&#xff09;是一种面向连接的、可靠的传输层协议&#xff0c;为网络通信提供了可靠性和连接稳定性。本文将详细介绍 TCP 协议如何保证数据的可靠传输和连接的稳定性&#xff0c;并分析其优缺点。 可靠性保证 序号和确认机制&…...

select,poll,epoll

在 Linux Socket 服务器短编程时&#xff0c;为了处理大量客户的连接请求&#xff0c;需要使用非阻塞I/O和复用&#xff0c;select&#xff0c;poll 和 epoll 是 Linux API 提供的I/O复用方式。 \selectpollepoll操作方式遍历遍历回调底层实现数组链表哈希表IO效率每次调用都进…...

【48天笔试强训】day18

题目1 描述 有一种兔子&#xff0c;从出生后第3个月起每个月都生一只兔子&#xff0c;小兔子长到第三个月后每个月又生一只兔子。 例子&#xff1a;假设一只兔子第3个月出生&#xff0c;那么它第5个月开始会每个月生一只兔子。 一月的时候有一只兔子&#xff0c;假如兔子都…...

链表经典面试题01

目录 引言 面试题01:返回倒数第k个节点 题目描述: 思路分析: 代码展示: 面试题02:链表的回文结构 题目描述: 描述 思路分析: 代码展示: 面试题03:相交链表 题目描述: 思路分析: 代码展示: 小结: 引言 这次的题均来自力扣和牛客有关链表的经典面试题,代码只会展示…...

基于java的CRM客户关系管理系统的设计与实现(论文 + 源码 )

【免费】基于Java的CRM客户关系管理系统的设计和实现.zip资源-CSDN文库https://download.csdn.net/download/JW_559/89273409 基于Java的CRM客户关系管理系统的设计与实现 摘 要 随着互联网的高速发展&#xff0c;市场经济的信息化&#xff0c;让企业之间的竞争变得&#xff0…...

【动态规划-最长上升子序列模型part2】:拦截导弹、导弹防御系统、最长公共上升子序列【已更新完成】

1、拦截导弹 某国为了防御敌国的导弹袭击&#xff0c;发展出一种导弹拦截系统。 但是这种导弹拦截系统有一个缺陷&#xff1a;虽然它的第一发炮弹能够到达任意的高度&#xff0c;但是以后每一发炮弹都不能高于前一发的高度。 某天&#xff0c;雷达捕捉到敌国的导弹来袭。 由于…...

Spring 如何解决 Bean 循环依赖

循环依赖解释 bean A 属性注入时依赖bean B &#xff0c;并且bean B属性注入时也依赖bean A &#xff0c;造成 bean A 和bean B 都无法完成初始化问题&#xff0c;形成了闭环。 注意 项目中存在Bean的循环依赖&#xff0c;是Bean对象职责划分不明确、代码质量不高的表现&#…...

【driver4】锁,错误码,休眠唤醒,中断,虚拟内存,tasklet

文章目录 1.互斥锁和自旋锁选择&#xff1a;自旋锁&#xff08;开销少&#xff09;的自旋时间和被锁住的代码执行时间成正比关系2.linux错误码&#xff1a;64位系统内核空间最后一页地址为0xfffffffffffff000~0xffffffffffffffff&#xff0c;这段地址是被保留的&#xff0c;如果…...

python之 函数相关知识解析

01 函数的注释与嵌套 1.函数的注释 函数的注释与普通注释的区别&#xff1a;用来说明当前函数的参数含义 param 参数名: 参数的注释信息 return: 函数的返回值 例如&#xff1a; def fun1(name):""":param name: 参数的注释信息:return: 函数的返回值"…...

监视器和显示器的区别,普通硬盘和监控硬盘的区别

监视器与显示器的区别&#xff0c;你真的知道吗&#xff1f; 中小型视频监控系统中&#xff0c;显示系统是最能展现效果的一个重要环节&#xff0c;显示系统的优劣将直接影响视频监控系统的用户体验满意度。 中小型视频监控系统中&#xff0c;显示系统是最能展现效果的一个重要…...

Linux:升级OpenSSL和OpenSSH

原因是现有版本存在安全漏洞&#xff0c;需要升级到新版本 原有版本和升级后的版本 OpenSSL 1.0.2k-fips 26 Jan 2017 -> OpenSSL 1.1.1w 11 Sep 2023OpenSSH_7.4p1, OpenSSL 1.0.2k-fips 26 Jan 2017 -> OpenSSH_9.5p1, OpenSSL 1.1.1w 11 Sep 2023目录 查看现有版…...

方法的入栈和出栈

一.作用域问题 1.全局作用域 在全局都能进行访问的变量 var a 10;function fn() {var b 20;return a b;}console.log(fn()); 2.局部的作用域 只能在限定的范围内进行访问 function fn() {var b 20;}console.log(b); b is not defined 打印的结果是b这个变量没用定义 3…...

PHP介绍

&#x1f40c;博主主页&#xff1a;&#x1f40c;​倔强的大蜗牛&#x1f40c;​ &#x1f4da;专栏分类&#xff1a;PHP❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 目录 一、PHP是什么&#xff1f; 二、 PHP 文件是什么&#xff1f; 三、PHP能做什么&#xff1f; 四、P…...

关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案

问题描述&#xff1a;iview使用table 中type: "index",分页之后 &#xff0c;索引还是从1开始&#xff0c;试过绑定后台返回数据的id, 这种方法可行&#xff0c;就是后台返回数据的每个页面id都不完全是按照从1开始的升序&#xff0c;因此百度了下&#xff0c;找到了…...

全球首个30米分辨率湿地数据集(2000—2022)

数据简介 今天我们分享的数据是全球30米分辨率湿地数据集&#xff0c;包含8种湿地亚类&#xff0c;该数据以0.5X0.5的瓦片存储&#xff0c;我们整理了所有属于中国的瓦片名称与其对应省份&#xff0c;方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...

渲染学进阶内容——模型

最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...

网络编程(UDP编程)

思维导图 UDP基础编程&#xff08;单播&#xff09; 1.流程图 服务器&#xff1a;短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...

是否存在路径(FIFOBB算法)

题目描述 一个具有 n 个顶点e条边的无向图&#xff0c;该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序&#xff0c;确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数&#xff0c;分别表示n 和 e 的值&#xff08;1…...

Java + Spring Boot + Mybatis 实现批量插入

在 Java 中使用 Spring Boot 和 MyBatis 实现批量插入可以通过以下步骤完成。这里提供两种常用方法&#xff1a;使用 MyBatis 的 <foreach> 标签和批处理模式&#xff08;ExecutorType.BATCH&#xff09;。 方法一&#xff1a;使用 XML 的 <foreach> 标签&#xff…...

Caliper 负载(Workload)详细解析

Caliper 负载(Workload)详细解析 负载(Workload)是 Caliper 性能测试的核心部分,它定义了测试期间要执行的具体合约调用行为和交易模式。下面我将全面深入地讲解负载的各个方面。 一、负载模块基本结构 一个典型的负载模块(如 workload.js)包含以下基本结构: use strict;/…...

【 java 虚拟机知识 第一篇 】

目录 1.内存模型 1.1.JVM内存模型的介绍 1.2.堆和栈的区别 1.3.栈的存储细节 1.4.堆的部分 1.5.程序计数器的作用 1.6.方法区的内容 1.7.字符串池 1.8.引用类型 1.9.内存泄漏与内存溢出 1.10.会出现内存溢出的结构 1.内存模型 1.1.JVM内存模型的介绍 内存模型主要分…...

解析奥地利 XARION激光超声检测系统:无膜光学麦克风 + 无耦合剂的技术协同优势及多元应用

在工业制造领域&#xff0c;无损检测&#xff08;NDT)的精度与效率直接影响产品质量与生产安全。奥地利 XARION开发的激光超声精密检测系统&#xff0c;以非接触式光学麦克风技术为核心&#xff0c;打破传统检测瓶颈&#xff0c;为半导体、航空航天、汽车制造等行业提供了高灵敏…...

6个月Python学习计划 Day 16 - 面向对象编程(OOP)基础

第三周 Day 3 &#x1f3af; 今日目标 理解类&#xff08;class&#xff09;和对象&#xff08;object&#xff09;的关系学会定义类的属性、方法和构造函数&#xff08;init&#xff09;掌握对象的创建与使用初识封装、继承和多态的基本概念&#xff08;预告&#xff09; &a…...