文章目录

  • String底层
  • List底层
  • Hash底层
  • Set底层
  • Zset底层

String底层

String的底层并不是简单的C字符串简单动态字符串(Simple Dynamic String,SDS)
SDS简介

Redis是用C语言写的,但是他的字符串竟然不是C字符串,这让我很惊讶。既然不是C字符串,那一定有一个数据类型吧,这个数据类型是简单动态字符串(Simple Dynamic String,SDS),这是什么鬼,第一次听说。既然用他,他肯定有厉害的地方,接下来就研究研究他厉害到哪!
先举个例子说说字符串。

127.0.0.1:6379> set msg "hello"
OK
127.0.0.1:6379> keys *
1) "msg"

上面的"msg"和"hello"就是字符串,而且是一个SDS字符串,可不是一个C字符串。
SDS的内部存储结构
好了,说了这么多,还没介绍啥是SDS呢,先说一下SDS的内部存储结构,其中一个SDS,内部包含三部分

char buf[];// 字节数组,用于保存字符串
int len; // buf数组中已使用的字节数量,即是SDS字符串的长度
int free;// buf数组中未使用的字节数量

C字符串的内部存储结构

char str[];// 其内部就是一个简单的字节数组

SDS和C字符串的区别
获取字符串长度
由于C字符串没有哦记录自身的长度信息,所以获取C字符串长度的时候,必须遍历整个字符串,其时间复杂度是O(n),而SDS中有len属性,所以在获取其长度时,时间复杂度为O(1)。
缓冲区溢出
由于C字符串不记录字符串的长度,当我们进行字符串拼接的时候,可能会出现缓冲区溢出问题,举个例子,我们有s1和s2两个字符串,当我们想要把s2字符串拼接到s1字符串的时候,如果在拼接之前没有给s1分配足够的空间,那么s1的数据就会溢出到s2所在的空间,导致s2中原有的数据被破坏。而SDS在进行修改操作时,会先检查空间是否够用,如果不够用会自动扩展,以解决缓冲区溢出的问题。
内存重分配次数
对于C字符串而言,当对字符串进行拼接操作时,需要先通过内存重分配来扩展其底层的数组空间大小,如果不扩展,很可能就会出现缓冲区溢出,当金星字符串缩短时,也需要进行内存重分配,进行释放那部分不用的内存,如果忘了这步,会产生内存泄漏。可以看出,不管是字符串拼接,还是字符串缩短,都要执行内存重分配。
SDS就不一样了,SDS在进行字符串拼接的时候,采用空间预分配,举个例子进行讲解,如果我们有s1="hello"和s2=“sds”,两个字符串,以s1为研究对象,s1的len为5,free为0,当s2拼接到s1时,进行内存重分配,s1的len变成了8,free也变为8,这就是空间预分配,当你下次进行拼接"world"字符串的时候,由于free的空间够用,就可以直接分配了,而不用再次内存重分配。当我们进行缩短字符串的时候,SDS采用惰性空间释放,如果我们有一个字符串为s=“hellosds”,其len为8,free为0,当我们把sds进行截取掉的时候,其len变为了5,free变为了3,也就是这三个空间并没有通过内存重分配释放。这样就减少了内存重分配的次数,同时提高了效率,需要注意的是,free空间并不是无限制的大,free空间上限是1MB
二进制安全
C字符串的编码是ASCII编码,在字符串的末尾是以\0结束,也就是空字符,所以在字符串中不能包含空字符,要不然会让程序误以为结束,这也限制了C字符串只能保存文本数据,不能保存图片,音频,视频等二进制数据。
SDS不仅可以保存文本数据,还可以以二进制方式把其他数据存储到buf数组中的,程序不会对数据做任何限制,写入什么样,读取就是什么样,这样不仅可以存储文本,而且可以存储任意格式的二进制数据。
SDS兼容部分C字符串函数
SDS虽然是二进制安全的,但是他同样遵循C字符串以空字符串解为的惯例,所以SDS可以重用C字符串的一些函数。
SDS总结
SDS获取字符串长度的时间复杂度是O(1),C字符串是O(n)。
SDS不会造成缓冲区溢出,C字符串可能会。
SDS修改N次字符串长度最多执行N次内存重分配,C字符串是执行N次。
SDS可以保存文本数据和二进制数据,C字符串只能保存文本数据。
SDS可以使用部分<string.h>库函数,C字符串可以使用全部。
总之SDS在Redis中很强。

