5月28日 09:35

Redis 是什么?核心特点有哪些?

Redis 是什么?

Redis(Remote Dictionary Server)是用 C 语言编写的开源高性能键值存储系统。它将数据全部驻留在内存中,通过单线程事件驱动模型处理请求,单机 QPS 可达 10 万+。与 Memcached 等纯缓存不同,Redis 提供持久化、事务、Lua 脚本、发布订阅等能力,既能做缓存,也能承担消息队列、会话存储、排行榜等业务角色。

主要特点

基于内存的高性能读写

Redis 所有数据存储在内存中,内存访问延迟在纳秒级,远低于磁盘的毫秒级延迟。配合 I/O 多路复用(epoll/kqueue)和单线程事件循环,避免了线程切换和锁竞争的开销,这是 Redis 高吞吐的根本原因。

Redis 6.0 引入了多线程 I/O,但命令执行仍是单线程——多线程只负责网络读写,执行命令本身仍串行执行,既保证了原子性,又提升了网络 I/O 瓶颈下的吞吐。

丰富的数据结构

Redis 远不止简单的 key-value,它原生支持 9 种数据类型:

类型典型场景
String缓存、计数器、分布式锁
Hash对象存储(用户信息、商品属性)
List消息队列、最新消息排行
Set去重、交集/并集运算(共同关注)
ZSet排行榜、延迟队列
Bitmap签到、在线状态
HyperLogLogUV 统计(允许误差)
Geo附近的人、距离计算
Stream消息队列(支持消费者组)

每种类型底层有对应的编码实现,Redis 会根据数据量自动选择编码以节省内存。例如 List 在元素少时使用 ziplist(紧凑列表),元素多时切换为 quicklist。

双模式持久化

Redis 提供两种持久化机制,可以单独使用也可以组合使用:

RDB(快照):在指定时间间隔内将内存数据集写入磁盘的二进制文件。优点是文件紧凑、恢复速度快;缺点是两次快照之间的数据可能丢失。

AOF(追加日志):将每个写命令追加到日志文件末尾。数据安全性更高,最多丢失 1 秒数据(everysec 策略);但日志文件体积更大,恢复速度较慢。

生产环境通常两者同时开启:RDB 用于快速恢复,AOF 用于保证数据完整性。Redis 4.0+ 的混合持久化模式,在 AOF 重写时将当前数据以 RDB 格式写入,后续增量命令以 AOF 格式追加,兼顾了恢复速度和数据安全。

原子性操作与事务

Redis 单个命令是原子性的,要么执行成功要么不执行。对于需要多个命令原子执行的场景,Redis 提供了事务机制:

shell
MULTI # 开启事务 SET key1 v1 SET key2 v2 EXEC # 提交事务

MULTIEXEC 之间的命令会按顺序串行执行,不会被其他客户端打断。但需要注意,Redis 事务不支持回滚——如果某条命令执行失败,其余命令仍会继续执行。这是 Redis 设计者有意为之,目的是保持简单高效。

配合 WATCH 命令可以实现乐观锁:在事务执行前监控 key,若 key 被其他客户端修改,事务自动取消。

主从复制与高可用

Redis 通过主从复制实现读写分离和数据冗余:

  • 全量同步:从库首次连接主库时,主库生成 RDB 快照发送给从库
  • 增量同步:主库将新的写命令持续发送给从库(基于 replication offset 和 repl_backlog)

Redis Sentinel 在主从基础上实现自动故障转移:监控主库状态,主库宕机时自动选举新主库并通知客户端切换。选举依据优先级、复制偏移量、run_id 三个维度排序。

集群与水平扩展

Redis Cluster 通过哈希槽(Hash Slot)实现数据分片,共 16384 个槽位分配到不同节点:

bash
# 集群中查看 key 所属槽位 CLUSTER KEYSLOT mykey

每个节点负责一部分槽位,客户端通过 MOVED 重定向找到目标节点。集群支持自动故障检测和转移,当某个主节点不可用时,其从节点自动升主。

发布订阅与 Stream

Redis 内置 Pub/Sub 模式,支持频道订阅和模式匹配订阅:

bash
SUBSCRIBE channel1 # 订阅频道 PUBLISH channel1 "hello" # 发布消息

Pub/Sub 的局限是消息不持久化,离线客户端无法收到历史消息。Redis 5.0 引入的 Stream 类型弥补了这一缺陷——它支持消息持久化、消费者组、消息确认(ACK),可以作为轻量级消息队列使用。

Lua 脚本支持

Redis 支持在服务端执行 Lua 脚本,脚本在执行期间不会被其他命令打断,适合需要原子性的复合操作:

lua
-- 限流脚本示例:每秒最多允许 N 次请求 local key = KEYS[1] local limit = tonumber(ARGV[1]) local count = redis.call("INCR", key) if count == 1 then redis.call("EXPIRE", key, 1) end if count > limit then return 0 end return 1

内存优化机制

Redis 使用多种策略控制内存使用:共享对象池复用小整数(0-9999)、ziplist/listpack 紧凑编码节省小数据内存、惰性删除避免大 key 阻塞主线程。配合 maxmemory 配置和淘汰策略(如 allkeys-lru、volatile-lfu),可以在内存不足时自动回收低价值 key。

面试追问方向

Redis 为什么快? 内存存储 + I/O 多路复用 + 单线程避免锁竞争 + 高效数据结构编码。

Redis 为什么早期用单线程? CPU 不是瓶颈,内存和网络才是;单线程避免上下文切换和锁开销,实现简单可靠。

RDB 和 AOF 怎么选? 对数据完整性要求高选 AOF,对恢复速度要求高选 RDB,生产环境建议混合持久化同时开启。

标签:Redis