Redis基础知识全解析:从数据结构到生产实战

在后端开发中,Redis绝对是绕不开的核心中间件------它基于内存、高性能、支持多种数据结构,既能作为缓存提升系统响应速度,也能作为数据库、消息队列支撑业务场景。今天就从基础到实战,全面拆解Redis的核心知识点,涵盖数据结构、缓存问题、持久化、内存管理,新手也能轻松看懂,面试复习也能直接用。

一、Redis 核心概述

Redis(Remote Dictionary Server),即远程字典服务,是一款开源的、基于内存的键值型NoSQL数据库,不同于传统关系型数据库,它不依赖磁盘IO,读写性能极高(单机QPS可达10万+),且支持多种数据结构、持久化、高可用(主从、哨兵、集群)和分布式特性。

核心优势: 1. 高性能:内存操作,延迟极低(毫秒级响应); 2. 多数据结构:支持String、Hash、List等多种类型,适配不同业务场景; 3. 持久化:支持RDB和AOF两种方式,避免内存数据丢失; 4. 高可用:主从复制、哨兵模式、集群部署,保证服务稳定; 5. 多功能:可实现缓存、计数器、消息队列、分布式锁等场景。

二、Redis 五大核心数据结构(底层实现+使用场景)

Redis的核心竞争力之一,就是支持多种数据结构,每种结构都有其底层实现逻辑和专属使用场景,吃透这5种,就能应对80%的业务需求。

1. String(字符串)------最基础、最常用

底层实现:默认使用SDS(简单动态字符串),而非C语言原生字符串。SDS支持动态扩容(避免频繁内存分配)、二进制安全(可存储图片、视频等二进制数据)、兼容C字符串函数,同时记录字符串长度,获取长度时时间复杂度为O(1)。

核心特性:单个字符串最大容量512MB,支持原子操作(incr、decr等),可实现自增自减。

常用命令:set(设值)、get(取值)、incr(自增1)、decr(自减1)、append(追加字符串)、expire(设置过期时间)。

使用场景: - 缓存用户信息(将用户JSON序列化后存入); - 分布式锁(setnx命令,即"set if not exists"); - 计数器(点赞数、阅读量、接口访问次数); - 分布式ID生成(incr自增,拼接前缀); - 简单的消息通知(存临时消息内容)。

2. Hash(哈希)------适合存储对象

底层实现:两种底层结构自适应切换------当哈希表中元素数量少、字段值小时,使用ziplist(压缩列表)节省内存;当元素数量或字段值超过阈值时,切换为hashtable(哈希表),保证查询效率。

核心特性:键值对的嵌套结构(key → field → value),支持字段级别的增删改查,无需修改整个对象,节省内存和带宽。

常用命令:hset(设字段值)、hget(取字段值)、hgetall(取所有字段值)、hexists(判断字段是否存在)、hdel(删除字段)。

使用场景: - 存储对象(用户信息、商品信息、订单信息),例如"user:1001"为key,field为"name""age""phone"; - 购物车(key为用户ID,field为商品ID,value为商品数量); - 配置信息存储(按模块划分字段,便于修改单个配置)。

3. List(列表)------有序、可重复,适合有序场景

底层实现:同Hash类似,底层是ziplist和linkedlist(双向链表)的自适应切换------元素少时用ziplist,元素多时用linkedlist,保证插入、删除效率。Redis 3.2版本后,统一用quicklist(快速列表)替代两者,quicklist是ziplist的双向链表,兼顾内存和效率。

核心特性:有序(按插入顺序排列)、可重复,支持从两端插入、删除元素,时间复杂度为O(1),查询中间元素效率较低(O(n))。

常用命令:lpush(左插入)、rpush(右插入)、lpop(左删除)、rpop(右删除)、lrange(获取指定范围元素)。

使用场景: - 消息队列(简单的生产者-消费者模型,lpush生产、rpop消费); - 排行榜(最新消息、最新评论,lpush插入,lrange获取前N条); - 历史记录(用户浏览记录、操作日志)。

4. Set(集合)------无序、不可重复,支持交集、并集

