深入浅出Redis:功能、特性及核心实现

深入浅出系列

深入浅出Redis:功能、特性及核心实现

在如今的分布式系统、高并发场景中,Redis绝对是绕不开的存在——它既是高性能的缓存中间件,也是灵活的键值数据库,甚至能承担消息队列、分布式锁等多种角色。很多开发者日常都会用Redis,但大多停留在“set/get”的基础用法,对它为什么快、支持哪些核心场景、背后靠什么架构和算法实现这些特性,却了解不深。今天就来一篇干货,从核心定位切入,逐步拆解Redis的功能、特性、架构、算法,结合版本演进和典型应用,帮大家真正读懂Redis的核心技术体系。

一、核心定位与功能概览

要真正理解Redis,首先要明确它的核心定位——它并非单纯的缓存工具,而是一款融合了内存数据库、数据结构服务器、高性能键值存储的多面手,同时支持持久化、高可用和分布式扩展,适配从单机到集群、从简单缓存到复杂业务的全场景需求。

1. 基础定位

A. 内存数据库(In-Memory Data Store):所有核心数据存储在内存中,这是其高性能的核心基础,同时通过持久化机制避免数据丢失;

B. 数据结构服务器(Data Structure Server):区别于传统键值存储,原生支持多种复杂数据结构,可直接支撑复杂业务逻辑,无需额外数据转换;

C. 支持持久化、高可用、分布式的高性能键值存储:兼顾性能与数据安全,支持主从复制、哨兵、集群等部署模式,可灵活扩展,适配高并发、海量数据场景。

2. 核心功能

Redis的功能覆盖多个业务域,形成完整的功能矩阵,可根据场景灵活选用,具体如下:

A. 缓存功能:主要用于热点数据加速、会话存储和页面缓存,能够有效缓解后端数据库压力;

B. 消息队列功能:支持Pub/Sub(发布/订阅)和Streams(流处理)两种模式,分别适配轻量级通知和复杂消息队列场景;

C. 实时计算功能:可实现计数器、排行榜、限流器、地理位置计算等需求,支撑实时数据处理;

D. 会话管理功能:能够实现分布式Session和Token黑名单管理,解决分布式系统会话一致性问题;

E. 全栈数据结构支持:支持String、Hash、List、Set、Sorted Set、Bitmap、HyperLogLog、Geo、Stream等多种类型,可适配各类不同的业务场景。

二、Redis核心功能:基于功能矩阵的详细拆解

结合核心功能矩阵,将Redis的核心功能进一步拆解,明确各功能的实现方式、底层支撑和典型应用,让每个功能的价值和用法更清晰:

1. 数据结构服务器(核心基础功能)

作为核心的数据结构服务器,Redis支持多种丰富的数据类型,远超传统键值存储,可直接支撑复杂业务逻辑,无需额外进行数据转换,提升开发效率,每种数据类型均对应特定的底层实现和业务场景,具体如下:

A. String(字符串):底层实现为SDS(Simple Dynamic String),采用预分配+惰性释放的核心机制,能实现O(1)获取长度且支持二进制安全,典型应用场景包括用户Token、验证码、商品库存、实时计数等;

B. Hash(哈希):小数据量时采用ziplist实现,大数据量时切换为hashtable,通过渐进式rehash和负载因子控制避免阻塞,适合存储用户信息、商品详情等对象类数据;

C. List(列表):3.2版本及以上采用quicklist实现,该结构由ziplist和双向链表组合而成,通过压缩节点减少内存占用,支持两端O(1)插入和删除操作,可用于消息队列、最新消息展示(如朋友圈点赞)等场景;

D. Set(集合):整数小集合时采用intset编码,大数据量时使用hashtable,通过整数编码优化提升性能,支持交集、并集、差集运算,适用于好友去重、共同好友、标签匹配等需求;

E. Sorted Set(有序集合):小数据量时用ziplist,大数据量时采用跳表(skiplist)结合hashtable的方式,跳表实现O(logN)的范围查询,哈希表实现O(1)的分值查询,是实时排行榜、热度排序(如视频播放量)的核心支撑;

F. Bitmap(位图):底层基于String(SDS)实现,通过位运算完成SETBIT/GETBIT操作,且操作效率为O(1),占用内存极少,适合用户签到、在线状态、布尔型统计等场景;

G. HyperLogLog:采用稀疏/密集编码的概率算法,基数统计误差仅为0.81%,且固定占用12KB内存,主要用于独立访客(UV)、独立IP统计等海量数据基数统计场景;

H. Geo(地理位置):底层依赖Sorted Set,通过GeoHash编码将二维坐标转换为一维分数,支持距离计算,可实现外卖、打车、社交等场景的附近地点查询;

I. Stream(流):底层实现为Radix Tree(基数树),支持消息ID自增、消费者组管理和ACK机制,且支持持久化,适用于复杂消息队列、流处理场景。

2. 持久化功能

持久化是Redis避免内存数据丢失的核心功能,提供三种持久化方式,可根据业务对数据安全性和性能的需求灵活选择,具体如下:

A. RDB(快照):基于fork() + COW(写时复制)实现,属于全量快照模式,生成的二进制文件紧凑,数据恢复速度快,但缺点是快照之间的新增数据可能丢失;

B. AOF(追加日志):采用命令追加+重写机制(Rewrite),以日志形式存储所有写命令,数据安全性高,支持always、everysec、no三种fsync策略,但存在文件体积大、恢复速度慢的问题;

C. 混合模式(4.0版本及以上支持):结合了RDB和AOF的优势,采用RDB头+AOF尾的结构,重启时先加载RDB快速恢复大部分数据,再重放AOF增量命令,既能保证恢复速度,又能提升数据安全性,是生产环境的首选方案。

关键算法:COW(Copy-On-Write,写时复制)

当Redis需要生成RDB快照或进行主从全量同步时,会通过fork()创建子进程,子进程与主进程共享内存页;当主进程修改数据时,会复制该内存页并修改,子进程继续读取原内存页生成快照,确保快照期间主进程服务不阻塞,兼顾性能与数据一致性。

