百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术教程 > 正文

深入redis源码,了解数据结构设计思想

mhr18 2025-03-29 19:45 36 浏览 0 评论

不同存储介质访问延迟

  • o 机械硬盘 10ms
  • o 固态硬盘 100us
  • o 内存条 150ns

hashtable数据结构

  • o 数组+链表
  • o 计算key的hash值取余hashtable的大小得到索引位置,然后头插法或尾插法,将K,V保存到链表中(redis使用的是头插法,因为热点数据放入缓存,访问链表头部节点就可以获取到,而不需要遍历整个链表,提高查询效率)
  • o 数组中存储的都是指针数据,指向链表的头节点
  • o 不同的key输入,可能会有相同的输出,那么就会产生hash冲突,通过链表法来解决冲突。

当链表越来越长,通过扩容的方式来减小链表长度

  • o 开辟新的内存空间存放新的hashtable,长度是老的hashtable size的2倍
  • o 将老的hashtable中的元素渐进式的搬到新的hashtable中,将原来的元素重新做hash计算并取模得到在新hashtable中的位置
  • o 所有老的数据搬到新的上面来之后,把老的hashtable空间释放掉

渐进式的搬数据

在搬数据的时候,每次都是获取老的hashtable中的第一个hash槽,然后按照该槽上的链表顺序一个一个的去搬。

redis通过后台轮循事件,每次搬数据的时候默认获取前100个hash槽,一次性搬有限个,目的是为了降低主线程的卡顿时间。

redis无论是多线程版本还是老的单线程版本,最终执行命令都是单线程,所以就不能一次性把数据搬完,而是渐进式的一点点搬。

同时存在2个hashtable怎么进行数据访问?

  • o 查询一个key,先访问老的hashtable,如果有则直接返回,如果没有,再访问新的hashtable。
  • o 添加新的元素,放到新的数组中