底层实现:两种结构------当元素全是整数且数量较少时,使用intset(整数集合)节省内存;否则使用hashtable,通过key存储元素,value设为null,利用哈希表的去重特性。

核心特性:无序、不可重复,支持交集、并集、差集等集合操作,时间复杂度为O(1)(判断元素是否存在)。

常用命令:sadd(添加元素)、srem(删除元素)、smembers(获取所有元素)、sismember(判断元素是否存在)、sinter(交集)、sunion(并集)。

使用场景: - 去重(用户标签去重、商品ID去重); - 好友关系(A的好友集合、B的好友集合,求交集就是共同好友); - 抽奖活动(srandmember随机取元素,spop随机删除元素)。

5. Sorted Set(有序集合)------有序、不可重复,带分数排序

底层实现:ziplist和skiplist(跳跃表)自适应切换------元素少时用ziplist,元素多或分数范围大时用skiplist+hashtable:skiplist负责按分数排序,hashtable负责快速查询元素对应的分数,兼顾排序和查询效率。

核心特性:不可重复,每个元素关联一个分数(score),按分数升序/降序排列,支持按分数范围查询。

常用命令:zadd(添加元素,指定分数)、zrange(按分数升序取元素)、zrevrange(按分数降序取元素)、zscore(获取元素分数)、zrem(删除元素)。

使用场景: - 排行榜(游戏战力榜、商品销量榜、用户积分榜,按分数排序); - 带权重的消息队列(按分数作为优先级,分数高的先消费); - 范围查询(查询分数在100-200之间的用户)。

三、缓存三剑客:穿透、击穿、雪崩(原因+解决方案)

Redis作为缓存使用时,最常见的三个问题就是"缓存穿透、缓存击穿、缓存雪崩",这三个问题会导致缓存失效,大量请求直接打向数据库,造成数据库压力过大,甚至宕机。下面逐一拆解原因和可落地的解决方案。

1. 缓存穿透(Cache Penetration)

定义:请求查询的数据,既不在Redis缓存中,也不在数据库中,导致每次请求都穿透缓存,直接访问数据库。例如:查询一个不存在的用户ID、不存在的商品ID,请求会一直打向数据库。

危害:大量无效请求冲击数据库,导致数据库负载过高,甚至宕机。

解决方案: - 方案1:缓存空值(最常用)------对于不存在的数据,Redis缓存一个空值(如""、null),并设置较短的过期时间(如5分钟),避免同一无效请求反复穿透; - 方案2:布隆过滤器(进阶)------在Redis之前添加布隆过滤器,将数据库中所有存在的key提前存入布隆过滤器,请求先经过布隆过滤器,不存在的key直接返回,无需访问Redis和数据库; - 方案3:接口层校验------在接口入口处,对无效参数(如负数ID、非法格式ID)进行校验,直接拦截无效请求。

2. 缓存击穿(Cache Breakdown)

定义:某个热点key(高频访问的key)过期时,大量请求同时访问该key,此时缓存失效,所有请求都直接打向数据库,导致数据库瞬间压力骤增。例如:热门商品、热门接口的缓存过期。

危害:热点key对应的数据库表被高并发请求冲击,可能导致数据库宕机,影响该热点业务。

解决方案: - 方案1:热点key永不过期------对于核心热点key,不设置过期时间,避免过期失效;但需注意内存占用,定期手动清理或更新; - 方案2:互斥锁(分布式锁)------当缓存失效时,只有一个请求能获取锁,去数据库查询并更新缓存,其他请求等待锁释放后,从缓存中获取数据,避免大量请求同时访问数据库; - 方案3:缓存预热+过期时间错开------提前将热点key加载到缓存中,并且给不同的热点key设置不同的过期时间,避免多个热点key同时过期。

3. 缓存雪崩(Cache Avalanche)

定义:大量缓存key在同一时间段内过期,或者Redis服务宕机,导致所有请求都穿透缓存,直接打向数据库,造成数据库雪崩式崩溃。例如:批量设置缓存时,给所有key设置相同的过期时间;Redis集群故障。

危害:数据库瞬间承受海量请求,直接宕机,导致整个系统不可用。

