redis数据(key-value),value往往是对象的形式存在(redisObject)。1.对象1.1.数据结构typedefstructredisObject{//对象类型unsignedtype:4;//对象编码类型unsignedencoding:4;//对象操作时间unsignedlru:LRU_BITS;/*LRUtime(relativetogloballru_clock)or*LFUdata(leastsignificant8bitsfrequency*andmostsignificant16bitsaccesstime).*///使用计数intrefcount;//数据
整数集合,是一个有序的数值数组对象,存储的数值不允许重复。源码在intset.c1.数据结构/*Notethattheseencodingsareordered,so:*INTSET_ENC_INT16<INTSET_ENC_INT32<INTSET_ENC_INT64.*/#defineINTSET_ENC_INT16(sizeof(int16_t))#defineINTSET_ENC_INT32(sizeof(int32_t))#defineINTSET_ENC_INT64(sizeof(int64_t))typedefstructintset{uint32_tencoding
张铁蕾的博客将skiplist原理和算法复杂度描述得很清楚,具体可以参考。我分享一下自己对部分源码的阅读情况和思考。1.数据结构跳跃表是一个有序的双向链表。理解zskiplistNode的zskiplistLevel是理解zskiplist工作流程的关键。/*ZSETsuseaspecializedversionofSkiplists*/typedefstructzskiplistNode{sdsele;doublescore;structzskiplistNode*backward;structzskiplistLevel{structzskiplistNode*forward;unsign
压缩列表ziplist是一个双向链表,设计主要是为了节省内存。保存字符串,数值两种类型(Itstoresbothstringsandintegervalues),列表内部实现主要是对一块连续内存进行管理,列表支持列表头尾的插入或弹出结点操作。因为写操作涉及到内存重新分配,所以复杂度需要根据当前使用内存的使用情况而定,一般情况下,不建议存储大量数据。sortedset根据数据长度,就分别用ziplist和skiplist两种数据结构进行保存。Theziplistisaspeciallyencodedduallylinkedlistthatisdesignedtobeverymemoryeffic
redis的链表实现不是很复杂,从listNode可以知道,list是一个双向链表,支持从链表首尾两边开始遍历结点。同时提供了listIter迭代器,方便前后方向迭代遍历。其它应该就是链表增删改查的一些常规操作了。1.文件adlist.h,adlist.c2.数据结构2.1.链表结点typedefstructlistNode{structlistNode*prev;structlistNode*next;void*value;}listNode;2.2.链表迭代器typedefstructlistIter{listNode*next;intdirection;}listIter;2.3.链表t
redis内存管理实现,有三种方案:jemalloc(谷歌)tcmalloc(facebook)libc(系统)其中jemalloc,tcmalloc是第三方的实现,libc的实现做了一些简单的封装。1.内存池方案//理解宏对相关库的引入使用。#ifdefined(USE_TCMALLOC)#defineZMALLOC_LIB("tcmalloc-"__xstr(TC_VERSION_MAJOR)"."__xstr(TC_VERSION_MINOR))#include<google/tcmalloc.h>#if(TC_VERSION_MAJO
为了节省内存空间,灵活处理不同长度范围的字符串,redis定义了几种sdshdr(X)数据结构,对不同长度的字符串数据进行存储。1.数据结构为了节省内存空间,灵活处理不同长度范围的字符串redis定义了sdshdr(X)几种数据结构,可以查看数据结构大小。同时struct数据结构没有进行内存对齐。typedefchar*sds;#defineSDS_HDR(T,s)((structsdshdr##T*)((s)-(sizeof(structsdshdr##T))))#defineSDS_HDR_VAR(T,s)structsdshdr##T*sh=(void*)((s)-(sizeof(str
redis是key-value的NoSQL数据库,dict是基础数据结构,dict总体来说是一个哈希表,哈希表O(1)的时间复杂度,能高效进行数据读取。dict还有动态扩容/缩容功能,能灵活高效地使用机器内存。因为redis是单进程服务,所以当数据量很大时,扩容/缩容这些内存操作,涉及到新内存重新分配,数据拷贝,必然会影响服务质量。redis作者采用了渐进方式,将一次性操作,分散到dict对应的各个增删改查操作里,每个操作触发有限的数据迁移。所以dict会有两个哈希表(dicththt[2];),相应的rehashidx迁移位置,方便数据迁移。1.数据结构设计图来源:《redisdict字典数
上一章简述了主从复制的基本配置,以及抓包查看了数据复制的一些工作流程,本章将通过源码去深入分析redis主从节点数据复制的要点逻辑:复制命令,master服务副本ID,复制偏移量,积压缓冲区。redis是纯c源码,阅读起来实现比较清晰,缺点:它也是一个异步的网络框架,回调处理的逻辑理解有点费劲。1.数据结构1.1.redisServerredismaster/slave节点数据结构redisServer。#defineCONFIG_RUN_ID_SIZE40structredisServer{...list*slaves,*monitors;/*ListofslavesandMONITORs*
redis主从模式作用:多节点协调工作保证服务高可用;读写分离,提高系统负载能力;多节点保存数据副本,确保数据安全性。主从模式,需要实现数据同步,master节点将数据(全量/增量)复制到链接它的slave节点。redis为了保证节点高性能,采用了异步的数据复制方式,高效地实现了数据的最终一致,并非强一致。那么接下来,将分两个章节来探索redis数据复制的核心工作流程。详细源码分析,请参考下一章1.复制架构#Master-Replicareplication.UsereplicaoftomakeaRedisinstanceacopyof#anotherRedisserver.Afewthing
redis服务底层采用了异步事件管理(aeEventLoop):管理时间事件和文件事件。对大量网络文件描述符(fd)事件管理,redis建立在安装系统对应的事件驱动基础上(例如Linux的epoll)。关于事件驱动,本章主要讲述Linux系统的epoll事件驱动。关于事件处理,本章主要讲述文件事件,时间事件可以参考帖子《[redis源码走读]事件-定时器》。1.事件驱动redis根据安装系统选择对应的事件驱动。//ae.c/*Includethebestmultiplexinglayersupportedbythissystem.*Thefollowingshouldbeorderedbype
很多朋友以为Redis是单线程程序,事实上它是多进程+多线程混合并发模型。子进程持久化:重写aof文件/保存rdb文件。多线程:主线程+后台线程+新增网络IO线程(Redis6.0)。本文使用的Redis版本:6.0.20。1.并发模型Redis使用了多进程+多线程混合并发模型。子进程持久化:重写aof文件/保存rdb文件。多线程:主线程+后台线程+新增网络IO线程。2.多进程子进程持久化:重写aof文件/保存rdb文件。为了性能和安全,一般情况下,Redis同一时刻只允许创建一个子进程在工作。bgsave命令,保存rdb文件。//rdb.cintrdbSaveBackground(intre
重点描述服务异步通信核心非阻塞+异步事件驱动。事件驱动核心源码ae.c网络通信核心源码connection.h/connection.c,networking.h/networking.c读/写数据核心函数readQueryFromClient/writeToClient本文主要讲述Linux平台下的redis客户端与服务端异步通信(单线程),不包括redis集群间的通信。1.异步服务工作流程redis客户端与服务端异步通信流程,整体逻辑有点复杂,先看看流程图,后面再抓抓重点。流程图来源:《redis异步网络通信流程-单线程》2.非阻塞2.1.socket非塞设置redis客户端与服务端通过T
Redis6.0版本增加了多线程并发处理网络IO功能,主要是为了利用多核资源,减轻主线程负载,提高程序整体性能。1.架构1.1.整体框架Redis是多进程+多线程混合并发模型。子进程持久化:重写aof文件/保存rdb文件。多线程:主线程+后台线程+新增网络IO线程(Redis6.0)。详细参考:《[Redis]浅析Redis并发模型》1.2.网络IO框架1.2.1.主线程异步网络IO下图描述了Redis客户端与服务端主线程异步通信流程,有兴趣的朋友可以参考:《[redis源码走读]异步通信流程-单线程》,这里不详细展开了。1.2.2.多线程网络IO框架Redis6.0以前,主线程处理网络IO;
redis服务底层采用了异步事件管理(aeEventLoop):管理时间事件和文件事件。对大量网络文件描述符(fd)事件管理,redis建立在安装系统对应的事件驱动基础上(例如Linux的epoll)。关于事件驱动,本章主要讲述Linux系统的epoll事件驱动。关于事件处理,本章主要讲述文件事件,时间事件可以参考帖子《[redis源码走读]事件-定时器》。1.事件驱动redis根据安装系统选择对应的事件驱动。//ae.c/*Includethebestmultiplexinglayersupportedbythissystem.*Thefollowingshouldbeorderedbype
定时器是redis异步处理事件的一个十分重要的功能。redis定时器功能由多个时间事件组成,事件由一个双向链表维护。时间事件可以处理多个定时任务。理解redis定时器,我们带着问题,看看redis是怎么处理的:定时器作用是什么。定时器实现原理。单进程里如何同时处理文件事件和时间事件。如何实现多定时任务。1.作用redis定时器核心逻辑在serverCron函数里。对设置了过期时间的数据进行检查回收。异步回收需要关闭的链接(socket)。检查fork的子进程是否已经关闭,处理回收的相关工作。检查内存数据是否符合rdb持久化快照落地条件,fork子进程进行快照保存。bgsaverdb生成快照或b
redis是内存数据库,可以通过redis.conf配置maxmemory,限制redis内存使用量。当redis主库内存超出限制时,命令处理将会触发数据淘汰机制,淘汰(key-value)数据,直至当前内存使用量小于限制阈值。1.数据淘汰策略概述redis.conf配置描述maxmemory将内存使用限制设置为指定的字节数。redis申请和回收内存基本上都是通过zmalloc接口统一管理的,可以通过接口统计redis的内存使用量。当redis超出了内存的使用限制maxmemory,服务在处理命令时会触发redis内部的数据淘汰机制。淘汰目标数据一共有两种:数据库所有(key-value)数据
redis可能存在大量过期数据,一次性遍历检查不太现实。redis有丰富的数据结构,key-value,value数据结构对象(redisObj)可能存储大量数据,key过期了,value也不建议在进程中实时回收。为了保证系统高性能,每次处理一点点,逐渐完成大任务,“分而治之”这是redis处理大任务的一贯作风。1.流程主服务检查过期/删除过期逻辑->删除过期键值->异步/同步删除数据->主从同步。设计图来源:《redis过期数据淘汰流程》redis数据库,数据内容和过期时间是分开保存。expires保存了键值对应的过期时间。typedefstructredisDb{dict
哨兵模式的redis集群,假如原来只有一个主服务,经过故障转移后,产生多个主服务,这样脑裂现象出现了。通过合理部署配置sentinel/master,可以降低脑裂问题出现的概率。1.原理1.1.概述哨兵模式的redis集群有三种角色:sentinel/master/slave,它们通过tcp链接,相互建立联系。sentinel作为高可用集群管理者,它的功能主要是:检查故障,发现故障,故障转移。1.2.故障转移流程在redis集群中,当sentinel检测到master出现故障,那么sentinel需要对集群进行故障转移。当一个sentinel发现master下线,它会将下线的master确认为
redis是否使用了raft一致性算法呢?使用了,但不是严格意义上的raft,然而raft算法的核心要点:领导者选举,日志复制,安全性,你都可以在redis中找到相似的实现。下面将探索一下redis关于raft算法的有关实现。1.raft算法1.1.概念Raft是一种用于分布式一致性的共识算法。它被设计用于在分布式系统中实现容错性,并确保系统中的所有节点达成一致的状态。Raft算法由Stanford大学的DiegoOngaro和JohnOusterhout于2013年提出,并已成为分布式系统领域中的重要研究课题。Raft算法的目标是提供一种易于理解和实现的共识算法,以替代复杂的Paxos算法。
sentinel监控管理redis节点,那么我们如何感知sentinel的动作?sentinel为我们提供了很多途径:详细请参考官方文档《RedisSentinelDocumentation》日志。事件的发布订阅,用户向sentinel订阅感兴趣事件。脚本通知sentinelnotification-script<master-name><script-path>。也提供了命令,提供client获取信息,例如SENTINELget-master-addr-by-name<mastername>1.日志在配置中可以开启sentinel日志,通过日志查看sent
前面几章已经讲了:redis集群各个角色之间的通信,主客观下线,投票选举;在选举中胜出的哨兵leader,由它来完成redis集群的故障转移:redis实例的角色转换。哨兵故障转移主体流程:master主观下线->master客观下线->选举->leader筛选原master的最优slave->晋升最优slave为新master->通知原master的其它slave链接新master->结束故障转移->更新新master的存储结构关系->旧master重新上线被降级为slave。1.故障转移故障转移由许多环节组成,集群中每个sentinel都有机
投票原理:”先到先得”,每个sentinel机会是对等的,都有投票权利。当前sentienl节点,确认某个master客观下线后,它会主动开启故障转移的选举环节,进行拉票(选自己)和投票(选别人)。经过票数统计,在最新一轮的选举过程中,超过法定数量(一般过半数)的sentinel投票给某个sentinel节点时,那么它就当选leader,选举结束后,由它去执行其它剩余的故障转移步骤。1.选举整体流程下面是伪代码的执行流程。#只有检查到master客观掉线了才会去询问,在确认客观下线了,马上进行多轮选举,直到选出leader。|--main#定时检测处理故障。|--sentinelTimer|-
本章重点走读redis源码,理解sentinel检测master节点的主客观下线流程。redis哨兵集群有3个角色:sentinel/master/slave,每个角色都可能出现故障,故障转移主要针对master:master主观下线-->master客观下线-->投票选举leader-->leader执行故障转移。1.故障转移流程sentinel时钟定时检查监控的各个redis实例角色,是否通信异常。发现master主观下线。向其它sentinel节点询问它们是否也检测到该master已下线。sentinel通过向其它sentinel节点询问,确认master客观下线。进入
上一章简述了哨兵的工作原理。本文通过strace命令抓取sentinel节点的系统调用日志,去熟悉节点的通信流程,更好地理解redis哨兵集群,是如何通过PING/INFO/PUBLISH/SUBSCRIBE这几个命令,将整个集群节点链接成一个整体的通信系统的。1.工作流程1.1.命令启动命令。下面两个命令都可以启动sentinel进程。redis-sentinel/path/to/your/sentinel.confredis-server/path/to/your/sentinel.conf--sentinel通信命令。structredisCommandsentinelcmds[]={{
redis有主从数据复制功能。多个实例通过读写分离,使得单进程的redis可以充分利用多核性能。当某些redis实例出现故障怎么办,服务还能正常工作吗?这时候故障管理者sentinel应运而生。它负责redis集群管理工作:检查故障,发现故障,转移故障,从而保证集群高可用。1.sentinel作用监控:检查redis节点健康状况。故障转移:当redis集群节点出现故障时,及时自动进行故障转移。通知:检测到redis实例出现故障,通过api进行通知用户。提供配置:用户可以通过命令查询当前redis集群相关信息。2.集群2.1.角色关系redis高可用集群,有三种角色:master,slave,s
aof和rdb是redis持久化的两种方式。我们看看它们的特点和具体应用场景区别。1.持久化特点1.1.aofaof是写命令追加到持久化文件的方式。aof支持几种持久化策略,其中每秒数据增量存盘一次效率比较高。aof支持rdb混合型存储(需要重写处理)。aof一定程度上记录了redis的写操作流水,一段时间内文件冗余数据比较大需要重写解决问题。1.2.rdbrdb快照,一个时间点的redis内存数据全盘落地(快照)。rdb文件是二进制数据压缩文件,数据落地速度快(相对),体积小。因为redis内存是全部数据落地,操作频率不能太高,通过配置持久化频率,几分钟到几小时不等。2.使用场景区别根据ao
aof(AppendOnlyFile)是redis持久化的其中一种方式。服务器接收的每个写入操作命令,都会追加记录到aof文件末尾,当服务器重新启动时,记录的命令会重新载入到服务器内存还原数据。这一章我们走读一下源码,看看aof持久化的数据结构和应用场景是怎样的。主要源码逻辑在aof.c文件中。在了解redis持久化功能前,可以先看看redis作者这两篇文章:RedisPersistenceRedispersistencedemystified链接可能被墙,可以用国内搜索引擎搜索下对应的文章题目。1.开启aof持久化模式可以看一下redis.conf有关aof持久化配置,有redis作者丰富的
文章重点讲述aof持久化的应用场景。aof持久化,拆分上下为两章,可以先读上一章。1.应用场景1.1.启动加载redis启动,程序会模拟一个客户端加载从aof文件读出的命令。aof持久化支持aof和rdb混合模式,参考上面的aof和rdb混合结构intmain(intargc,char**argv){...loadDataFromDisk();...}voidloadDataFromDisk(void){...if(server.aof_state==AOF_ON){if(loadAppendOnlyFile(server.aof_filename)==C_OK)serverLog(LL_NO
rdb文件是一个经过压缩的二进制文件,上一章讲了rdb持久化-应用场景,本章主要讲述rdb文件的结构组成包含了哪些数据。1.rdb临时文件redis内存数据异步落地到临时rdb文件,成功存储后,临时文件覆盖原有文件。/*flagsonthepurposeofrdbsaveorload*/#defineRDBFLAGS_NONE0#defineRDBFLAGS_AOF_PREAMBLE(1<<0)#defineRDBFLAGS_REPLICATION(1<<1)#defineREDIS_AUTOSYNC_BYTES(1024*1024*32)/*fdatasyncever