List底层

List的底层实现是压缩列表(ziplist)和链表(双向链表linkedlist)
当列表键(是指列表的value底层实现)包含的列表项比较少的时候,就用压缩列表来做列表的底层实现
压缩列表实现
zlbytes占4字节,记录压缩列表占用的内存字节
zltail占4字节,记录压缩列表尾到表头距离的字节
zllen占2字节,记录压缩列表包含的节点数
entryx占不定字节,压缩列表的各个节点(可以是字节数组和整数值)
zlend占1字节,标记压缩列表的表尾
压缩列表很像数组,可以简单理解为把数组进行了封装,里面包含了,一些属性,像zlend属性等
链表实现
下面给出每一个节点的底层实现

typedef struct listNode {struct listNode *prev;struct ListNode *next;void *value;// 保存节点的值
}listNode;

而list的底层就是多个链表节点连在一起的,另外,list还提供了一些固有的属性,方便操作,下面给出list的简单底层实现

typedef struct list {listNode *head;// 该指针指向链表的表头listNode *tail;// 该指针指向链表的表尾unsigned long len;// 记录链表的长度void *(*dup) (void *ptr);void (*free) (void *ptr);void (*match) (void *ptr,void *key);
}list;

list在操作链表的表头表尾和长度时,时间复杂度是O(1),list中还封装了有dup、free和match函数,分别用于复制链表节点所保存的值,释放链表节点所保存的值,对比链表节点所保存的值和另一个输入值是否相等。
列表常用操作
lpush从列表的左侧添加元素
rpush从列表的右侧添加元素
lpop从列表的左侧弹出一个元素,并返回该元素的值
rpop从列表的右侧弹出一个元素,并返回该元素的值
lindex返回指定索引的值
llen返回列表的长度
lrem删除包含给定元素的节点

Hash底层

Hash的底层是ziplist和hashtable,上面已经说了压缩列表,这里着重讲解HashTable。
压缩列表
当数据少的时候用压缩列表,存储的时候是成对(键值对)存储的,意思是占用一个压缩列表的连续两个位置,而不是用两个压缩列表存。
HashTable
哈希表里保存了一个hash表数组、hash表的大小、hash表大小掩码,用于计算索引值、hash表的节点数

type struct dictht {
dictEntry **table;
unsigned long size;
unsigned long sizemask;
unsigned long used;
}dictht;

table是一个hash表数组,该数组里的每个元素都保存着一个指针,保存键值对。 当同一个key对应多个键时,通过链地址法解决冲突(详情可以看面试官问你什么是Hash表),当数据多的时候,用跳跃表来实现,跳跃表基本可以和红黑树相媲美。
Hash常用操作
hsest添加新节点
hget返回节点的值
hexists查找指定键
hdel删除指定键
hlen返回键值对数
hgetall返回所有键和值

Set底层

底层是整数集合(intset)和hashtable
整数集合

typedef struct intset {uint32_t encoding;uint32_t length;int8_t contents[];// 这里保存的int类型,会根据存储数据而改变,可能是int_8、int_16、int_32等
}intset;

其底层是一个数组,该数组中的元素不可重复。
Set常用操作
sadd添加新元素到集合
scard返回集合中的元素个数
sismember查找指定元素
smembers返回集合元素
srandmember随机返回一个键
spop随机取出一个键返回,并删除
srem删除指定键

Zset底层

底层是ziplisst和skiplist,即是压缩列表和跳跃表
zset的特点就是可以通过分值排序。
Zset常用操作
zadd插入成员和分值
zcard获取元素数量
zcount统计分值在范围内的元素数
zrange从头遍历,返回给定索引范围内的元素
zrevrange从尾遍历,返回给定索引范围内的元素
zrank从头遍历,返回给定元素的排名
zrevrank从尾遍历返回给定元素的排名
zrem删除所有给定元素的节点以及分值节点
zscore获取给定元素的分值