3. 复制功能

复制功能是Redis实现高可用和读写分离的基础,通过将主节点数据同步到从节点,实现数据冗余,同时分担主节点读压力,核心支持三种机制,具体如下:

A. 全量同步:主节点生成RDB文件传输给从节点,从节点加载RDB后,主节点再发送缓冲区积压的增量命令,完成同步;

B. 部分重同步(PSYNC 2.0):基于复制偏移量和Replication Backlog(复制积压缓冲区),网络闪断后无需全量同步,仅同步中断期间的增量命令,提升同步效率;

C. 无磁盘复制(diskless):主节点生成RDB后,不写入磁盘,直接通过socket传输给从节点,避免磁盘I/O开销,适用于磁盘性能较差的场景。

此外,Redis还支持链式复制(“主-从-从”结构),从节点可作为其他从节点的主节点,减少主节点的复制压力,适用于大规模集群场景。

4. 高可用与集群功能

Redis通过哨兵模式和集群模式,实现高可用和分布式扩展,避免单点故障,支撑海量数据和高并发场景,核心分为三大模块,具体如下:

A. 主从复制(Replication):基础高可用方案,采用“一主多从”架构,主节点负责写操作,从节点负责读操作,实现读写分离,同时通过复制机制保证数据冗余;当主节点故障时,可手动将从节点切换为主节点,保障服务持续可用。

B. 哨兵模式(Sentinel):由多个哨兵节点组成的监控集群,自动实现主从故障检测和转移,无需人工干预,其核心功能及实现机制如下:

故障发现:通过主观下线(SDOWN)和客观下线(ODOWN)判断主节点状态,基于Gossip协议同步节点状态;

领导者选举:基于Raft算法变种,哨兵节点间投票选举出leader哨兵,由leader负责发起故障转移;

配置传播:通过Pub/Sub机制通知所有客户端主从切换信息,确保客户端连接新主节点。

C. 集群模式(Cluster):Redis官方提供的分布式集群方案,采用去中心化设计,支持数据自动分片和故障转移,适配海量数据和高并发场景,其核心特性及支撑架构如下:

数据分片:通过CRC16(key) % 16384算法分配槽位,共16384个slot,每个节点负责一部分槽位存储;

节点通信:基于Gossip协议,节点间通过PING/PONG消息交换状态,实现故障检测和状态同步;

请求路由:通过MOVED/ASK命令重定向请求,Smart Client会缓存槽位映射,减少重定向开销;

故障转移:从节点发起选举,集群多数派(majority)投票确认(基于Raft思想),选举新主节点接管槽位;

在线扩容:通过reshard命令实现槽位迁移,渐进式数据搬迁,不影响集群正常服务。

5. 其他核心功能

除上述核心功能外,Redis还提供一系列实用功能,进一步提升灵活性和扩展性,支撑复杂业务场景,具体如下:

A. 发布/订阅(Pub/Sub):基于频道(Channel)与模式(Pattern)的事件分发机制,支持一对多、多对多的消息推送,适用于轻量级通知场景(如系统公告、实时消息推送),底层基于简单的事件订阅机制实现。

B. 管道(Pipeline):支持批量发送多个命令,减少客户端与Redis服务器的网络交互次数,提升批量操作效率(如批量插入、批量查询),核心是将多个命令打包一次性发送,服务器批量响应,减少网络延迟。

C. Lua脚本:内嵌Lua解释器,支持Lua脚本的原子执行,可将复杂的业务逻辑(如多步命令组合)封装为脚本,在Redis服务端执行,减少网络开销,同时保证逻辑原子性,避免并发修改导致的数据不一致,支持EVAL/EVALSHA命令。

D. 事务:支持MULTI、EXEC、DISCARD等命令,将一组命令打包形成命令队列,实现原子性执行(要么全部执行,要么全部不执行),但不支持回滚,仅保证命令顺序执行;同时支持WATCH命令实现乐观锁,基于CAS(Compare And Swap)语义监控键的变化。

E. 键过期(TTL):支持给任意键设置过期时间,通过多种过期删除策略,自动删除过期键,释放内存,适用于缓存场景(如热点数据自动过期),底层结合惰性删除和定期删除策略实现。

F. 布隆过滤器(通过模块):Redis本身不自带布隆过滤器,需通过RedisBloom等扩展模块实现,基于布隆过滤算法,通过多个哈希函数将元素映射到二进制位,快速判断元素是否存在,误判率可配置,适用于去重、缓存穿透防护等场景。

G. 限流器:可通过Sorted Set实现滑动窗口限流器,或通过Redis Cell模块实现令牌桶限流器,控制接口请求频率,避免高并发压垮后端服务。

三、Redis核心特性与支撑架构/算法

Redis之所以能在众多缓存中间件中脱颖而出,核心在于它的五大核心特点,这些特点相互支撑,让Redis能适配高并发、低延迟、高可用的各类场景,每一个特点背后都有对应的架构和算法深度支撑:

1. 高性能(单线程 10w+ QPS)

Redis的QPS(每秒查询数)可轻松达到10万级别,延迟通常在1ms以内,远优于传统数据库,核心特性与支撑机制如下:

A. 单线程事件循环:Reactor模式 + 非阻塞I/O(epoll/kqueue/evport/select),高效处理多客户端并发请求;

B. 避免上下文切换:单线程执行所有核心命令,无多线程锁竞争和上下文切换开销,提升执行效率;

C. 零拷贝技术:客户端输出缓冲区直接发送数据,减少数据在用户态和内核态之间的拷贝,提升传输效率;

D. 高效序列化:采用RESP协议(Redis Serialization Protocol),简单文本格式+二进制安全,解析速度快。

核心架构:Reactor事件驱动模型

Redis的高性能核心依赖Reactor事件驱动模型,整体流程如下:

客户端请求 → 多路复用器(epoll) → 事件分发器 → 命令处理(单线程) → 返回结果