解决方案: - 方案1:过期时间错开------给不同的key设置随机的过期时间(如基础过期时间+1-5分钟随机值),避免大量key同时过期; - 方案2:Redis高可用部署------搭建主从复制、哨兵模式或Redis集群,即使主节点宕机,从节点也能快速切换,保证Redis服务不中断; - 方案3:缓存降级------当Redis服务故障时,暂时关闭部分非核心业务的缓存查询,优先保证核心业务正常运行,减少数据库压力; - 方案4:多级缓存------除了Redis,增加本地缓存(如Caffeine、Ehcache),当Redis宕机时,请求先访问本地缓存,缓解数据库压力。

四、Redis 持久化方式(RDB vs AOF)

Redis是基于内存的数据库,内存中的数据断电即失,因此需要通过持久化将内存中的数据保存到磁盘,重启Redis时再从磁盘恢复数据。Redis支持两种持久化方式:RDB和AOF,可单独使用,也可结合使用。

1. RDB(Redis Database)------快照持久化

定义:在指定的时间间隔内,将Redis内存中的所有数据生成一份快照(.rdb文件),保存到磁盘,重启时加载该文件恢复数据。

实现原理:Redis会fork一个子进程,子进程负责将内存中的数据写入磁盘,主进程继续处理客户端请求,不影响服务可用性(写时复制技术,避免主进程阻塞)。

触发方式: - 手动触发:执行save(主进程执行,阻塞所有请求,不推荐)、bgsave(后台执行,推荐)命令; - 自动触发:在redis.conf配置文件中设置触发条件,例如"save 900 1"(900秒内有1个key变化)、"save 300 10"(300秒内有10个key变化)。

优点: - 快照文件体积小,加载速度快,适合用于备份、灾难恢复; - 持久化时主进程不阻塞,不影响服务性能。

缺点: - 数据一致性差,若Redis宕机,上次快照之后的数据会丢失(丢失时间取决于快照间隔); - fork子进程时,若内存较大,会占用较多系统资源,可能导致短暂卡顿。

2. AOF(Append Only File)------日志持久化

定义:将Redis执行的每一条写命令(set、hset、lpush等)都追加到AOF日志文件中,重启Redis时,通过重新执行日志中的命令,恢复内存中的数据。

实现原理:Redis执行写命令后,先将命令写入AOF缓冲区,再由缓冲区写入磁盘(可配置写入策略),保证命令不丢失。

写入策略(redis.conf配置): - appendfsync always:每执行一条命令,立即写入磁盘,数据一致性最高,但性能最差(IO开销大); - appendfsync everysec:每秒写入一次磁盘,兼顾一致性和性能(默认配置),最多丢失1秒的数据; - appendfsync no:由操作系统决定何时写入磁盘,性能最好,但数据一致性最差,可能丢失大量数据。

优点: - 数据一致性高,可配置丢失数据的范围(最多1秒); - 日志文件是文本格式,可手动修改日志,恢复指定数据。

缺点: - 日志文件体积大,加载速度慢(需要重新执行所有命令); - 写入命令时会有IO开销,性能略低于RDB。

3. RDB vs AOF 选择建议

  • 若追求数据安全性、可容忍少量性能损耗,选择AOF; - 若追求高性能、可容忍少量数据丢失(如缓存场景),选择RDB; - 生产环境推荐:RDB+AOF结合使用,RDB用于备份和快速恢复,AOF用于保证数据一致性,避免大量数据丢失。

五、Redis 过期删除策略

Redis支持给key设置过期时间(expire命令),当key过期后,Redis需要将其删除,释放内存。但如果直接删除所有过期key,会占用大量CPU资源,影响服务性能,因此Redis采用"三种策略结合"的方式,平衡删除效率和性能。

1. 惰性删除(Lazy Expiration)

核心逻辑:不主动删除过期key,只有当客户端请求该key时,才判断该key是否过期,若过期则删除,返回null;若未过期则返回value。

优点 :不占用额外CPU资源,只在请求时判断,性能损耗小; 缺点:过期key会一直占用内存,导致内存泄漏(若大量key过期但未被访问,内存会被无效数据占用)。