学习了Redis的五大数据类型,其底层实现你了解吗?相关推荐

  1. redis的zset的底层实现_Redis(三)--- Redis的五大数据类型的底层实现

    1.简介 Redis的五大数据类型也称五大数据对象:前面介绍过6大数据结构,Redis并没有直接使用这些结构来实现键值对数据库,而是使用这些结构构建了一个对象系统redisObject:这个对象系统包 ...

  2. Redis中五大数据结构的底层实现

    来自:DBAplus社群 作者介绍 田兆壮,新炬网络开发工程师.具备扎实的Java.Scala开发经验,熟练使用Python和Shell等脚本语言:具备前后端开发能力,熟练使用关系型数据库和非关系型数 ...

  3. NoSQL(1)之 Redis的五大数据类型使用方法的详细介绍

    Redis的五大数据类型也称五大数据对象:了解过6大数据结构,Redis并没有直接使用这些结构来实现键值对数据库,而是使用这些结构构建了一个对象系统redisObject:这个对象系统包含了五大数据对 ...

  4. redis之五大数据类型

    redis之五大数据类型 redis redis的两种链接方式 简单链接 1234 import redisconn = redis.Redis(host='10.0.0.200',port=6379 ...

  5. 解析Redis操作五大数据类型常用命令

    摘要:分享经常用到一些命令和使用场景总结,以及对Redis中五大数据类型如何使用cmd命令行的形式进行操作的方法. 本文分享自华为云社区<Redis操作五大数据类型常用命令解析>,作者:灰 ...

  6. 配置文件存int类型_Redis详解(五)------ redis的五大数据类型实现原理

    > 本系列教程持续更新,可以微信搜索「 IT可乐 」第一时间阅读.回复<电子书>有我为大家特别筛选的书籍资料 前面两篇博客,第一篇介绍了五大数据类型的基本用法,第二篇介绍了Redis ...

  7. Redis常用五大数据类型

    1.String(字符串) string类型是二进制安全的.意思是redis的string可以包含任何数据.比如jpg图片或者序列化的对象 . string类型是Redis最基本的数据类型,一个red ...

  8. Redis的五大数据类型

    1.String(字符串) String是Redis最基本的类型,一个Key对应一个Value. String类型是二进制安全的,意思是Redis的String可以包含任何数据,比如jpg图片或者序列 ...

  9. redis srandmember_Redis五大数据类型使用场景

    Redis是一种基于键值对的NoSQL数据库,它的值主要由string(字符串),hash(哈希),list(列表),set(集合),zset(有序集合)五种基本数据结构构成,除此之外还支持一些其他的 ...

最新文章

  1. 【数据结构与算法】之深入解析“贪心算法“的原理解析和算法实现
  2. Visual Studio 常用快捷键 (二)
  3. 虚拟技术必须解决的问题_VR/3D虚拟实验室亮相重庆市初中物理青年教师优质课大赛...
  4. Google惊人研究:一组图片,就能强迫神经网络执行其他任务
  5. Scala 专题指南
  6. dcopserver出错解决办法
  7. 算法——最好理解的动态规划之01背包详解(看完这篇再不敢说自己不知道01背包算法!!!)
  8. 基于Microhard P900无人机PIX飞控远距离数传解决方案
  9. Java笔记17:JAVA常用函数
  10. 何钦铭.c语言程序设计,《C语言程序设计》 - 何钦铭
  11. php 58房源采集,如何用火车采集器采集二手房数据
  12. 100层高的大楼,丢玻璃球的问题
  13. 在Windows上安装Elasticsearch v5.4.2
  14. 绘制半长轴和半短轴分别为a,b的椭圆
  15. 读写锁 -- ReentrantReadWriteLock
  16. LVM-HOWTO/学习笔记(五)
  17. 【场景化解决方案】慧致造ERP,为企业提供生产全流程数字化管理
  18. Linux 挂载(NFS)
  19. 13图形光栅化——区域填充(种子填充)+多边形扫描转换(扫描线算法)
  20. 2013年第4季度MP3品牌网络知名度排名

热门文章

  1. GPUImage组合滤镜
  2. Flex4学习笔记(二)--语法相关
  3. Reids Lua 模糊查询所有key 及 相对应的集合总数
  4. HTTP性能测试工具wrk安装及使用
  5. SDF(Signed-distance-field: 有向距离场)(12): Shadow And AO(WebGL实现)增强立体感
  6. 关于svn的安装配置开启服务过程和 eclipse安装SVN插件的方法
  7. java 多线程 day06 threadLocal
  8. 动态规划经典题之石子合并
  9. Windows Embedded Standard 7 剪裁随笔
  10. JAVA设计模式-委派模式(Delegate)