多路复用器监听所有客户端连接的I/O事件,当某个连接有事件(如数据到达)时,事件分发器将事件分发到对应的处理模块,由单线程执行命令处理,处理完成后将结果返回给客户端,整个过程无阻塞,高效处理高并发请求。

2. 丰富的数据结构

Redis的核心优势之一是支持多种全栈数据结构,每种数据结构都有针对性的底层实现和算法,兼顾查询效率和内存开销,核心设计思路是通过自适应底层实现(如小数据量用紧凑存储、大数据量用高效查询结构),在不同场景下平衡性能和内存利用率,具体细节可参考“数据结构服务器”模块。

3. 持久化机制

Redis提供RDB、AOF、混合持久化三种方式,核心目标是兼顾数据安全和性能,核心支撑是COW(写时复制)算法和AOF重写机制,确保持久化过程不阻塞主进程服务,具体细节可参考“持久化功能”模块。

4. 高可用与分布式

Redis通过主从复制、哨兵模式、集群模式三层架构,实现高可用和分布式扩展,核心支撑算法包括Raft变种、Gossip协议、CRC16哈希槽分配等,确保集群稳定运行、数据均匀分布、故障自动转移,具体细节可参考“高可用与集群功能”模块。

5. 内存管理与优化

Redis的内存管理机制,核心是“高效利用内存、避免内存溢出”,通过多种算法和策略,优化内存占用和回收效率,具体如下:

A. 过期键删除:采用惰性删除(访问时检查)+ 定期删除(随机采样)的组合策略,平衡CPU占用和内存开销;

B. 内存淘汰:支持8种策略,包括LRU、LFU、Random、TTL等,采用近似LRU算法(随机采样5个取最久未使用),兼顾性能和效果;

C. 内存分配:默认采用jemalloc(可选tcmalloc),支持多种内存大小分配,有效减少内存碎片;

D. 对象编码:根据数据量自动进行编码转换(如ziplist → hashtable),节省小对象内存占用;

E. Intset编码:采用整数集合存储小整数,根据整数范围自动选择int16/int32/int64编码,提升内存利用率。

6. 事务与Lua脚本

Redis通过事务和Lua脚本,实现复杂业务逻辑的原子性执行,具体实现如下:

A. 事务:通过MULTI、EXEC、DISCARD命令,将命令放入队列批量执行,具有原子性(无回滚);同时通过WATCH命令实现乐观锁,监控键的变化,基于CAS(Compare And Swap)语义保证数据一致性;

B. Lua脚本:内嵌Lua解释器,脚本在同一线程内原子执行,支持EVAL/EVALSHA命令,可封装复杂业务逻辑,减少网络开销并保证原子性。

7. 多线程演进(6.0+)

Redis核心命令执行一直采用单线程模型,从6.0版本开始引入多线程优化,主要针对I/O操作,进一步提升网络吞吐能力,版本演进如下:

6.0版本:引入多线程I/O,命令解析和结果写回采用多线程,核心命令执行仍保持单线程,减少I/O阻塞,提升网络吞吐;

7.0版本:优化多线程I/O性能,新增函数库(Functions)持久化,进一步提升Redis的性能和扩展性。

多线程架构流程:

网络读 → 多线程解析 → 单线程执行命令 → 多线程序列化/发送

核心设计思路:保持核心命令单线程执行(避免锁竞争),将耗时的I/O操作(网络读、解析、写回)交由多线程处理,平衡性能和复杂度。

四、Redis核心算法总结

Redis的每一个核心功能和特性,都依赖于高效的算法支撑,这些算法是Redis高效、灵活、高可用的“灵魂”,核心算法及其应用场景如下:

A. 跳表(Skip List):主要应用于Sorted Set的范围查询,实现O(logN)的插入、查询效率,支撑实时排行榜等场景;

B. Radix Tree(基数树):用于Stream消息索引和IP路由表,支持高效的前缀查询和范围查询;

C. LRU/LFU 近似算法:用于内存淘汰策略,筛选需要删除的键,平衡性能和内存利用率;

D. HyperLogLog 概率算法:用于海量数据基数统计(如UV),固定内存占用,允许微小误差(0.81%);

E. GeoHash:用于地理位置索引,将二维经纬度转为一维分数,实现附近地点查询和距离计算;

F. Raft 变种:用于Sentinel领导选举和Cluster故障转移,确保高可用决策的一致性;

G. Gossip 协议:用于集群节点状态传播和故障检测,实现去中心化的集群管理;

H. COW(写时复制):用于RDB持久化和主从全量复制,确保操作期间服务不阻塞;

I. CRC16:用于Cluster槽位计算,将key映射到对应的哈希槽,实现数据分片;

J. 渐进式rehash:用于Hash、Set等数据结构的哈希表扩容,避免一次性扩容阻塞主线程。

五、Redis版本演进

Redis的发展历程中,多个版本推出了里程碑式的特性,这些特性不断完善Redis的功能、性能和扩展性,核心版本的关键特性如下:

2.6版本:引入Lua脚本,支持毫秒级过期时间,提升业务灵活性;

2.8版本:实现PSYNC部分重同步,新增Scan命令,优化主从复制和数据遍历效率;

3.0版本:Cluster集群正式版上线,原生支持分布式分片和高可用;

3.2版本:新增Geo地理位置功能,支持Lua脚本调试,用quicklist替代List旧实现;

4.0版本:引入混合持久化、模块系统、异步删除,提升数据安全和性能;

5.0版本:新增Stream数据类型,完善消息队列功能,支持消费者组和ACK机制;

6.0版本:引入多线程I/O、SSL加密、ACL访问控制,提升网络吞吐和安全性;

7.0版本:新增Functions函数库、Sharded Pub/Sub,优化多线程I/O,进一步提升性能和扩展性。

六、Redis典型应用场景

基于Redis的核心功能和特性,它在实际业务中有着广泛的应用,覆盖缓存、消息、会话、实时计算等多个场景,具体如下:

A. 缓存层:配合TTL过期策略和内存淘汰策略,缓存热点数据(如商品详情、用户信息),缓解后端数据库压力,提升接口响应速度,是Redis最常用的场景;