2. 定期删除(Periodic Expiration)

核心逻辑:Redis每隔一段时间(默认100毫秒),随机抽取一部分过期key进行检查,若过期则删除。抽取的数量和频率可配置,避免一次性检查所有key,减少CPU开销。

优点 :定期清理过期key,减少内存泄漏; 缺点:可能存在少量过期key未被及时删除,占用内存(但概率较低)。

3. 内存淘汰策略(Memory Eviction Policy)

核心逻辑:当Redis内存达到最大限制(maxmemory)时,即使没有过期key,也会主动淘汰部分key,释放内存,保证Redis正常运行。(注意:这是"内存满了"的兜底策略,和"过期删除"是两个不同的场景)

Redis 8种内存淘汰策略(按优先级排序,推荐使用前4种):

  • volatile-lru(推荐):从设置了过期时间的key中,淘汰最近最少使用(LRU)的key;

  • volatile-ttl:从设置了过期时间的key中,淘汰剩余过期时间最短的key;

  • volatile-random:从设置了过期时间的key中,随机淘汰一个key;

  • allkeys-lru(推荐):从所有key中,淘汰最近最少使用(LRU)的key(适合缓存场景,不分是否设置过期时间);

  • allkeys-random:从所有key中,随机淘汰一个key;

  • volatile-lfu:从设置了过期时间的key中,淘汰最近最不常用(LFU)的key(LFU比LRU更精准,考虑访问频率);

  • allkeys-lfu:从所有key中,淘汰最近最不常用(LFU)的key;

  • noeviction(默认,不推荐):不淘汰任何key,当内存满时,拒绝所有写请求,返回错误(会导致服务不可用)。

选择建议: - 缓存场景(大多数业务):优先选择 volatile-lru 或 allkeys-lru; - 对访问频率敏感的场景:选择 LFU 相关策略; - 不推荐使用 noeviction,避免内存满导致服务异常。

六、总结

Redis的核心价值在于"高性能"和"多功能",掌握它的基础知识点,能轻松应对日常开发和面试。本文梳理的核心要点:

  1. 五大数据结构:String(基础)、Hash(对象)、List(有序可重复)、Set(无序去重)、Sorted Set(有序带分数),吃透底层和场景,就能灵活运用;

  2. 缓存三剑客:穿透(缓存空值+布隆过滤器)、击穿(互斥锁+热点永不过期)、雪崩(过期错开+高可用),记住原因和解决方案,避免生产事故;

  3. 持久化:RDB(快照,快但一致性差)、AOF(日志,一致但慢),生产环境推荐结合使用;

  4. 内存管理:过期删除(惰性+定期)、内存淘汰策略(优先LRU/LFU),保证Redis高效、稳定运行。

Redis的知识点还有很多(如主从复制、哨兵、集群),后续会继续拆解进阶内容,关注我,一起搞定后端中间件!

相关推荐
21439652 小时前
Redis如何解决哨兵通知延迟问题_优化客户端连接池动态刷新拓扑的订阅监听机制
jvm·数据库·python
川石课堂软件测试2 小时前
requests接口自动化测试
数据库·python·功能测试·测试工具·单元测试·grafana·prometheus
瀚高PG实验室2 小时前
瀚高数据库安全版4.5.8系列使用pg_cron定时任务
服务器·数据库·瀚高数据库
2401_871696522 小时前
HTML5中Canvas局部刷新区域重绘的算法优化
jvm·数据库·python
CodeMartain2 小时前
MongoDB--Spring
数据库·mongodb·spring
自我意识的多元宇宙2 小时前
二叉树遍历方式代码解读(3层序遍历)
数据结构
数字孪生进化论2 小时前
数据集成实战|从零到一,把物联网数据接入数字孪生IOC需要几步?
数据库·物联网
野生技术架构师2 小时前
从两套系统到一条 SQL:SelectDB search() 搞定日志的搜索与分析
数据库·sql
2301_777599372 小时前
CSS如何制作卡片翻转效果_利用backface-visibility与动画
jvm·数据库·python