什么是 Redis?
Redis 是一个开源的内存数据结构存储系统。它以键值方式组织数据,把主要工作集放在内存中,并提供字符串、哈希、列表、集合、有序集合和 Stream 等数据结构。它可以用作缓存、数据库、计数器、排行榜、分布式协调工具和消息中间件。

如果你写过网站后端,很可能见过这种情况:用户每刷新一次页面,应用就重新执行相同的数据库查询。访问量上来后,数据库 CPU、连接数和磁盘 IO 一起升高,页面却越来越慢。
Redis 最常见的角色,就是挡在应用与数据库之间:重复读取的数据先放进 Redis,后续请求直接从内存返回,只有缓存不存在时才查询数据库。它像服务器里的“临时大脑”,负责记住需要快速访问、频繁变化或有明确有效期的数据。

Redis 这个名字最初来自 Remote Dictionary Server。所谓“键值存储”,可以把它理解成一个巨大的字典:应用通过唯一的键定位值,而不是先做多表连接再筛选结果。
user:1001 -> {"name":"小明","level":8}
product:88 -> {"name":"机械键盘","price":599}
video:123:views -> 98766
一句话总结:Redis 是一个以内存换取低延迟、以数据结构和原子命令降低开发复杂度的键值数据存储系统。
Redis 为什么比传统磁盘数据库快?
Redis 快的第一原因不是单线程,而是大多数读写都在内存中完成。内存访问通常处于微秒甚至更低的硬件延迟范围,随机磁盘访问则可能进入毫秒范围;两条路径在特定场景下可以相差两个数量级。

“快百倍”适合描述内存路径与真实磁盘 IO 的量级差异,但不能当成固定性能承诺。MySQL、PostgreSQL 等数据库也会使用缓冲池和操作系统页缓存,命中内存的简单查询未必比 Redis 慢百倍;网络延迟、数据大小、命令复杂度、持久化配置和部署方式也会改变结果。
Redis 的低延迟来自以下因素共同作用:
- 数据主要在内存中:常规命令不必等待随机磁盘寻址。
- 键值定位直接:很多操作的时间复杂度是
O(1)或O(log N),不需要临时执行复杂查询计划。 - 命令执行路径短:紧凑的 RESP 协议、成熟的 C 实现和专门的数据编码减少了额外工作。
- 核心命令串行执行:大多数命令不需要为共享数据加锁,也没有多个命令线程争抢同一份结构的成本。
- IO 多路复用:一个事件循环可以同时管理大量连接,只处理已经准备好读写的套接字。
- 支持批量与流水线:Pipelining 能把多条命令一起发送,减少网络往返次数。
因此,Redis 的速度不是“单线程创造的奇迹”,而是内存、事件驱动模型、数据结构和短执行路径共同带来的结果。
Redis 的数据在内存里,断电后会丢吗?
Redis 可以把内存状态写入磁盘,但是否丢数据取决于持久化模式和刷盘策略。它主要提供 RDB 快照与 AOF 追加日志两套机制,两者可以单独使用,也可以同时开启。

| 维度 | RDB 快照 | AOF 日志 |
|---|---|---|
| 记录内容 | 某个时刻的数据快照 | 会改变数据的写命令 |
| 恢复方式 | 直接加载快照 | 重放日志恢复状态 |
| 文件特点 | 通常更紧凑,适合备份 | 通常更大,可通过重写压缩 |
| 数据风险 | 可能丢失最近一次快照后的数据 | 取决于 appendfsync,常用 everysec 最坏可能丢约 1 秒写入 |
| 典型用途 | 冷备、灾备、快速恢复 | 更低的数据丢失窗口 |
RDB 像定期给整本日记拍照。AOF 像把每次修改逐条记下来,重启时再按顺序重放。生产环境可以同时使用两者,但“同时开启”并不自动等于零丢失:机器故障、磁盘损坏、主从复制延迟和错误配置仍可能造成数据风险。
如果 Redis 只做可重建缓存,可以接受关闭持久化;如果 Redis 保存无法重建的数据,就必须明确恢复点目标、备份位置、刷盘策略和故障演练,不能只依赖默认配置。
Redis 明明是单线程,为什么还能处理高并发?
更准确的说法是:Redis 的大多数命令在主线程中串行执行,但 Redis 整个进程并非只有一个线程。 后台持久化、异步释放内存、复制相关工作会使用其他线程或子进程;Redis 6 起还可以使用 IO 线程分担读写网络数据,但命令执行仍主要由主线程完成。

它处理并发连接的方式可以分成四步:
- 客户端连接 Redis,套接字被注册到事件循环。
- IO 多路复用器同时监听大量套接字。
- 某个套接字准备好后,Redis 读取并解析命令。
- 主线程执行命令,再把结果写回客户端。
这种模型的关键是“不在慢 IO 上傻等”。事件循环只处理已经就绪的连接,而内存命令通常能在很短时间内完成。命令按顺序执行,也让单条命令天然具备原子性,不需要在多条命令线程之间频繁加锁和切换上下文。
但单线程也有明确代价:一条慢命令会阻塞后续请求。对超大集合执行高复杂度操作、用 KEYS * 扫描生产库、执行耗时 Lua 脚本,或一次传输过大的值,都可能让所有客户端一起等待。生产环境应使用 SCAN 渐进遍历、控制单个值的大小,并监控慢日志与延迟。
Redis 有哪些核心数据结构?
Redis 不只是“存字符串的缓存”。它把常见业务模型封装成服务端数据结构,并提供原子命令,应用不必每次都把整个对象取回、修改后再写回。

| 数据结构 | 常用命令 | 适合场景 | 典型复杂度 |
|---|---|---|---|
| String 字符串 | GET、SET、INCR | 缓存、计数器、令牌、JSON | GET/SET 通常为 O(1) |
| Hash 哈希 | HGET、HSET | 用户资料、商品属性、字段级更新 | 单字段读写通常为 O(1) |
| List 列表 | LPUSH、RPOP、BLPOP | 简单队列、时间线、阻塞消费 | 两端操作通常为 O(1) |
| Set 集合 | SADD、SISMEMBER、SINTER | 去重、标签、共同好友 | 成员判断通常为 O(1) |
| Sorted Set 有序集合 | ZADD、ZRANGE、ZREVRANGE | 排行榜、优先级队列、延迟任务 | 插入通常为 O(log N) |
| Stream 流 | XADD、XREADGROUP、XACK | 可追踪消息、消费者组 | 适合需要确认和消费进度的队列 |
字符串和列表能解决什么问题?
String 可以保存文本、二进制内容、序列化后的 JSON 或整数。INCR 会在服务端原子加一,因此多个请求同时更新播放量时,不需要先读再写。
List 支持从左右两端压入和弹出元素,适合简单先进先出队列。它能用阻塞弹出减少空轮询,但如果业务需要消费确认、消息重放、消费者组和失败恢复,应优先评估 Redis Stream 或专业消息队列。
集合与有序集合有什么区别?
Set 关心“成员是否存在”,Sorted Set 则为每个成员绑定一个分数并保持排序。共同好友可以使用集合交集,实时排行榜可以使用有序集合。

例如:
SADD user:1:friends 100 101 102
SADD user:2:friends 101 102 103
SINTER user:1:friends user:2:friends
ZADD game:ranking 9850 user:A
ZADD game:ranking 9320 user:B
ZREVRANGE game:ranking 0 99 WITHSCORES
前两组命令能找出共同好友 101 和 102;后一组命令维护分数并取出排行榜前 100 名。
哈希为什么适合存对象?
Hash 把一个 Redis 键映射成多个字段和值。更新用户积分时可以只修改 score 字段,不必读取并重新序列化整个用户对象。

HSET user:1001 name "小明" avatar "/avatars/1001.png" score 1250
HINCRBY user:1001 score 10
HGET user:1001 score
数据结构选对了,Redis 才能真正减少应用代码和网络往返;如果所有内容都无差别塞进超大 JSON,很多服务端原子操作就失去了意义。
Redis 最常见的使用场景有哪些?
Redis 最适合低延迟、访问频繁、结构相对明确的数据。缓存、计数器、排行榜、过期数据和分布式协调是最常见的五类场景。
场景一:缓存数据库查询结果
典型缓存流程叫 Cache Aside:先查 Redis,命中就直接返回;未命中再查数据库,然后把结果写回 Redis 并设置过期时间。

