redis的内部数据结构主要有:字符串,双端链表,字典,跳跃表. 这里主要记录redise字符串的设计.相关的源码位于:src/sds.h 和 src/sds.c.   一 字符串 sds的结构体 struct sdshdr { int len; // buf 已占用长度 int free; // buf 剩余可用长度 char buf[]; // 实际保存字符串数据的地方 }; 从这个结构可以看出,redis字符串和C的不一样,本质字符串是保存在内存的某一个位置,然后把它的指针放到buf上..…
初衷 从开始工作就开始使用Redis,也有一段时间了,但都只是停留在使用阶段,没有往更深的角度探索,每次想读源码都止步在阅读书籍上,因为看完书很快又忘了,这次逼自己先读代码.因为个人觉得写作需要阅读文字来增强灵感,那么写代码的,就阅读更多代码来增强灵感吧. redis的实现原理,在<Redis设计与实现>一书中讲得很详细了,但是想通过结合代码的形式再深入探索,加深自己的理解,现在将自己探索的心得写在这儿. sds结构体的定义 #define SDS_TYPE_5 0 #define SDS_T…
Redis源码阅读(二)高可用设计-复制 复制的概念:Redis的复制简单理解就是一个Redis服务器从另一台Redis服务器复制所有的Redis数据库数据,能保持两台Redis服务器的数据库数据一致. 使用场景:复制机制很实用,在客户端并发访问量很大,单台Redis扛不住的情况下,可以部署多台Redis复制相同的数据,共同对外提供服务,提高Redis并发访问处理能力.当然这种通过复制方式部署多台Redis以提高并发处理能力的方式只适用于客户端大部分访问为读数据请求的场景.此外,Redis从2.…
Redis源码阅读(三)集群-连接建立 对于并发请求很高的生产环境,单个Redis满足不了性能要求,通常都会配置Redis集群来提高服务性能.3.0之后的Redis支持了集群模式. Redis官方提供的集群功能是无中心的,命令请求可以发送到任意一个Redis节点,如果该请求的key不是由该节点负责处理,则会返回给客户端MOVED错误,提示客户端需要转向到该key对应的处理节点上.支持集群模式的redis客户端会自动进行转向,普通模式客户端则只返回MOVED错误. 先看下常见的Redis集群结构:…
Redis源码阅读-链表部分- 链表数据结构在Adlist.h   Adlist.c Redis的链表是双向链表,内部定义了一个迭代器. 双向链表的函数主要是链表创建.删除.节点插入.头插入.尾插入.第N个节点.节点迭代遍历.链表复制.链表rotate.节点删除 typedef struct listNode { struct listNode *prev; struct listNode *next; void *value; //定义为void *类型,方便用户自行使用自己的数据结构 } l…
Redis源码阅读(六)集群-故障迁移(下) 最近私人的事情比较多,没有抽出时间来整理博客.书接上文,上一篇里总结了Redis故障迁移的几个关键点,以及Redis中故障检测的实现.本篇主要介绍集群检测到某主节点下线后,是如何选举新的主节点的.注意到Redis集群是无中心的,那么使用分布式一致性的算法来使集群中各节点能对在新主节点的选举上达成共识就是一个比较可行的方案. 在工程上,Raft一致性算法是比较易于实现和理解的分布式一致性算法:Redis也是使用了Raft来做主节点选举的.所以这里先简单…
Redis源码阅读(四)集群-请求分配 集群搭建好之后,用户发送的命令请求可以被分配到不同的节点去处理.那Redis对命令请求分配的依据是什么?如果节点数量有变动,命令又是如何重新分配的,重分配的过程是否会阻塞对外提供的服务?接下来会从这两个问题入手,分析Redis3.0的源码实现. 1. 分配依据——槽 Redis将每个客户端的请求命令通过哈希的方式映射到槽上,映射方法就是对该客户端请求中的键值求CRC16校验值,求得的值再和16383(0x3FFF)进行与操作,得到的结果即为槽值:Redis…
Redis源码阅读(一)事件机制 Redis作为一款NoSQL非关系内存数据库,具有很高的读写性能,且原生支持的数据类型丰富,被广泛的作为缓存.分布式数据库.消息队列等应用.此外Redis还有许多高可用特性,包括数据持久化,主从模式备份等等,可以满足对数据完整性有一定要求的场景. Redis的源码结构简单清晰,有大量材料可以参阅:通过阅读Redis源码,掌握一些常用技术在Redis中的实现,相信会对个人编程水平有很大帮助.这里记录下我阅读Redis源码的心得.从我自己比较关心的几个技术点出发,每…
Redis源码阅读(五)集群-故障迁移(上) 故障迁移是集群非常重要的功能:直白的说就是在集群中部分节点失效时,能将失效节点负责的键值对迁移到其他节点上,从而保证整个集群系统在部分节点失效后没有丢失数据,仍能正常提供服务.这里先抛开Redis实际的做法,我们可以自己想下对于Redis集群应该怎么做故障迁移,哪些关键点是必须要实现的.然后再去看Redis源码中具体的实现,是否覆盖了我们想到的关键点,有哪些设计是我们没有想到的,这样看代码的效果会比较好. 我在思考故障迁移这个功能时,首先想到的是节点…
源码阅读基于Redis4.0.9 SDS介绍 redis 127.0.0.1:6379> SET dbname redis OK redis 127.0.0.1:6379> GET dbname "redis" 从上面的例子可以看到,key为dbname的值是一个字符串"redis" Redis源码是用c写成,但并没有使用c的字符串.c的字符串有以下缺点: 没有储存字符串长度的变量,获取长度只能靠遍历字符串 扩容麻烦.没有相应保护,容易造成缓冲区溢出 更…
首先,sds即simple dynamic string,redis实现这个的时候使用了一个技巧,并且C99将其收录为标准,即柔性数组成员(flexible array member),参考资料见这里.柔性数组成员不占用结构体的空间,只作为一个符号地址存在,而且必须是结构体的最后一个成员.柔性数组成员不仅可以用于字符数组,还可以是元素为其它类型的数组.C99中,结构中的最后一个元素允许是未知大小的数组,这就叫做柔性数组成员,但结构中的柔性数组成员前面必须至少一个其他成员.柔性数组成员允许结构中包…
对于并发请求很高的生产环境,单个Redis满足不了性能要求,通常都会配置Redis集群来提高服务性能.3.0之后的Redis支持了集群模式. Redis官方提供的集群功能是无中心的,命令请求可以发送到任意一个Redis节点,如果该请求的key不是由该节点负责处理,则会返回给客户端MOVED错误,提示客户端需要转向到该key对应的处理节点上.支持集群模式的redis客户端会自动进行转向,普通模式客户端则只返回MOVED错误. 先看下常见的Redis集群结构: 节点两两之间都有连接,只有主节点可以处…
dict的用途 dict是一种用于保存键值对的抽象数据结构,在redis中使用非常广泛,比如数据库.哈希结构的底层. 当执行下面这个命令: > set msg "hello" 以及使用哈希结构,如: > hset people name "hoohack" 都会使用到dict作为底层数据结构的实现. 结构的定义 先看看字典以及相关数据结构体的定义: 字典 /* 字典结构 每个字典有两个哈希表,实现渐进式哈希时需要用在将旧表rehash到新表 */ typ…
前段时间翻看了Redis的源代码(C语言版本,Git地址:https://github.com/antirez/redis), 过了一遍Redis数据结构,包括SDS.ADList.dict.intset.ziplist.quicklist.skiplist. 在此进行总结 一.SDS(Simple Dynamic String) 简单动态字符串 SDS是redis最简单的数据结构 sds(简单动态字符串)特点,预先分配内存,记录字符串长度,在原字符串数组里新增加一串字符串. 新长度newlen…
Redis主从复制 为了提高性能和系统可用,Redis都会做主从复制,一来可以分担主库压力,二来在主库挂掉的时候从库依旧可以提供服务.Redis的主从复制是异步复制,返回结果给客户端和同步命令到从库是两回事,互不相干,主库也不关心从库的执行结果,对于同步命令执行的结果,从库会直接丢弃并不返回给主库.Redis的主从复制简单高效,但也不太算可靠. Redis的主从复制是异步复制:全量同步(或增量同步)+命令传播 Slave Server Slave Server启动初始化配置,根据slaveof配…
结构定义 在redis中,对象的数据结构定义如下: ​typedef struct redisObject { ​unsigned type:4; ​unsgined encoding:4; ​unsigned lru:LRU_BITS; ​int refcount; ​void *ptr; ​} 结构定义中的type:4.encoding:4这种定义方式称为位段类型. 使用位段类型的好处就是避免浪费内存,如果使用unsigned int type定义type字段,需要4个字节,而使用unsig…
跳跃表是一种随机化的数据结构,在查找.插入和删除这些字典操作上,其效率可比拟于平衡二叉树(如红黑树),大多数操作只需要O(log n)平均时间,但它的代码以及原理更简单. 和链表.字典等数据结构被广泛地应用在Redis内部不同,Redis只在两个地方用到了跳跃表,一个是实现有序集合键,另一个是在集群结点中用作内部数据结构.除此之外,跳跃表在Redis里面没有其他用途. /* ZSETs use a specialized version of Skiplists */ typedef struc…
redis使用sds代替char *字符串, 其定义如下: typedef char *sds; struct sdshdr { unsigned int len; unsigned int free; char buf[]; }; sds指向了char 字符串 sdshdr是字符串头 结构比较巧妙 使用char buf[]存放字符串实际内容 注意char *buf和char buf[]是不同的 sizeof(sdshdr)等于8,而不是我以为的12 连续内存结构如下: 0----7 sdshd…
Dict和Java中的HashMap很相似,都是数组开链法解决冲突. 但是Redis为了高性能, 有很多比较微妙的方法,例如 数组的大小总是2的倍数,初始大小是4. rehash并不是一次就执行完,而是分多次执行.每次执行一部分.其中rehashidx表示现在hash到哪一个桶啦,-1表示现在并没有rehash. dict包含两个dicttable, 编号为0,1,  dictht0是直接存储哈希表的地方, dictht1在rehash中用到,当rehashidx不为-1时, 查找key,同时在…
redis 在底层中会把long long转成string 再做存储. 主个功能是在sds模块里. 下面两函数是把long long 转成 char  和   unsiged long long 转成 char. 大致的思路是: 1 把数值从尾到头一个一个转成字符, 2 算出长度,加上结束符. 3 把字符串反转一下. 4 如果是 long long 型 要考虑有负数的情况.   int sdsll2str(char *s, long long value) { char *p, aux; uns…
本文不讲hash算法,而主要是分析redis中的dict数据结构的特性--分步rehash. 首先看下数据结构:dict代表数据字典,每个数据字典有两个哈希表dictht,哈希表采用链式存储. typedef struct dictEntry {//封装键值对 void *key; union {//联合体表示不同数据类型,节省空间 void *val; uint64_t u64; int64_t s64; } v; struct dictEntry *next; } dictEntry; ty…
因为redis是用c写的,c中没有自带的map,所以redis自己实现了map,来看一下redis是怎么实现的. 1.redis字典基本数据类型 redis是用哈希表作为字典的底层实现,dictht是哈希表的定义: typedef struct dictht { // 哈希表节点指针数组(俗称桶,bucket) dictEntry **table; // 指针数组的大小 unsigned long size; // 指针数组的长度掩码,用于计算索引值 unsigned long sizemask…
作为web开发的一员,相信大家的面试经历里少不了会遇到这个问题:redis是怎么做持久化的? 不急着给出答案,先停下来思考一下,然后再看看下面的介绍.希望看了这边文章后,你能够回答这个问题. 为什么需要持久化? 由于Redis是一种内存型数据库,即服务器在运行时,系统为其分配了一部分内存存储数据,一旦服务器挂了,或者突然宕机了,那么数据库里面的数据将会丢失,为了使服务器即使突然关机也能保存数据,必须通过持久化的方式将数据从内存保存到磁盘中. 对于进行持久化的程序来说,数据从程序写到计算机的磁盘的…
原文链接 在这篇文章中, 我将向大家介绍一种我认为比较合理的 Redis 源码阅读顺序, 希望可以给对 Redis 有兴趣并打算阅读 Redis 源码的朋友带来一点帮助. 第 1 步:阅读数据结构实现 刚开始阅读 Redis 源码的时候, 最好从数据结构的相关文件开始读起, 因为这些文件和 Redis 中的其他部分耦合最少, 并且这些文件所实现的数据结构在大部分算法书上都可以了解到, 所以从这些文件开始读是最轻松的.难度也是最低的. 下表列出了 Redis 源码中, 各个数据结构的实现文件: 文…
2019年第一篇文档,为2019年做个良好的开端,本文档通过step by step的方式向读者展示如何为redis添加一个数据类型,阅读本文档后读者对redis源码的执行逻辑会有比较清晰的认识,并且可以深入理解redis 源码中关于链表数据结构的使用,写这篇文档作者获益良多,阅读系统软件源码的兴趣也大大提高. 同时也再次感受到良好的基础是深入学习的前提.特别强调本文档仅用于学习,并非是要修改redis源码. 建议读者阅读本文档时实际动手敲一下代码,然后翻阅下redis源码,debug下redi…
本文档分为三大部分: 环境介绍与效果演示 redis接收命令到返回数据的执行逻辑 代码实现 文档的重点和难点在第三部分,完全阅读本文档需要读者具备基本的c语言和数据结构知识. 环境介绍和效果演示环境介绍redis版本为5.0.3 64 bit操作系统版本为Ubuntu 18.10 64bit源码可以用gedit查看 gdb调试ide 可以用eclipse+CDT 效果演示本案例实现了一个链表,对应redis的list数据类型,对链表的操作实现了插入.设置某个节点的值.新建节点.获取一定范围内的节…
redis中动态字符串sds相关的文件为:sds.h与sds.c 一.数据结构 redis中定义了自己的数据类型"sds",用于描述 char*,与一些数据结构 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…
Redis源码学习:字符串 1.初识SDS 1.1 SDS定义 Redis定义了一个叫做sdshdr(SDS or simple dynamic string)的数据结构.SDS不仅用于 保存字符串,还用来当做缓冲区,例如AOF缓冲区或输入缓冲区等.如下所示,整数len和free分别表示buf数组中已使用的长度和剩余可用的长度,buf是一个原生C字符串,以\0结尾. sds就是sdshdr中char buf[]的别名,后面能看到,各种操作函数的入参和返回值都是sds而非sdshdr.那sdshd…
redis中双向链表相关的文件为:adlist.h与adlist.c 一.数据结构 redis里定义的双向链表,与普通双向链表大致相同 单个节点: typedef struct listNode { struct listNode *prev; struct listNode *next; void *value; } listNode; 链表: typedef struct list { listNode *head; listNode *tail; void *(*dup)(void *pt…
redis中整数集合intset相关的文件为:intset.h与intset.c intset的所有操作与操作一个排序整形数组 int a[N]类似,只是根据类型做了内存上的优化. 一.数据结构 typedef struct intset { uint32_t encoding; uint32_t length; int8_t contents[]; } intset; intset的数据结构比较简单,使用了一个变长结构体,成员length记录当前成员数量,成员encoding记录当前的int类…