`redis可以存储的数据结构

  • o string
  • o list
  • o set
  • o zset
  • o hash
  • o stream

redis客户端可以把任意类型字面量的key传给服务端

redis的key可以是任意的数据类型,甚至可以是一个文件、音频、视屏。通过redis客户端把不同数据类型的key,传递给redis服务端,redis服务端把key转换成一个string对象即所有键都是string对象。

redis string源码怎么设计的

在c语言中用char[] data数组来表示一个字符串,比如char[] data="pingfanrenbiji\0",c语言编译器还会在pingfanrenbiji后面加\0字符,作为字符串的结尾。但会有弊端,比如 data="dafasdasd\0dafadfa",正巧里面有一些特殊字符会导致数据直接截断,后面的就会被丢弃。

redis不是这种方式,而是自定义了一个数据结构SDS(simple dynamic string)

redis 3.2版本sds数据结构:

sds:
   int len:  8
   char[] buf="ping\0fan" 

\0占一个字节

sds根据自定义长度来决定字符串是否结束,可以理解为这是一种二进制安全的数据结构。

int 占4个字节,无符号数据范围是2^32-1,这是几个亿的数据范围,在业务上根本不用上,所以有很大的内存空间的浪费。

redis 6.x sds数据结构

使用相对更小的空间去描述数据。

hdr是头部数据的意思,比如unsigned char flags;uint8_t len;这些都是头部数据,是用来描述真正业务数据的长度,不是真正的数据,而是一些描述信息。

char类型在c语言中占1个字节,在java中占2个字节。

真正的数据长度封装在了flags中

一个字节有8个bit位,其中后面5个bit位用来描述数据长度,前面3个bit作为数据类型。

Type

hisdshdr8

uint8_t代表占8个bit位一个字节。alloc是为了满足一种特殊的需求:

buffer数组一旦定义,空间大小不可变

在上面那个字符串的基础上追加bbbb,需要重新开辟一个内存空间,再把数据拷贝过来,然后再追加字符串,这是一般的方法,很浪费内存空间,在redis 3.版本的sds中定义了一个int free字段和redis 6.x版本的uint8_t alloc含义是一样的,有了该字段就可以适用不同长度需求。

redis 3.2版本sds数据结构:

sds:
   int len:  6
   int free: 0
   char[] buf="pingfa\0" 

追加一个1

pingfa -> pingfa1

redis会自动分配6+1=7,7*2=14即一次性开辟14个空间

buf[14]=pingfa1

用掉了7还剩7即free=7

sds:
   int len:  7
   int free: 7
   buf[14]=pingfa1 

再追加23

pingfa1-> pingfa123

变成了

sds:
   int len:  9
   int free: 5
   buf[14]=pingfa123

这样的话,不需要重新分配内存空间,数据量比较大的情况下,直接追加,效率提升很明显。

alloc分配多少空间,减去使用的,就是剩下的free。

动态字符串,自动的去扩容。

char[] buf="pingfa123\0"

redis编译器自动加上\0字符,尽量兼容c语言本身的函数库。

通过redis源码了解数据库设计

底层数据结构

redisDb

  • o dict *dict

字典数据结构和hashmap是同一种数据类型,只是不同语言的不同实现。

  • o dict *expires

过期时间处理

  • o dict *blocking_keys

阻塞API,比如BLPOP是list中的阻塞api

  • o dict *ready_keys

阻塞的key,在收到消息时,做对应的处理

  • o dict *watched_keys

事务相关的命令

dict

ht 就是hashtable,因为需要rehash,所以数组长度是2,一个是老的hashtable,另外一个是新的hashtable,新的是老的hashtable大小的2倍。如果没有rehash的话,只会用到其中一个。

rehashidx 是rehash到每个hash槽的一个索引。

元素个数超过hashtable本身数组容量,就会触发rehash,首先申请大一倍的内存空间,渐进式的把数据从老的hashtable中搬到新的hashtable中,搬完之后,将老的hashtable释放掉,ht[0]指向新的数组,h[1]指向null,回到正常状态。

dictType

键key通过hashFunction这个hash函数计算并取模hashtable size,得到hash槽的位置。

如果2个key hashcode一样,如果2个key不一样,则是hash冲突,使用链表关联起来,如果2个key一样,则会覆盖。

dictht

table指向了一个hash链表;sizemask是求模的一个优化;used是hashtable中有多少个元素。

dictEntry

union中是value的类型,同一时间只能用一个;比如val就是key-value键值对类型,redis底层会进一步封装成redisObject对象;next指向链表中的下一个节点。

redisObject

  • o type 表示对象是什么类型,string、hash、list、set、zset,而type是什么取决于你使用什么样的api。

1、

value是整数、短字符串、长字符串,底层都是string类型。

set操作,底层就会封装成一个string对象。

2、

lpush操作,是list类型。list操作,底层会封装成一个list对象。

3、

hset操作,是hash类型。set操作,底层会封住成一个hash对象。

  • o encoding

每种类型底层有不同的编码。相同类型,底层编码也可能不相同即在内存中使用不同的方式去存储。

整数的编码是int,长字符串的编码是raw,短字符串的编码是embstr。

hash value短字符串底层编码是ziplist,hash value长字符串底层编码是hashtable。

  • o unsigned lru:LRU_BITS

内存淘汰算法

#define LRU_BITS 24

在内存中占24个bit位

  • o int refcount

引用计数法,判断对象是否存活的一种算法。

redis是c语言写的,需要自己的管理内存,该值为0,则可以把这个对象回收掉。

  • o void *ptr

这是指向内存区域的指针

看redis底层把数据存储在哪里?

redis底层最终会把100这个值封装成redisObject,通过encoding知道编码是int,确定编码之后,通过ptr找到真实的数据。

整数一般不会超过8个字节,java中最长是long类型占8个字节。

ptr指针在64位操作系统中占8个字节空间。

假设这个数据能用整型去表示,那也能用指针去表示,不让指针去存储真实的位置信息,而是直接存整数,是否可以?

ptr指向一个额外的内存空间,用额外的内存空间去存储整数的话,会导致什么问题?

  • o 需要一个内存空间来存储数据
  • o 先找到redisObject,通过ptr指针做一次io操作才能拿到真实的数据

如果直接用指针存储数据,有什么好处呢?

  • o 减少了额外的内存空间来存储数据
  • o 拿到redisObject中的ptr可以直接得到数据了,就不需要再通过ptr指针从内存中获取真实数据了,减少了一次io操作

相关推荐

Redis合集-使用benchmark性能测试

采用开源Redis的redis-benchmark工具进行压测,它是Redis官方的性能测试工具,可以有效地测试Redis服务的性能。本次测试使用Redis官方最新的代码进行编译,详情请参见Redis...

Java简历总被已读不回?面试挂到怀疑人生?这几点你可能真没做好

最近看了几十份简历,发现大部分人不是技术差,而是不会“卖自己”——一、简历死穴:你写的不是经验,是岗位说明书!反面教材:ד使用SpringBoot开发项目”ד负责用户模块功能实现”救命写法:...

redission YYDS(redission官网)

每天分享一个架构知识Redission是一个基于Redis的分布式Java锁框架,它提供了各种锁实现,包括可重入锁、公平锁、读写锁等。使用Redission可以方便地实现分布式锁。red...

从数据库行锁到分布式事务:电商库存防超卖的九重劫难与破局之道

2023年6月18日我们维护的电商平台在零点刚过3秒就遭遇了严重事故。监控大屏显示某爆款手机SKU_IPHONE13_PRO_MAX在库存仅剩500台时,订单系统却产生了1200笔有效订单。事故复盘发...

SpringBoot系列——实战11:接口幂等性的形而上思...

欢迎关注、点赞、收藏。幂等性不仅是一种技术需求,更是数字文明对确定性追求的体现。在充满不确定性的网络世界中,它为我们建立起可依赖的存在秩序,这或许正是技术哲学最深刻的价值所在。幂等性的本质困境在支付系...

如何优化系统架构设计缓解流量压力提升并发性能?Java实战分享

如何优化系统架构设计缓解流量压力提升并发性能?Java实战分享在高流量场景下。首先,我需要回忆一下常见的优化策略,比如负载均衡、缓存、数据库优化、微服务拆分这些。不过,可能还需要考虑用户的具体情况,比...

Java面试题: 项目开发中的有哪些成长?该如何回答

在Java面试中,当被问到“项目中的成长点”时,面试官不仅想了解你的技术能力,更希望看到你的问题解决能力、学习迭代意识以及对项目的深度思考。以下是回答的策略和示例,帮助你清晰、有说服力地展示成长点:一...

互联网大厂后端必看!Spring Boot 如何实现高并发抢券逻辑?

你有没有遇到过这样的情况?在电商大促时,系统上线了抢券活动,结果活动刚一开始,服务器就不堪重负,出现超卖、系统崩溃等问题。又或者用户疯狂点击抢券按钮,最后却被告知无券可抢,体验极差。作为互联网大厂的后...

每日一题 |10W QPS高并发限流方案设计(含真实代码)

面试场景还原面试官:“如果系统要承载10WQPS的高并发流量,你会如何设计限流方案?”你:“(稳住,我要从限流算法到分布式架构全盘分析)…”一、为什么需要限流?核心矛盾:系统资源(CPU/内存/数据...

Java面试题:服务雪崩如何解决?90%人栽了

服务雪崩是指微服务架构中,由于某个服务出现故障,导致故障在服务之间不断传递和扩散,最终造成整个系统崩溃的现象。以下是一些解决服务雪崩问题的常见方法:限流限制请求速率:通过限流算法(如令牌桶算法、漏桶算...

面试题官:高并发经验有吗,并发量多少,如何回复?

一、有实际高并发经验(建议结构)直接量化"在XX项目中,系统日活用户约XX万,核心接口峰值QPS达到XX,TPS处理能力为XX/秒。通过压力测试验证过XX并发线程下的稳定性。"技术方案...

瞬时流量高并发“保命指南”:这样做系统稳如泰山,老板跪求加薪

“系统崩了,用户骂了,年终奖飞了!”——这是多少程序员在瞬时大流量下的真实噩梦?双11秒杀、春运抢票、直播带货……每秒百万请求的冲击,你的代码扛得住吗?2025年了,为什么你的系统一遇高并发就“躺平”...

其实很多Java工程师不是能力不够,是没找到展示自己的正确姿势。

其实很多Java工程师不是能力不够,是没找到展示自己的正确姿势。比如上周有个小伙伴找我,五年经验但简历全是'参与系统设计''优化接口性能'这种空话。我就问他:你做的秒杀...

PHP技能评测(php等级考试)

公司出了一些自我评测的PHP题目,现将题目和答案记录于此,以方便记忆。1.魔术函数有哪些,分别在什么时候调用?__construct(),类的构造函数__destruct(),类的析构函数__cal...

你的简历在HR眼里是青铜还是王者?

你的简历在HR眼里是青铜还是王者?兄弟,简历投了100份没反应?面试总在第三轮被刷?别急着怀疑人生,你可能只是踩了这些"隐形求职雷"。帮3630+程序员改简历+面试指导和处理空窗期时间...

取消回复欢迎 发表评论: