AUTHORED_MARKDOWN / 2026-08-26
Redis 学习笔记
记录 Hash、Listpack、Hashtable 等底层结构,以及购物车、缓存与常见面试场景中的选择依据。
- 字符
- 3,535
- 标题节点
- 20
- 预计阅读
- 约 8 分钟
- 内容状态
- 持续整理
redis学习:
1.Redis Hash底层是什么?
Redis Hash底层根据数据规模采用不同编码。
当Hash对象较小时(一般小于64或256)省内存,会采用listpack结构(类似于在内存里连续存储的链表数组),它将field和value连续存储,减少额外指针和对象开销。
当Hash数据量较大或者字段值较长时,会转换为hashtable结构(哈希表),
通过哈希定位field,提高查询效率。
这种设计体现了Redis空间和时间的平衡:
小数据优先节省内存,大数据优先保证查询性能。
这也是 Redis 设计思想:
用空间换时间,用结构适配场景,而不是所有数据都使用同一种实现。
Hash实际案例:购物车
这是面试非常喜欢问的。
购物车:
用户:
user:1001
商品:
商品id
数量
Redis:
cart:user:1001
商品id 数量
10001 2
10002 5
10003 1
添加商品:
HSET cart:user:1001 10001 2
增加数量:
HINCRBY cart:user:1001 10001 1
数量:
2+1=3
查询购物车:
HGETALL cart:user:1001
删除商品:
HDEL cart:user:1001 10001
这就是电商购物车典型设计。
Hash 常用命令总结
命令 作用
HSET 设置字段
HGET 获取字段
HMGET 获取多个字段
HGETALL 获取全部字段
HDEL 删除字段
HEXISTS 判断字段
HLEN 字段数量
HINCRBY 数字增加
HKEYS 获取所有字段名
HVALS 获取所有值
List:栈与队列
List = 栈(Stack)
什么是栈?
特点:
后进先出(在栈里全是左,即LPUSH,LPOP)
英文:
LIFO
Last In First Out
现实:
一摞盘子。
放:
盘子1
盘子2
盘子3
拿:
先拿盘子3。
Redis实现:
使用:
LPUSH + LPOP
例如:
入栈:
LPUSH stack A
LPUSH stack B
LPUSH stack C
现在:
C
B
A
出栈:
LPOP stack
得到:
C
符合:
后进先出。
List = 队列(Queue)
更重要。
队列:
特点:
先进先出(在队列里先向右插入RPUSH,在向左处理LPOP)
FIFO
First In First Out
现实:
排队买票:
张三
李四
王五
张三先处理。
Redis实现:
生产:
RPUSH queue task1
RPUSH queue task2
RPUSH queue task3
队列:
task1
task2
task3
消费:
LPOP queue
得到:
task1
剩余:
task2
task3
这就是最简单消息队列。
Redis List底层是什么?
回答:
Redis List底层采用quicklist结构。
quicklist是双向链表和listpack的结合,
每个节点内部使用listpack存储多个元素。
相比传统链表,它减少了指针带来的内存开销,
相比连续数组,它保留了快速插入删除能力。
Redis通过这种结构在性能和内存之间取得平衡。
实际项目案例:订单异步处理
假设:
用户下单。
传统:
用户请求
↓
创建订单
↓
发送短信
↓
扣库存
↓
生成物流
↓
返回
问题:
后面步骤慢。
用户等待:
5秒。
改成 Redis List:
流程:
用户下单
↓
订单服务
↓
写入Redis List
↓
立即返回
↓
后台Worker消费
↓
处理订单
Redis List vs Kafka/RabbitMQ
面试经常问:
为什么不用Redis做消息队列?
对比:
Redis List RabbitMQ Kafka
定位 简单队列 消息中间件 大数据流平台
吞吐 高 高 极高
消息持久化 弱 强 强
消息确认 弱 强 强
顺序保证 简单 支持 强
重试机制 需要自己实现 内置 支持
适合 简单异步任务 业务消息 日志/数据流
如果面试问:Redis如何实现消息队列?
Redis可以通过List结构实现简单消息队列。
生产者使用RPUSH将消息放入队列,
消费者使用LPOP获取消息。
为了避免消费者不断轮询导致CPU浪费,
Redis提供BLPOP阻塞读取,
消费者没有消息时进入等待状态,
有消息时立即唤醒。
不过Redis List缺少完整消息确认和持久化机制,
在可靠性要求高的场景通常使用Kafka或RabbitMQ。
Set和List区别(面试)
List Set
是否有序 有序 无序
是否重复 允许 不允许
访问方式 索引 成员
底层 quicklist intset/hash
应用 队列 去重集合
Redis Set有什么特点?
回答:
Redis Set是一种无序不可重复的数据结构。
它底层根据数据类型选择intset或者hashtable:
当元素都是整数且数量较小时使用intset节省内存,
否则使用hashtable保证查询效率。
Set除了提供增删查操作外,
还支持交集、并集、差集等集合运算,
因此常用于标签系统、共同好友、点赞去重、抽奖等场景。
Redis ZSet底层是什么?
回答:
Redis ZSet底层主要由hash table和skiplist组成。
hash table用于快速通过member找到score,
时间复杂度接近O(1)。
skiplist用于按照score排序,
支持范围查询和排名操作,
平均时间复杂度O(logN)。
两者结合,使ZSet既能快速查询元素,
又能高效实现排行榜功能。
Redis如何保证数据持久化?
回答:
Redis提供RDB和AOF两种持久化方式。
RDB通过定期生成内存快照保存数据,
优点是恢复快、文件小,
缺点是可能丢失最近一次快照之后的数据。
AOF通过记录每次写命令实现持久化,
数据安全性更高。
生产环境通常会结合使用,
RDB用于快速恢复和备份,
AOF用于降低数据丢失风险。
Redis缓存异常有哪些?如何解决?
可以回答:
Redis缓存主要有三个问题。
缓存穿透是查询不存在的数据,
导致请求绕过缓存访问数据库,
可以通过缓存空值或者布隆过滤器解决。
缓存击穿是热点数据过期时,
大量请求同时访问数据库,
可以通过互斥锁或者热点数据永不过期解决。
缓存雪崩是大量缓存同时失效,
导致数据库压力瞬间增加,
可以通过随机过期时间、缓存预热、多级缓存等方式解决。
穿透:
没有数据 → 查数据库
解决:
布隆过滤器 + 空值缓存
击穿:
一个热点key失效
解决:
互斥锁 + 永不过期
雪崩:
大量key一起失效
解决:
随机过期 + 预热 + 多级缓存
Redis如何实现分布式锁?
回答:
Redis实现分布式锁主要利用SET命令的NX和EX参数。
NX保证只有一个客户端能够成功创建锁,
EX设置过期时间防止服务异常导致死锁。
为了避免误删其他客户端的锁,
通常会给锁设置唯一value,
释放锁时通过Lua脚本判断value是否属于当前客户端。
生产环境通常使用Redisson等成熟框架实现。
AOF和replication buffer(复制缓冲区)的区别
非常重要:
AOF replication buffer
作用 持久化 同步Slave
保存位置 磁盘文件 内存
保存内容 写命令 写命令
生命周期 长期 临时
服务对象 自己恢复 从节点同步
Redis主从复制是什么?
回答:
Redis主从复制是一种数据同步机制。
主节点负责处理写请求,
从节点复制主节点数据并承担部分读请求。
第一次同步时采用全量复制,
通过生成RDB快照传输数据。
之后通过复制缓冲区进行增量复制,
同步新的写操作。
主从复制可以提高读性能和数据可靠性,
但主节点故障自动切换需要结合Sentinel实现。
Redis Sentinel(哨兵)是什么?
回答:
Redis Sentinel是Redis提供的高可用解决方案。
它主要负责监控Redis节点状态,
当Master发生故障时,
通过多个Sentinel节点投票确认故障,
选择合适的Slave提升为新的Master,
并通知客户端更新连接。
主从复制负责数据同步,
Sentinel负责故障检测和自动故障转移。
Redis Cluster(集群)为什么需要Hash Slot(哈希槽)?
回答:
Redis Cluster通过16384个Hash Slot实现数据分片。
客户端根据key计算slot,
然后根据slot所在节点发送请求。
相比直接key映射节点,
Hash Slot可以更方便地进行节点扩容和数据迁移,
因为只需要迁移部分slot,而不是重新分布全部数据。