B. 分布式锁:通过SETNX命令+Lua脚本实现分布式锁,或使用Redlock算法(存在争议),解决分布式系统中多节点并发竞争问题(如秒杀、库存扣减);

C. 限流器:通过Sorted Set实现滑动窗口限流器,或通过Redis Cell模块实现令牌桶限流器,控制接口请求频率,避免高并发请求压垮后端服务;

D. 实时排行榜:基于Sorted Set的跳表结构,实现实时热度排序(如视频播放量、商品销量排行榜),支持范围查询和排名更新;

E. 消息系统:通过Stream数据类型实现复杂消息队列,支持消费者组、消息持久化、ACK确认机制,可替代轻量级MQ,适配中小规模消息场景;Pub/Sub适用于轻量级通知场景;

F. 会话存储:用Hash数据类型存储用户会话信息,设置过期时间自动清理,解决分布式Web系统中会话一致性问题(如电商平台、后台管理系统);

G. 布隆过滤器:通过RedisBloom模块实现,用于概率去重(如用户行为去重、黑名单过滤),避免缓存穿透,提升系统性能;

H. 实时统计:通过Bitmap实现用户签到、在线状态统计,通过HyperLogLog实现UV统计,通过计数器实现文章阅读量、商品销量实时计数;

I. 地理位置服务:基于Geo功能,实现附近商家、附近好友查询,适配外卖、打车、社交等本地生活场景。

七、总结:Redis的高效密码

看到这里,相信大家已经明白:Redis的高效、灵活、高可用,并非偶然,而是“核心定位+功能矩阵+架构设计+算法优化+版本演进”共同作用的结果。

简单总结一下:Redis以“内存数据库+数据结构服务器”为核心定位,构建了覆盖缓存、消息、实时计算、会话管理的全栈功能矩阵;以Reactor事件驱动模型、单线程核心+多线程I/O为架构基础,实现极致性能;以跳表、Raft、Gossip、COW等核心算法为支撑,保障功能的高效实现;通过版本迭代不断完善特性,适配更多业务场景;最终在实际业务中,成为分布式系统、高并发场景中不可或缺的核心组件。

对于开发者来说,了解Redis的核心技术体系,不仅能帮助我们更好地使用Redis(如根据场景选择合适的数据类型、配置最优的持久化和淘汰策略、优化命令执行效率),还能让我们在遇到问题时(如Redis卡顿、内存溢出、集群故障),快速定位根源,找到解决方案。

如果觉得这篇文章对你有帮助,欢迎点赞、收藏,也可以在评论区留言,聊聊你在使用Redis时遇到的问题~

【温故知新】Redis04性能瓶颈与优化

Redis性能瓶颈与优化

一、核心瓶颈
主要限制:内存容量(单节点内存上限)、网络带宽(高并发下网络I/O瓶颈)、单核CPU性能(单线程主线程瓶颈)。

二、优化方案
1、软件层优化
版本选择:使用Redis 6.0+版本,开启网络I/O多线程,根据服务器配置合理设置线程数,突破网络瓶颈。
Key优化:避免大Key(拆分大Hash、大List),定期扫描大对象,减少单命令耗时;合理设计键名与数据结构,适配动态编码优化。
命令优化:使用Pipeline批量操作、Lua脚本、事务,减少网络往返次数;避免频繁执行耗时命令(如keys、hgetall),替换为高效替代命令。
持久化优化:合理配置持久化策略,缓存场景可关闭AOF或采用everysec刷盘;主从复制场景开启无磁盘复制,减少IO开销;定期清理过期键,优化内存使用。

2、系统层优化
内存优化:禁用透明大页(THP),避免内存碎片增加;选用jemalloc/tcmalloc内存分配器,提升内存分配效率。
CPU优化:CPU绑核,将Redis进程绑定到固定CPU核心,减少缓存失效,提升CPU利用率。
网络优化:优化TCP配置,调整连接超时时间、缓冲区大小,提升网络传输效率;避免网络带宽过载,合理规划集群节点网络。

三、性能监控
慢查询日志:配置合理的慢查询阈值,记录耗时命令,分析命令执行效率,优化耗时操作。
延迟监控:利用Redis内置延迟钩子,统计99分位延迟数据,实时掌握服务响应延迟情况,及时发现性能瓶颈。
内存分析:通过INFO命令获取多维内存统计数据,扫描大对象,分析内存占用情况,优化内存使用效率,避免内存溢出。

【温故知新】Redis03稳定性

Redis稳定性

一、单机稳定性
1、持久化机制(数据安全保障)
混合持久化(默认开启):结合RDB与AOF优势,AOF文件头部存储RDB全量数据,尾部存储增量命令,兼顾RDB恢复速度快与AOF数据全的特点,规避单一持久化短板。
RDB快照持久化:通过fork子进程+写时复制(Copy-on-Write)机制,对内存数据做全量快照,写入.rdb二进制文件,恢复速度快;支持定期备份与灾备,配置灵活,开启stop-writes-on-bgsave-error保护,避免备份失败导致数据丢失;配合完善的数据恢复机制,保障数据安全。
AOF日志持久化:采用命令追加模式,将所有写命令追加到.aof文件,恢复时通过重放命令还原数据,实现实时日志备份;支持三种可配置刷盘策略(always每个命令fsync最安全、everysec每秒后台fsync默认、no由OS决定最性能),兼顾数据安全与性能;AOF重写机制可合并冗余命令、压缩历史日志,fork子进程执行,不阻塞主线程,实现持久化异步化。

2、容错与安全机制(减少异常影响)
内存安全:maxmemory限制内存容量,配合过期删除与内存淘汰策略,避免内存溢出;大Key检测规避单命令阻塞;写时复制减少内存占用,保障内存合理利用。
并发安全:单线程命令原子执行,避免并发不一致;Lua脚本、事务机制进一步强化原子性,支持复杂业务场景的并发控制。
权限与监控:密码认证、ACL权限控制,防止非法操作;慢查询日志(阈值可配置)记录耗时命令,便于排查性能隐患;内置监控机制,支持实时掌握服务运行状态。
架构优势:简单架构降低故障率,减少并发Bug,便于调试与维护;核心组件设计简洁,依赖少,进一步提升服务稳定性。

二、高可用架构
1、主从复制
核心机制:采用主写从读架构,从节点异步复制主节点数据,实现数据冗余与读写分离,减少主节点负载压力。
同步方式:支持全量同步(基于RDB快照)与增量同步(基于AOF日志),通过部分重同步PSYNC2机制(依托复制积压缓冲区与主从RunID匹配),减少同步数据量,提升同步效率。
优化设计:支持无磁盘复制(diskless),避免持久化文件写入磁盘带来的IO开销;主节点宕机时,可实现故障自动转移,保障服务不中断。

2、哨兵模式
核心作用:通过独立的哨兵进程,实现主从节点状态监控、自动故障检测、主节点选举与配置自动更新,无需人工干预,实现故障自愈。
故障检测:采用主观下线(SDOWN,单哨兵检测节点异常)与客观下线(ODOWN,多哨兵共识确认异常)相结合的方式,避免误判,确保故障检测准确性。
高可用保障:多哨兵部署,避免哨兵单点故障;主节点宕机后,通过Raft算法选举最优从节点升为主节点,自动更新集群配置,快速恢复服务。

3、Cluster集群(横向扩展)
架构设计:去中心化架构,通过16384个哈希槽实现数据分片存储,支持多主多从部署,节点间通过Gossip协议进行状态同步,通信规范高效。
扩展能力:支持动态增删节点,解决单节点内存上限问题,实现横向扩展,适配业务增长带来的数据量提升需求。
容错机制:主节点宕机后,其从节点自动补位,哈希槽自动迁移;支持故障检测与迁移,多节点宕机时,只要哈希槽全覆盖,就不影响整体服务可用性。

【温故知新】Redis02核心数据结构与底层算法

Redis数据结构及算法

一、基础对象结构
核心对象:所有数据类型均基于redisObject统一封装,包含五大核心属性:type(数据类型)、encoding(底层编码)、ptr(数据指针)、refcount(引用计数)、lru(淘汰标识)。
设计优势:支持动态编码,可根据数据量、数据类型按需切换底层实现,兼顾性能与内存利用率;通过引用计数实现内存自动回收,支持对象共享,进一步提升内存利用率。

二、动态编码优化
核心原则:同一数据类型根据数据量、数据特性,自动切换底层编码,支持编码升级与降级,针对不同场景优化,减少内存碎片,提升算法效率。
1、String
底层采用SDS动态字符串(主力)与int整数编码(小整数场景),适配短字符串常用场景。
2、List
小数据量用压缩列表(ziplist),大数据量转快速链表(quicklist),同时支持listpack紧凑列表,解决ziplist级联更新问题。
3、Hash
小对象用ziplist,大数据量转Dict字典,平衡内存与查询性能。
4、Set
整数小集合用intset,字符串/大集合用hashtable,实现高效去重。
5、ZSet
小数据量用ziplist,大数据量用skiplist跳表+hashtable组合,兼顾排序与查询效率。

三、核心数据结构
1、SDS动态字符串
核心特性:二进制安全,支持预分配冗余空间减少扩容开销,采用惰性释放策略降低内存重分配损耗,可实现O(1)时间复杂度获取字符串长度。
优势:规避C字符串短板,适配Redis中字符串的各种使用场景(如键名、字符串值),兼顾性能与灵活性。

2、链表
底层实现:双端无环链表,带有表头指针与长度计数器,便于快速定位链表首尾节点与获取链表长度。
作用:作为部分数据结构的底层支撑,提升插入、删除操作的灵活性,适配需要频繁修改的场景。

3、Dict字典
核心机制:采用链地址法解决哈希冲突,通过渐进式Rehash实现无阻塞扩容,哈希算法选用MurmurHash2,哈希分布均匀,减少冲突概率。
优化设计:双哈希表结构实现平滑扩容,通过负载因子控制扩容时机;渐进式Rehash分批次迁移数据,结合定时任务与读写操作触发迁移,避免阻塞主线程。
性能:单键增删改查操作时间复杂度为O(1),ziplist编码场景下,因紧凑存储(CPU缓存友好,无指针开销)进一步提升效率。

4、压缩列表(ziplist)
核心设计:小数据紧凑存储,采用连续内存块组织数据,元素间无指针开销,大幅节省内存空间,支持快速定位元素。
优化:通过级联更新控制,减少级联更新带来的性能损耗,适配小数据量、高频访问的场景,后续被listpack进一步优化替代。

5、快速链表(quicklist)
核心结构:双向链表与Ziplist节点的结合,通过分段存储平衡内存占用与操作性能,避免纯链表的内存碎片问题。
性能优势:链表两端插入、删除操作时间复杂度为O(1),Ziplist节点紧凑存储提升内存利用率,兼顾灵活性与内存效率。

6、紧凑列表(listpack)
设计目的:替代ziplist,解决ziplist级联更新的性能问题,优化内存占用与遍历效率。
优势:支持倒序遍历优化,紧凑存储节省内存,无级联更新隐患,适配小数据量紧凑存储场景。

7、整数集合(intset)
核心特性:有序存储、无重复元素,支持自动升级策略(int16→int32→int64),根据元素大小动态调整存储类型,最大限度节省内存。
性能:采用二分查找算法,查询效率高,适配纯整数小集合场景,内存利用率远超hashtable。

8、跳表(skiplist)
核心结构:多层索引结构,层级随机晋升概率为1/4,实现简单(无需旋转操作),替代平衡树降低实现复杂度。
性能:平均查询时间复杂度为O(logN),插入、删除操作效率高,支持高效范围查询,结合hashtable实现O(1)时间复杂度获取元素分数,双结构兼顾排序与查询需求。

四、核心底层算法
1、渐进式Rehash
Dict字典核心优化,分批次迁移哈希表数据,结合定时任务与读写操作触发迁移,避免扩容过程中阻塞主线程,保障服务流畅。

2、MurmurHash2
Dict字典核心哈希算法,哈希分布均匀,冲突概率低,计算高效,适配Redis的哈希存储场景。

3、LRU(最近最少使用)
近似实现,通过随机采样key提升效率,作为内存淘汰策略之一,平衡性能与淘汰准确性,配合随机采样池优化淘汰效果。

4、LFU(最不经常使用)
基于访问频率实现,通过24位计数器记录元素访问频率,设置衰减因子防止冷数据永存,适配高频访问场景的内存淘汰需求。

5、HyperLogLog
基数估计算法,用于统计独立元素个数,内存占用极低,标准误差仅0.81%,无需存储全部元素,适配大数据量基数统计场景。

6、GeoHash
地理位置编码算法,将二维经纬度坐标映射到一维字符串,支持距离计算与范围查询,适配地理位置相关业务场景。

五、过期与淘汰策略
1、过期键删除
采用惰性删除与定期删除相结合的方式,平衡CPU与内存开销。惰性删除在访问键时判断是否过期,避免无用删除操作;定期删除通过随机采样删除过期键,控制删除频率,不阻塞主线程;过期键独立存储于哈希表,提升清理效率。

2、内存淘汰策略
共8种,核心分为4类,配合maxmemory配置使用,防止内存溢出:

2.1、LRU
近似算法,通过随机采样key实现,结合随机采样池优化淘汰准确性。

2.2、LFU
基于访问频率,通过24位计数器记录访问频率,设置衰减因子防止冷数据永存。

2.3、volatile系列
仅淘汰设置过期时间的键,适用于需要保留未过期数据的场景。

2.4、allkeys系列
淘汰所有键,适用于缓存场景,优先保留访问频率高的键;同时支持按TTL过期时间淘汰,适配时间敏感场景。

【温故知新】Redis01核心架构

Redis核心架构

一、Redis 核心定位
本质:纯内存KV数据库 + 分布式缓存,采用C语言实现,代码库简洁(约3万行),由核心开发者维护,Bug率极低,支撑服务稳定性。
核心优势:亚毫秒级低延迟、高并发、高可用、多场景适配,依托纳秒级内存读写基础,可实现微秒级响应时间。
设计理念:极致优化内存操作、简化交互、保障数据安全与服务可用,简单架构降低故障率,便于调试和维护。

二、Redis 为什么这么快
架构层优势:单线程事件循环(无锁、无上下文切换开销)+ IO多路复用(Reactor模式,非阻塞、高并发)+ 全内存存储(纳秒级读写,微秒级响应),三者协同,彻底规避磁盘I/O瓶颈与多线程并发损耗,奠定高性能基础。
算法层优势:以O(1)时间复杂度操作为主,结合动态编码(按需切换底层实现)、高效底层算法(跳表、渐进式Rehash、LRU、LFU等),优化操作效率与内存占用,适配不同业务场景。
实现层优势:jemalloc/tcmalloc内存分配器减少内存碎片、提升复用率;RESP精简协议减少编解码与网络传输开销;异步后台线程避免主线程阻塞;Redis 6.0+网络I/O多线程优化,进一步突破网络瓶颈。
细节优化:Pipeline批量操作、零拷贝技术减少网络往返与数据复制开销;高效数据结构设计节省内存与解析开销;单线程无锁设计避免竞态问题;Lua脚本、事务实现命令原子执行,提升并发效率;RDB、AOF持久化优化,不阻塞主线程,兼顾数据安全与性能。

三、单机架构
内存数据库:数据全量存储于内存,实现纳秒级读写,规避磁盘I/O寻道延迟,可高速访问且无需等待磁盘I/O,支持RDB与AOF两种持久化机制。
事件驱动:基于Reactor模式,通过I/O多路复用监听客户端请求,提升并发处理效率,贴合单线程模型的事件驱动核心设计。
持久化双引擎:采用RDB快照(全量备份)+AOF日志(增量备份)的组合,支持混合持久化,兼顾数据安全与恢复效率,后续将详细展开具体实现。

四、运行模型
1、单线程主线程
核心机制:基于Reactor模式的事件驱动,单主线程集中处理所有客户端读写、计算命令,依托事件循环机制实现高效调度。
性能优势:无多线程上下文切换与锁竞争开销(上下文切换耗时可达微秒级),命令原子执行、天然线程安全;无锁设计简化架构,事件处理时间复杂度达到O(1),同时避免竞态条件,简化并发控制。
优化设计:引入异步后台线程,专门处理过期删除、AOF重写等耗时操作,避免阻塞主线程;单线程模型简化整体设计,减少并发相关Bug,降低调试与维护成本。

2、IO多路复用机制
底层实现:优先采用epoll(Linux环境),兼容kqueue/select实现跨平台适配,贴合单线程事件循环设计,支持非阻塞I/O操作。
性能优势:单线程可监听海量客户端连接(多socket),通过非阻塞IO避免空等,实现一核多并发;高效处理大量并发连接的同时,减少系统调用次数,降低性能损耗。
工作逻辑:由内核负责监听多个文件描述符,当有就绪事件(如客户端请求)时,及时通知主线程处理,确保主线程高效响应,不做无用等待。

3、多线程演进(Redis 6.0+)
核心优化:引入网络I/O多线程设计,可根据实际场景配置线程数,仅将网络I/O相关操作(连接建立、数据读写)交由多线程处理。
设计原则:命令执行仍保持单线程,兼顾高并发与命令原子性,既解决网络I/O瓶颈,又避免多线程并发带来的竞态问题,进一步提升网络读写效率。

五、内存架构
1、全内存存储模型
核心设计:所有数据全量存储于内存,读写操作直接作用于内存,数据结构设计紧凑,内存访问速度远超磁盘,无需等待磁盘I/O,实现微秒级响应时间。
性能优势:彻底规避磁盘I/O寻道与读写延迟,多数操作耗时可达O(1),奠定亚毫秒级延迟的基础;内存访问基于纳秒级速度,进一步放大性能优势。
内存优化:采用对象共享与引用计数机制,提升内存利用率;引入内存池管理,减少内存碎片;配合内存预分配与惰性释放策略,优化内存使用效率,降低内存重分配开销。

2、内存分配与管理
分配器选择:默认采用jemalloc内存分配器,也支持tcmalloc,替代C标准malloc,适配Redis的内存使用场景。
优化设计:按内存块大小分块分配,减少内存碎片,提升内存复用率;通过负载因子控制哈希表扩容时机,避免扩容过程中阻塞主线程,优化整体性能。
辅助优化:采用惰性释放策略,避免频繁释放内存带来的性能波动;对部分数据结构进行内存压缩,进一步降低内存占用。

3、内存安全机制
写时复制(Copy-on-Write):持久化过程中,子进程共享主进程内存空间,仅当主进程执行写操作时,才复制对应内存块,大幅减少内存占用,是RDB持久化的核心机制。
大Key检测:实时监控大Key(如单Hash存储百万级字段),避免单命令执行耗时过长阻塞主线程,保障服务流畅运行。
内存限制:通过maxmemory配置最大内存容量,防止内存溢出;配合过期删除与内存淘汰策略,确保内存合理利用,避免服务因内存不足异常。

六、网络架构
通信协议:采用RESP精简序列化协议,协议轻量易解析、支持二进制安全,减少编解码与网络传输开销,提升解析速度。
连接优化:支持非阻塞IO与连接复用,提升客户端连接效率,适配IO多路复用的非阻塞特性;引入零拷贝技术,让客户端缓冲数据直接发送至网络,避免内核态与用户态的数据复制,降低性能损耗。
命令优化:支持Pipeline管道、mset/mget批量命令,减少网络往返次数;支持Lua脚本与Multi/Exec事务,实现命令原子执行,提升并发场景下的执行效率。

Redis分片(Jedis)

Redis的分片技术一般是通过客户端或代理来实现的

1、用jedis实现分片的时候,服务端不需要做任何配置即可

package com.djhu.redis.test;

import java.util.ArrayList;
import java.util.List;

import redis.clients.jedis.JedisShardInfo;
import redis.clients.jedis.ShardedJedis;

public class JedisShardTest
{
	public static void main(String[] args)
	{
		List<JedisShardInfo> jedisShardInfoList = new ArrayList<JedisShardInfo>();
		jedisShardInfoList.add(new JedisShardInfo("172.16.172.4", 6379));
		jedisShardInfoList.add(new JedisShardInfo("172.16.172.4", 6380));

		ShardedJedis sharded = new ShardedJedis(jedisShardInfoList);
		sharded.set("key01", "a");
		sharded.set("key02", "b");
		sharded.set("key03", "c");
		sharded.set("key04", "d");
		sharded.set("key05", "e");
		
		System.out.println(sharded.get("key03"));
	}
}

2、用Jedis连接池实现分片

package com.djhu.redis.test;

import java.util.ArrayList;
import java.util.List;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisShardInfo;
import redis.clients.jedis.ShardedJedis;
import redis.clients.jedis.ShardedJedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.util.Hashing;
import redis.clients.util.Sharded;

public class JedisSharedFactory
{
	// 最大可用连接数,默认值为8,如果赋值为-1则表示不限制
	private static int MAX_TOTAL = 256;
	// 最大空闲连接数,默认值为8
	private static int MAX_IDLE = 32;
	// 最小空闲连接数
	private static int MIN_IDLE = 4;
	// 最大等待连接毫秒数,默认值为-1表示永不超时
	private static int MAX_WAIT = 3000;
	// 连接redis超时时间
	private static int TIMEOUT = 3000;
	// true表示验证连接
	private static boolean TEST_ON_BORROW = true;

	//连接池
	private static ShardedJedisPool jedisPool = null;
	public static void initJedisPool()
	{
		try
		{
			JedisPoolConfig config = new JedisPoolConfig();
			config.setMaxTotal(MAX_TOTAL);
			config.setMaxIdle(MAX_IDLE);
			config.setMinIdle(MIN_IDLE);
			config.setMaxWaitMillis(MAX_WAIT);
			config.setTestOnBorrow(TEST_ON_BORROW);
			
			List<JedisShardInfo> jedisShardInfoList = new ArrayList<JedisShardInfo>();
			jedisShardInfoList.add(new JedisShardInfo("172.16.172.4", 6379));
			jedisShardInfoList.add(new JedisShardInfo("172.16.172.4", 6380));
			jedisPool = new ShardedJedisPool(config, jedisShardInfoList,Hashing.MURMUR_HASH,Sharded.DEFAULT_KEY_TAG_PATTERN);
		} 
		catch (Exception e)
		{
			e.printStackTrace();
		}
	}

	public synchronized static ShardedJedis getConnection()
	{
		try
		{
			if (jedisPool != null)
			{
				ShardedJedis resource = jedisPool.getResource();
				return resource;
			} else
			{
				return null;
			}
		}
		catch (Exception e)
		{
			e.printStackTrace();
			return null;
		}
	}

	public static void returnResource(final ShardedJedis jedis)
	{
		if (jedis != null)
		{
			jedis.close();
		}
	}
	
	public static void main(String[] args)
	{
		initJedisPool();
		ShardedJedis redis = getConnection();
		redis.set("key10", "j");
		redis.set("key11", "k");
		redis.set("key12", "l");
		redis.set("key13", "m");
		redis.set("key14", "n");
		
		System.out.print(redis.get("key12"));
		
		returnResource(redis);
	}
}

Jedis连接Redis3 Cluster

1、源码如下

package com.djhu.redis.test;

import java.util.Set;
import java.util.HashSet;

import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;

