Redis源码阅读笔记 ----数据结构部分 sds
壹、sds
(SDS)Simple Dynamic String, 从字面意思上来简单理解就是简单的动态字符,其为具有动态增加空间的能力,扩容不需要使用者担心。
好的咱们来看一下他的数据结构吧
这个版本是黄所注释的3.0版本
typedef char *sds; //注意,sds其实不是一个结构体类型,而是被typedef的char*,好处见下文struct sdshdr {unsigned int len; //记录buf中已经使用的长度 等于SDS所保存的字符串的长度unsigned int free; //buf中未使用的长度char buf[]; //柔性数组buf
};
sds遵循c字符串以空字符结尾的惯例,但是保存空字符的一字节空间不计算在sds的len属性之中,并且为空字符分配额外的一字节空间,使用这一惯例的好处是可直接重用c字符串函数库里面的额函数,这也符合软件工程的思想。
此处应有图(没图你说个毛)
但是在Redis 3.2 版本中,对数据结构做出了修改,针对不同的长度范围定义了不同的结构,如下,这是目前的结构:
typedef char *sds; struct __attribute__ ((__packed__)) sdshdr5 { // 对应的字符串长度小于 1<<5unsigned char flags; /* 3 lsb of type, and 5 msb of string length */char buf[];
};
struct __attribute__ ((__packed__)) sdshdr8 { // 对应的字符串长度小于 1<<8uint8_t len; /* used */ //目前字符串的长度uint8_t alloc; //已经分配的总长度unsigned char flags; //flag用3bit来标明类型,类型后续解释,其余5bit目前没有使用char buf[]; //柔性数组,以'\0'结尾
};
struct __attribute__ ((__packed__)) sdshdr16 { // 对应的字符串长度小于 1<<16uint16_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 { // 对应的字符串长度小于 1<<32uint32_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 { // 对应的字符串长度小于 1<<64uint64_t len; /* used */uint64_t alloc; /* excluding the header and null terminator */unsigned char flags; /* 3 lsb of type, 5 unused bits */char buf[];
};
新版带来的好处就是针对长度不同的字符串做了优化,选取不同的数据类型uint8_t或者uint16_t或者uint32_t等来表示长度、一共申请字节的大小等。上面结构体中的 attribute ((packed)) 设置是告诉编译器取消字节对齐,则结构体的大小就是按照结构体成员实际大小相加得到的。(这也是我第一次了解到的) 记得小组每年的纳新面试题,结构体的大小是必考的,试想当年如果能答出来这个的话,会不会刮目呢。
贰、SDS的优劣
typedef char *sds;
e.m.
typedef char *sds;
sds string = sdsnew ("hello world!"); // 生成这样的一个sds。
我们通过sdsnew返回的实际上是一个char * 类型的指针,这个指针指向的是字符串的开始位置,它的头部信息是在字符串前面分配的,这样带来的好处有:
- sds可以传给任何 char * 为参数的函数,复用c 函数。
- string[0], 可以直接访问单个字符,对长度进行检测,杜绝缓冲区溢出。
- 分配的空间地址是连续的(存疑),对高速缓存的命中率更加友好,
- 二进制安全,读完的标志为len - free, 可存二进制数据。
- 获取长度的复杂度为O(1),
除此之外需要重点介绍的就是关于sds的内存分配策略(主要指修改时),
redis 作为数据库,经常被用于速度要求严苛,数据被频繁修改的场合,如果每次修改字符串的时候都去执行一次内存重新分配,那么光是执行内存重新分配都会占去很大一部分时间,频繁发生的话,会对性能造成影响。
为了避免之中缺陷,sds解除了字符串长度与底层数组的关联: 在SDS中,buf的长度不一定是字符数量加1,数组里边可以包含未使用的字节,由SDS中的free来记录。
通过未使用空间,SDS使用了空间预分配和惰性空间释放的两种优化策略。
1.空间预分配
空间预分配用于优化SDS的字符串增长操作:即不仅分配修改所需空间,还会额外分配未使用空间。
其中未使用的空间的数量由一下公式决定
- 进行修改之后,sds的长度(len的属性值)将小于1MB,那么程序分配和len属性同样大小的未使用空间,举个例子,修改之后len将变为12,则程序会另外分配13个未使用的字节,总长为 len = 12+12+1 = 25
(不要忘记额外的一字节用于保存空字符串)。 - 如果大于1MB,会分配1MB的未使用空间。举个例子,sds变为12MB,那么程序会分配1MB的未使用空间,则len = 12MB+1MB+1byte。
通过这种预分配策略,SDS连续增长N次字符串所需要的内存冲分配次数从N次将为最对N次。
2.惰性空间释放
用有优化SDS缩短操作:当字符串缩短时,并不立即使用内存重新分配回收多出来的字符,而是使用free把这些字节的长度记录起来等待将来使用。
可能你会担心惰性空间释放策略会导致内存浪费。Never mind,SDS提供相应的api 让我们在有需要的时候,真正的释放SDS的未使用空间。
要说缺点也有:虽然用这么多手段提高效率,但是我们并不知道内部是否重新分配了空间。
可以预见到的是,如果程序中别的地方使用了这个字符串,一经改变之后,没有更新地址,那么原地址就会失效,所以这一定要注意,索性这些问题redis已经帮我们处理就好了,我们只需要拿来用就可以啦!!!
Redis源码阅读笔记 ----数据结构部分 sds相关推荐
- Redis源码阅读笔记-动态字符串(SDS)结构
2019独角兽企业重金招聘Python工程师标准>>> Redis中采用自定义的结构来保存字符串,在sds.h中: /* Note: sdshdr5 is never used, w ...
- Redis源码阅读笔记(1)——简单动态字符串sds实现原理
首先,sds即simple dynamic string,redis实现这个的时候使用了一个技巧,并且C99将其收录为标准,即柔性数组成员(flexible array member),参考资料见这里 ...
- Redis源码阅读笔记(二)list双向链表结构
- list简介 相对于sds来说,list并没有太多新的想法和机制,就和自己实现一个双向链表差不多,主要有节点结构.链表结构和迭代器结构三个部分. 只有一个比较新奇的想法是,因为在存储节点的值时,使 ...
- 出招吧!腾讯专家手敲《Redis源码日志笔记》,不服来对打!
引言 本文分为六个部分,包括 Redis 源码日志,服务框架,基础数据结构,内功心法,应用,其他,从源码层面循序渐进的了解Redis.可以快速.有效地了解Redis 的内部构造以及运作机制,更好.更高 ...
- redis源码阅读-持久化之RDB
持久化介绍: redis的持久化有两种方式: rdb :可以在指定的时间间隔内生成数据集的时间点快照(point-in-time snapshot) aof : 记录redis执行的所有写操作命令 根 ...
- redis源码阅读-zset
前段时间给小伙伴分享redis,顺带又把redis撸了一遍了,对其源码,又有了比较深入的了解.(ps: 分享的文章再丰富下再放出来). 数据结构 我们先看下redis 5.0的代码.本次讲解主要是zs ...
- 代码分析:NASM源码阅读笔记
NASM源码阅读笔记 NASM(Netwide Assembler)的使用文档和代码间的注释相当齐全,这给阅读源码 提供了很大的方便.按作者的说法,这是一个模块化的,可重用的x86汇编器, 而且能够被 ...
- CI框架源码阅读笔记4 引导文件CodeIgniter.php
到了这里,终于进入CI框架的核心了.既然是"引导"文件,那么就是对用户的请求.参数等做相应的导向,让用户请求和数据流按照正确的线路各就各位.例如,用户的请求url: http:// ...
- 【Flink】Flink 源码阅读笔记(20)- Flink 基于 Mailbox 的线程模型
1.概述 转载:Flink 源码阅读笔记(20)- Flink 基于 Mailbox 的线程模型 相似文章:[Flink]Flink 基于 MailBox 实现的 StreamTask 线程模型 Fl ...
最新文章
- java中缀表达式转后缀表达式(逆波兰算法)
- 民族、学历学位、所学专业、、专业技术职务 对应表
- c++中list容器
- [LeetCode]题解(python):008-String to Integer (atoi)
- 大数据Hadoop原理学习(HDFS,MAPREDUCE,YARN)
- c语言源程序自动评判系统,C语言源程序的自动评判毕业设计论文
- CentOS 7.4 上如何安装 tomcat 9
- mysql 6位随机数_MySQL生成固定位数的随机数
- 第一节:setTimeout和setInterval定时器
- 初学者指南:为开源做贡献
- 湖大离散数学实验代码汇总
- leetcode第1282题
- Duilib介绍-3
- halo博客:如何加快搜索引擎收录网站速度
- 物联网应用之远程控制
- 通过 api 和 keycloak 理解OIDC认证
- 删除Linux虚拟机中的/dev/sdb磁盘步骤
- python绘制动态Julia集,超炫酷
- ipad协议最新版本
- 机器视觉实用工具集NO.16——指定监控区域运动物体闯入检测工具
热门文章
- ctf解题--当眼花的时候,会显示两张图(隐写)
- Keil MDK又来了一个小升级
- 将MindManager添加到鼠标右键新建项
- bugly android 错误不上报,Flutter Android 端集成 Bugly 的异常上报和升级功能
- mysql数据库基础知识点总结--看完即入门
- 一周上榜新书:强化学习、深度学习最受程序员关注
- 自学编程,痛并快乐着
- vue集成svg-sprite-loader
- scratch双语教师课件文档手册 1.初识scratch
- 微趣能Weiqn(微信源码下载)V1.5.0.2beta官方版