async function getHotProducts() {
const key = 'home:hot-products'
const cached = await redis.get(key)
if (cached) return JSON.parse(cached)
const products = await db.queryHotProducts()
await redis.set(key, JSON.stringify(products), { EX: 10 })
return products
}
这个例子把热门商品缓存 10 秒。它降低了重复查询压力,但也意味着用户可能看到最多约 10 秒的旧数据。缓存设计本质上是在一致性、延迟与数据库压力之间取舍。
场景二:计数器与实时排行榜
INCR 适合播放量、点赞数、接口调用次数等原子计数。Sorted Set 适合按分数排序,并快速读取 Top N。

计数值最终是否回写数据库,要由业务的重要程度决定。视频播放量可以批量异步落库;账户余额则不应该只靠一个缓存计数器维护。
场景三:分布式锁与消息队列
多个服务实例竞争同一资源时,Redis 可以参与实现分布式锁;任务量不大、可靠性要求有限时,也可以承担轻量消息分发。

一个基本锁不能只写成“SETNX 成功后执行业务,结束时直接 DEL”。更稳妥的最低要求是:
- 使用
SET lock:key random-token NX PX 10000原子完成占锁与设置过期时间。 - 每个持有者保存唯一 token,避免误删后来者获得的锁。
- 释放时使用 Lua 脚本原子比较 token 并删除。
- 处理业务执行超过租约、客户端暂停、网络分区和主从切换。
锁直接影响资金、库存或唯一性约束时,应优先使用数据库约束、事务或成熟的协调系统,并评估现成客户端库,而不是手写几行命令后假设它绝对安全。
消息队列也有分层选择:Pub/Sub 延迟低但不持久化,订阅者离线会错过消息;List 简单但确认和重放能力有限;Stream 支持消费者组、待处理消息和确认。需要严格顺序、长期保留、跨集群复制或复杂消费治理时,Kafka、RabbitMQ 等专业系统通常更合适。
场景四:为数据设置过期时间
Redis 可以为每个键设置 TTL,适合验证码、会话、限流窗口、购物车保留时间、优惠券和临时任务状态。

SET verify:phone:13800000000 5837 EX 300
INCR rate-limit:user:1001
EXPIRE rate-limit:user:1001 60
TTL verify:phone:13800000000
过期时间到了,不代表键会在那一微秒立即被删除。Redis 结合惰性删除与主动过期扫描:访问过期键时会清理,后台也会周期抽样回收。因此,TTL 适合表达“过期后不可再使用”,不适合充当要求精确触发时刻的任务调度器。
Redis 和 MySQL 有什么区别?
Redis 与 MySQL 解决的问题不同。Redis 强在低延迟访问和服务端数据结构,MySQL 强在关系模型、事务、复杂查询与持久数据管理;大多数后端系统会组合使用,而不是二选一。
| 维度 | Redis | MySQL | 选择建议 |
|---|---|---|---|
| 主要存储 | 内存为主,可持久化 | 磁盘为主,使用缓冲池加速 | 热数据与临时状态用 Redis |
| 数据模型 | 键值与数据结构 | 表、行、索引、关系 | 复杂关联和约束用 MySQL |
| 查询能力 | 按键和结构化命令访问 | SQL、连接、聚合、事务 | 查询维度多时用 MySQL |
| 延迟特征 | 常见操作延迟低且稳定 | 取决于索引、缓存、IO 与查询计划 | 高频短操作更适合 Redis |
| 持久性 | 可配置 RDB/AOF,需权衡性能与数据窗口 | 以持久事务数据为核心 | 关键事实数据优先放 MySQL |
| 容量成本 | 受内存成本限制 | 单位容量成本通常更低 | 大规模冷数据放 MySQL |
Redis 不应该因为“快”就替代所有数据库。订单、支付、账户余额和审计记录通常需要关系约束、事务与可靠持久化;Redis 更适合缓存这些事实的派生结果,或保存可恢复的临时状态。
使用 Redis 有哪些限制和常见错误?
Redis 的主要限制来自内存成本、单线程命令阻塞、缓存一致性和分布式系统故障。上线前至少要检查以下问题:
- 缓存穿透:反复查询数据库中不存在的键。可以缓存空结果、校验参数或使用布隆过滤器。
- 缓存击穿:一个热点键过期时,大量请求同时打到数据库。可以使用互斥重建、逻辑过期或提前刷新。
- 缓存雪崩:大量键在同一时间失效。可以为 TTL 增加随机抖动,并设计限流与降级。
- 数据库与缓存不一致:更新数据库后旧缓存仍存在。常见做法是先更新数据库再删除缓存,并根据业务增加重试、消息补偿或版本控制。
- 热键和大键:少数键占据大量流量或内存,会拖慢单节点、删除和迁移。应持续监控键大小、访问分布与慢日志。
- 内存打满:达到
maxmemory后,Redis 会按淘汰策略删除键或拒绝写入。必须根据业务选择策略并设置告警。 - 把高复杂度命令放进在线链路:
KEYS、超大范围集合运算和长 Lua 脚本会阻塞主线程。 - 误把复制当备份:误删除会快速复制到副本。备份必须独立保存并定期验证恢复。
Redis 不是“接上就自动变快”的按钮。它把一部分数据库压力转换成了缓存更新、容量规划、故障恢复和一致性治理问题。
哪些官方资料可以继续查证?
Redis 的配置项和命令会随版本演进。设计生产方案时,应以实际部署版本的官方文档为准:
常见问题
Redis 真的是单线程吗?
不是。Redis 的大多数命令主要由单个主线程串行执行,这是人们所说的“单线程”;但持久化、异步释放、复制辅助工作和可选网络 IO 线程会使用其他执行单元。判断性能瓶颈时,不能把整个 Redis 进程简单理解成只有一个线程。
Redis 单线程为什么还能支持高并发?
因为高并发连接不等于同时用多个线程执行命令。Redis 使用 IO 多路复用管理大量连接,主线程只处理已经就绪的请求;内存操作又足够短,因此可以快速轮转。它的前提是单条命令不能长期占用主线程。
Redis 一定比 MySQL 快百倍吗?
不一定。“百倍”描述的是内存访问相对真实磁盘 IO 可能达到的数量级差异,不是通用基准。MySQL 查询命中缓冲池、Redis 跨网络访问、值过大或命令复杂时,差距会明显变化。选型应使用真实数据和访问模式压测。
Redis 重启后数据还在吗?
取决于配置。开启 RDB 或 AOF 后,Redis 可以从磁盘恢复数据;关闭持久化时,进程重启后内存数据会丢失。即使开启持久化,也要根据快照周期或 AOF 刷盘策略接受相应的数据丢失窗口。
Redis 可以完全替代 MySQL 吗?
多数业务不能。Redis 缺少关系型数据库完整的 SQL 查询、外键约束和同类事务能力,而且内存容量成本更高。常见架构是 MySQL 保存权威业务数据,Redis 保存热点副本、计数、会话和临时状态。
Redis 做分布式锁只用 SETNX 就够了吗?
不够。安全的基本实现还需要原子设置过期时间、唯一持有者 token、原子校验后释放,并处理租约超时和故障切换。关键业务应优先使用经过验证的客户端实现,并让数据库唯一约束或幂等机制承担最后一道保护。
Redis 的核心价值是什么?
Redis 的核心价值,是把高频、重复、短生命周期或需要原子更新的数据放进低延迟内存层,并用现成数据结构简化业务逻辑。

它之所以快,不是因为“单线程天然比多线程快”,而是因为大部分工作发生在内存中,命令执行路径短,事件循环避免等待,串行执行又减少了锁竞争。单线程模型同时意味着慢命令会阻塞整个实例,所以速度依赖正确的数据结构、命令和容量设计。
如果数据库正在被重复查询压垮,Redis 往往能成为有效的加速层;如果问题来自错误索引、低效 SQL 或不合理的数据模型,则应先修复根因。最准确的结论是:Redis 不是更快的 MySQL,而是专门处理低延迟数据访问与临时状态的内存数据平台。