public class JedisClusterTest
{
	public static void main(String[] args)
	{
		Set<HostAndPort> jedisClusterNodes = new HashSet<HostAndPort>();  
        jedisClusterNodes.add(new HostAndPort("172.16.172.4", 6379));  
        jedisClusterNodes.add(new HostAndPort("172.16.172.4", 6380));  
        jedisClusterNodes.add(new HostAndPort("172.16.172.4", 6381));  
        jedisClusterNodes.add(new HostAndPort("172.16.172.4", 6382));  
        jedisClusterNodes.add(new HostAndPort("172.16.172.4", 6383));  
        jedisClusterNodes.add(new HostAndPort("172.16.172.4", 7384));  
        
		//JedisCluster cluster = new JedisCluster(jedisClusterNodes,3000,1000);
        JedisCluster cluster = new JedisCluster(jedisClusterNodes);
		cluster.set("key10", "j");
		cluster.set("key11", "k");
		cluster.set("key12", "l");
		cluster.set("key13", "m");
		cluster.set("key14", "n");
		
		System.out.println(cluster.get("key12"));
		
	}
}

2、如果遇到下面错误,主要是因为建立cluster时,ip用了127.0.0.1。用其他ip重建一下cluster,就可以解决了。

Exception in thread "main" redis.clients.jedis.exceptions.JedisClusterMaxRedirectionsException: Too many Cluster redirections?
	at redis.clients.jedis.JedisClusterCommand.runWithRetries(JedisClusterCommand.java:34)
	at redis.clients.jedis.JedisClusterCommand.runWithRetries(JedisClusterCommand.java:68)
	at redis.clients.jedis.JedisClusterCommand.runWithRetries(JedisClusterCommand.java:85)
	at redis.clients.jedis.JedisClusterCommand.runWithRetries(JedisClusterCommand.java:68)
	at redis.clients.jedis.JedisClusterCommand.runWithRetries(JedisClusterCommand.java:85)
	at redis.clients.jedis.JedisClusterCommand.runWithRetries(JedisClusterCommand.java:68)
	at redis.clients.jedis.JedisClusterCommand.run(JedisClusterCommand.java:29)
	at redis.clients.jedis.JedisCluster.set(JedisCluster.java:75)

Redis3配置Cluster

1、redis3的cluster是基于ruby的,所以要安装好ruby,然后安装redis的gem

gem install redis

2、然后配置6份redis,修改配置如下

节点
dbm6379 dbm6380 dbm6381 dbs6382 dbm6383 dbm6384
配置文件 dbm6379/conf/redis.conf dbm6380/conf/redis.conf dbm6381/conf/redis.conf dbs6382/conf/redis.conf dbm6383/conf/redis.conf dbm6384/conf/redis.conf
port 6379 6380 6381 6382 6383 6384
logfile “/home/neohope/DB/redis-3.0.4/cluster/dbm6379/logs/redis_log.log” “/home/neohope/DB/redis-3.0.4/cluster/dbm6380/logs/redis_log.log” “/home/neohope/DB/redis-3.0.4/cluster/dbm6381/logs/redis_log.log” “/home/neohope/DB/redis-3.0.4/cluster/dbm6382/logs/redis_log.log” “/home/neohope/DB/redis-3.0.4/cluster/dbm6383/logs/redis_log.log” “/home/neohope/DB/redis-3.0.4/cluster/dbm6384/logs/redis_log.log”
dir “/home/neohope/DB/redis-3.0.4/cluster/dbm6379/data” “/home/neohope/DB/redis-3.0.4/cluster/dbm6380/data” “/home/neohope/DB/redis-3.0.4/cluster/dbm6381/data” “/home/neohope/DB/redis-3.0.4/cluster/dbm6382/data” “/home/neohope/DB/redis-3.0.4/cluster/dbm6383/data” “/home/neohope/DB/redis-3.0.4/cluster/dbm6384/data”
cluster-enabled yes yes yes yes yes yes
cluster-config-file nodes-6379.conf nodes-6380.conf nodes-6381.conf nodes-6382.conf nodes-6383.conf nodes-6384.conf
cluster-node-timeout 15000 15000 15000 15000 15000 15000
cluster-migration-barrier 1 1 1 1 1 1
cluster-require-full-coverage yes yes yes yes yes yes

3、启动redis

#!/bin/sh
~/DB/redis-3.0.4/bin/redis-server ~/DB/redis-3.0.4/cluster/dbm6379/conf/redis.conf & echo $! & ~/DB/redis-3.0.4/bin/redis-server ~/DB/redis-3.0.4/cluster/dbm6380/conf/redis.conf & echo $! & ~/DB/redis-3.0.4/bin/redis-server ~/DB/redis-3.0.4/cluster/dbm6381/conf/redis.conf & echo $! & ~/DB/redis-3.0.4/bin/redis-server ~/DB/redis-3.0.4/cluster/dbs6382/conf/redis.conf & echo $! & ~/DB/redis-3.0.4/bin/redis-server ~/DB/redis-3.0.4/cluster/dbs6383/conf/redis.conf & echo $! & ~/DB/redis-3.0.4/bin/redis-server ~/DB/redis-3.0.4/cluster/dbs6384/conf/redis.conf & echo $!

4、配置cluster

#!/bin/sh
#这里最好不要用127.0.0.1做地址
~/DB/redis-3.0.4/bin/redis-trib.rb create --replicas 1 127.0.0.1:6379 127.0.0.1:6380 127.0.0.1:6381 127.0.0.1:6382 127.0.0.1:6383 127.0.0.1:6384      

5、测试redis cluster

~/DB/redis-3.0.4/bin/redis-cli -c -p 6379 
set key01 a
set key02 b
set key03 c
set key04 d
dbsize
keys *
get key03

6、关闭redis

#!/bin/sh
~/DB/redis-3.0.4/bin/redis-cli -p 6379 shutdown
~/DB/redis-3.0.4/bin/redis-cli -p 6380 shutdown
~/DB/redis-3.0.4/bin/redis-cli -p 6381 shutdown
~/DB/redis-3.0.4/bin/redis-cli -p 6382 shutdown
~/DB/redis-3.0.4/bin/redis-cli -p 6383 shutdown
~/DB/redis-3.0.4/bin/redis-cli -p 6384 shutdown

参考:
redis cluster tutorial