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

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

mhr18 2025-03-29 19:45 51 浏览 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操作

相关推荐

【推荐】一个开源免费、AI 驱动的智能数据管理系统,支持多数据库

如果您对源码&技术感兴趣,请点赞+收藏+转发+关注,大家的支持是我分享最大的动力!!!.前言在当今数据驱动的时代,高效、智能地管理数据已成为企业和个人不可或缺的能力。为了满足这一需求,我们推出了这款开...

Pure Storage推出统一数据管理云平台及新闪存阵列

PureStorage公司今日推出企业数据云(EnterpriseDataCloud),称其为组织在混合环境中存储、管理和使用数据方式的全面架构升级。该公司表示,EDC使组织能够在本地、云端和混...

对Java学习的10条建议(对java课程的建议)

不少Java的初学者一开始都是信心满满准备迎接挑战,但是经过一段时间的学习之后,多少都会碰到各种挫败,以下北风网就总结一些对于初学者非常有用的建议,希望能够给他们解决现实中的问题。Java编程的准备:...

SQLShift 重大更新:Oracle→PostgreSQL 存储过程转换功能上线!

官网:https://sqlshift.cn/6月,SQLShift迎来重大版本更新!作为国内首个支持Oracle->OceanBase存储过程智能转换的工具,SQLShift在过去一...

JDK21有没有什么稳定、简单又强势的特性?

佳未阿里云开发者2025年03月05日08:30浙江阿里妹导读这篇文章主要介绍了Java虚拟线程的发展及其在AJDK中的实现和优化。阅前声明:本文介绍的内容基于AJDK21.0.5[1]以及以上...

「松勤软件测试」网站总出现404 bug?总结8个原因,不信解决不了

在进行网站测试的时候,有没有碰到过网站崩溃,打不开,出现404错误等各种现象,如果你碰到了,那么恭喜你,你的网站出问题了,是什么原因导致网站出问题呢,根据松勤软件测试的总结如下:01数据库中的表空间不...

Java面试题及答案最全总结(2025版)

大家好,我是Java面试陪考员最近很多小伙伴在忙着找工作,给大家整理了一份非常全面的Java面试题及答案。涉及的内容非常全面,包含:Spring、MySQL、JVM、Redis、Linux、Sprin...

数据库日常运维工作内容(数据库日常运维 工作内容)

#数据库日常运维工作包括哪些内容?#数据库日常运维工作是一个涵盖多个层面的综合性任务,以下是详细的分类和内容说明:一、数据库运维核心工作监控与告警性能监控:实时监控CPU、内存、I/O、连接数、锁等待...

分布式之系统底层原理(上)(底层分布式技术)

作者:allanpan,腾讯IEG高级后台工程师导言分布式事务是分布式系统必不可少的组成部分,基本上只要实现一个分布式系统就逃不开对分布式事务的支持。本文从分布式事务这个概念切入,尝试对分布式事务...

oracle 死锁了怎么办?kill 进程 直接上干货

1、查看死锁是否存在selectusername,lockwait,status,machine,programfromv$sessionwheresidin(selectsession...

SpringBoot 各种分页查询方式详解(全网最全)

一、分页查询基础概念与原理1.1什么是分页查询分页查询是指将大量数据分割成多个小块(页)进行展示的技术,它是现代Web应用中必不可少的功能。想象一下你去图书馆找书,如果所有书都堆在一张桌子上,你很难...

《战场兄弟》全事件攻略 一般事件合同事件红装及隐藏职业攻略

《战场兄弟》全事件攻略,一般事件合同事件红装及隐藏职业攻略。《战场兄弟》事件奖励,事件条件。《战场兄弟》是OverhypeStudios制作发行的一款由xcom和桌游为灵感来源,以中世纪、低魔奇幻为...

LoadRunner(loadrunner录制不到脚本)

一、核心组件与工作流程LoadRunner性能测试工具-并发测试-正版软件下载-使用教程-价格-官方代理商的架构围绕三大核心组件构建,形成完整测试闭环:VirtualUserGenerator(...

Redis数据类型介绍(redis 数据类型)

介绍Redis支持五种数据类型:String(字符串),Hash(哈希),List(列表),Set(集合)及Zset(sortedset:有序集合)。1、字符串类型概述1.1、数据类型Redis支持...

RMAN备份监控及优化总结(rman备份原理)

今天主要介绍一下如何对RMAN备份监控及优化,这里就不讲rman备份的一些原理了,仅供参考。一、监控RMAN备份1、确定备份源与备份设备的最大速度从磁盘读的速度和磁带写的带度、备份的速度不可能超出这两...

取消回复欢迎 发表评论: