POST · 数据库 · 文章
Redis 从入门到进阶
2026/11/13 · by ZHANG XING YUN · 阅读 1 次 · 3716 字 · 预计阅读 10 分钟
本文覆盖 Redis 的数据类型与命令、过期与淘汰、持久化、事务与 Lua、缓存模式、分布式锁、限流、消息队列、集群架构与运维排查。
- 环境:Redis 7.x、
redis-cli - 定位:内存数据结构服务,用于缓存、计数、协调与轻量消息
一、Redis 是什么、适合做什么
Redis 是基于内存的键值数据库,所有数据放在内存,读写为微秒级。它提供多种数据结构(字符串、哈希、列表、集合、有序集合、位图、HyperLogLog、Stream),并有持久化与主从复制能力。
适合:
| 场景 | 使用的结构 |
|---|---|
| 缓存热点数据 | String / Hash |
| 计数器、限流 | String(INCR) |
| 分布式锁 | String(SET NX PX)+ Lua |
| 排行榜 | ZSet |
| 去重、标签 | Set |
| 会话 | Hash |
| 消息队列 | Stream / List |
| 布隆过滤、UV 统计 | Bitmap / HyperLogLog |
不适合:
- 作为唯一数据源(内存成本高、持久化不能保证零丢失)。
- 复杂查询(没有 SQL,没有跨集合 JOIN)。
- 大 Value(单 key 过大影响性能与迁移)。
二、安装与连接
# Docker
docker run -d --name redis -p 6379:6379 \
-v redisdata:/data \
redis:7-alpine redis-server --appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru
# Ubuntu
sudo apt install -y redis-server
sudo systemctl enable --now redis-server
连接:
redis-cli -h 127.0.0.1 -p 6379
redis-cli -u redis://:password@127.0.0.1:6379/0
程序连接串:
redis://[[用户名]:密码@]主机:端口/库号
redis://default:secret@localhost:6379/0
三、redis-cli 使用
redis-cli ping # PONG
redis-cli info server # 服务器信息
redis-cli dbsize # 当前库 key 数
redis-cli keys "user:*" # 生产禁用(会阻塞),改用 scan
redis-cli --bigkeys # 找出大 key
redis-cli --hotkeys # 需开启 maxmemory-policy lfu
redis-cli monitor # 实时命令流,调试用,生产谨慎
redis-cli slowlog get 10 # 慢查询
redis-cli --latency # 延迟
交互式使用:
redis-cli
127.0.0.1:6379> set name 张三
OK
127.0.0.1:6379> get name
"张三"
127.0.0.1:6379> ttl name
(integer) -1
四、数据类型与命令
4.1 String
SET user:1:name "张三"
SET session:abc "token" EX 3600 # 秒级过期
SET lock:1 "1" NX PX 3000 # 不存在才设置,3 秒过期(分布式锁)
GET user:1:name
MSET a 1 b 2
MGET a b
APPEND k "suffix"
STRLEN k
INCR counter # 原子自增
INCRBY counter 10
DECR counter
SETNX k v # 不存在才设置(旧写法,建议用 SET NX)
GETSET k v # 设置新值并返回旧值
适用:缓存对象(JSON 串)、计数器、锁、会话 token。
4.2 Hash
HSET user:1 name 张三 age 18
HGET user:1 name
HGETALL user:1
HMGET user:1 name age
HDEL user:1 age
HINCRBY user:1 age 1
HEXISTS user:1 name
HKEYS user:1
HVALS user:1
HLEN user:1
适用:对象字段较多且需要单独更新某些字段(比整个 JSON 重新序列化更省)。
4.3 List
LPUSH queue a b c # 左侧入队,结果是 c b a
RPUSH queue d # 右侧入队
LPOP queue
RPOP queue
LRANGE queue 0 -1
LLEN queue
LREM queue 0 a # 删除指定值
LTRIM queue 0 99 # 只保留前 100 个
BLPOP queue 5 # 阻塞弹出,5 秒超时(简单队列/消费者)
适用:队列、最新列表、简易消息(配合 BLPOP)。
4.4 Set
SADD tags a b c
SMEMBERS tags
SISMEMBER tags a
SREM tags b
SCARD tags
SINTER s1 s2 # 交集
SUNION s1 s2
SDIFF s1 s2
SRANDMEMBER tags 2 # 随机取
SPOP tags # 随机弹出(抽奖)
适用:去重、标签、共同好友、抽奖。
4.5 ZSet(有序集合)
ZADD rank 100 user:1
ZADD rank 200 user:2
ZINCRBY rank 50 user:1
ZSCORE rank user:1
ZRANK rank user:1 # 从低到高排名(0 开始)
ZREVRANK rank user:1 # 从高到低
ZRANGE rank 0 9 WITHSCORES # 前 10 名
ZREVRANGE rank 0 9 WITHSCORES
ZRANGEBYSCORE rank 100 200 # 按分数区间
ZREMRANGEBYSCORE rank 0 50 # 删除低分
ZCARD rank
适用:排行榜、延时队列(分数为时间戳)、按分数范围查询。
4.6 Bitmap、HyperLogLog、Geo
# 签到:第 5 天签到
SETBIT sign:2026-01:user1 5 1
GETBIT sign:2026-01:user1 5
BITCOUNT sign:2026-01:user1
# UV 统计(近似,误差约 0.81%)
PFADD uv:20260113 user1 user2
PFCOUNT uv:20260113
# 地理位置
GEOADD shops 116.40 39.90 shop1
GEODIST shops shop1 shop2 km
GEOSEARCH shops FROMMEMBER shop1 BYRADIUS 5 km ASC
Bitmap 适合"只有 0/1"的海量标记(签到、活跃),HyperLogLog 适合去重计数但不要求精确。
五、键管理
KEYS user:* # 生产禁用:全量扫描会阻塞
SCAN 0 MATCH user:* COUNT 100 # 游标遍历,可分批
TYPE key
EXISTS key
DEL key
UNLINK key # 异步删除,适合大 key
EXPIRE key 60
TTL key
PERSIST key # 移除过期时间
RENAME k1 k2
RANDOMKEY
DBSIZE
SCAN 0 TYPE string # 按类型扫描
SCAN 是增量遍历,可能返回重复 key,需要幂等处理。
六、过期与内存淘汰
过期策略:
- 惰性删除:访问时判断是否过期。
- 定期删除:后台周期扫描部分 key。
因此过期 key 不会立刻释放内存,内存占用可能高于预期。
内存淘汰策略(maxmemory-policy):
| 策略 | 行为 |
|---|---|
noeviction |
内存满时写入报错(默认) |
allkeys-lru |
所有 key 中淘汰最近最少使用(缓存首选) |
volatile-lru |
仅淘汰设置了过期时间的 key |
allkeys-random |
随机淘汰 |
volatile-ttl |
优先淘汰即将过期的 |
allkeys-lfu |
淘汰访问频率最低的(4.0+) |
配置:
maxmemory 256mb
maxmemory-policy allkeys-lru
不设置 maxmemory 时,Redis 会一直用内存直到被系统 OOM 杀掉。作为缓存使用时必须设置上限。
七、持久化:RDB 与 AOF
| 方式 | 原理 | 优点 | 缺点 |
|---|---|---|---|
| RDB | 定时生成内存快照 | 文件小、恢复快 | 两次快照之间宕机会丢数据 |
| AOF | 追加写命令日志 | 丢数据少(可配 everysec) | 文件大、恢复慢 |
| 混合 | AOF 重写时带 RDB 头 | 兼顾两者 | 配置略复杂 |
# redis.conf
save 900 1
save 300 10
save 60 10000
appendonly yes
appendfsync everysec
appendfsync 取值:
always:每条命令都 fsync,最安全但最慢。everysec:每秒 fsync,最多丢 1 秒(推荐)。no:交给操作系统,可能丢较多数据。
手动操作:
BGSAVE # 后台生成 RDB
BGREWRITEAOF # 重写 AOF
即使同时开启 RDB 与 AOF,也不能保证零丢失。需要强一致的数据应落库,Redis 只做缓存或可重建数据。
八、事务、Lua 与原子性
8.1 MULTI / EXEC
MULTI
SET a 1
INCR a
EXEC
特点:
- 命令入队后一次性执行,中间不会被其它客户端插入。
- 不支持回滚:某条命令运行时报错,其它命令仍会执行。
WATCH实现乐观锁:
WATCH balance
val = GET balance
MULTI
SET balance <val - 10>
EXEC # 期间 balance 被改过则 EXEC 返回 nil
8.2 Lua 脚本
EVAL "return redis.call('get', KEYS[1])" 1 mykey
# 释放锁:值匹配才删除
EVAL "if redis.call('get',KEYS[1])==ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end" 1 lock:1 token
Lua 脚本的保证:
- 执行期间不会被其它命令打断(原子)。
- 适合"读-判断-写"的组合操作。
注意:脚本中不要写死 key(集群模式需要 key 在同一槽),复杂逻辑容易阻塞,单脚本执行时间应控制在毫秒级。
九、发布订阅
# 订阅
SUBSCRIBE news
PSUBSCRIBE news.*
# 发布
PUBLISH news "hello"
特点:fire-and-forget,订阅者不在线就收不到,没有持久化与确认。需要可靠投递时用 Stream 或专业消息队列。
十、管道与批量
管道(pipeline)把多条命令一次性发送,减少网络往返:
redis-cli --pipe < commands.txt
// Node(ioredis)
const pipeline = redis.pipeline();
pipeline.set("a", 1);
pipeline.incr("a");
const results = await pipeline.exec();
与事务的区别:管道只是批量发送,不保证原子性;MULTI/EXEC 才保证不被插入。批量操作还可使用 MSET、MGET、HMSET 等原生命令。
十一、缓存模式与常见问题
11.1 Cache-Aside(旁路缓存)
读:查缓存 → 未命中 → 查库 → 回写缓存 → 返回
写:更新库 → 删除缓存
这是最常用的模式。要点:
- 写操作先更新数据库,再删除缓存,不要直接改缓存。
- 删除失败时要有补偿(重试、延迟双删、消息队列)。
- 缓存必须设置 TTL 兜底。
11.2 三个经典问题
| 问题 | 现象 | 处理 |
|---|---|---|
| 缓存穿透 | 查不存在的数据,每次都打数据库 | 缓存空值(短 TTL)、布隆过滤器、参数校验 |
| 缓存击穿 | 热点 key 过期瞬间大量请求打到数据库 | 互斥锁重建、逻辑过期、永不过期 + 后台刷新 |
| 缓存雪崩 | 大量 key 同时过期或 Redis 宕机 | TTL 加随机偏移、多级缓存、限流降级、哨兵/集群 |
11.3 key 规范
业务:实体:标识 user:1001 order:20260113:1001
- 长度适中,避免超长 key。
- 不要用用户输入原文拼 key(注入、超长)。
- 同一业务的 key 前缀一致,便于统计与清理。
十二、分布式锁
# 加锁:不存在才设置,带过期时间
SET lock:order:1001 <token> NX PX 5000
# 解锁:值匹配才删除(避免删别人的锁)
EVAL "if redis.call('get',KEYS[1])==ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end" 1 lock:order:1001 <token>
必须满足的三个条件:
- 加锁与设置过期是同一条命令(
SET NX PX),不能分两步。 - 释放时校验 token(Lua 原子判断)。
- 过期时间要大于业务最长耗时,必要时续期(watchdog)。
局限:
- Redis 主从异步复制,主节点宕机可能丢锁(Redlock 也解决不了强一致场景)。
- 对一致性要求极高的场景(如扣款)应使用数据库行锁或分布式协调服务。
十三、限流与计数
固定窗口:
INCR rate:127.0.0.1:202601131000
EXPIRE rate:127.0.0.1:202601131000 60 NX
滑动窗口(ZSet):
ZADD rate:127.0.0.1 <timestamp> <uuid>
ZREMRANGEBYSCORE rate:127.0.0.1 0 <timestamp-60000>
ZCARD rate:127.0.0.1
令牌桶可用 Lua 实现。固定窗口实现简单,但窗口边界会出现双倍突发。
十四、排行榜与集合运算
ZADD rank 1200 user:1
ZADD rank 1800 user:2
ZINCRBY rank 100 user:1
ZREVRANGE rank 0 9 WITHSCORES # 前 10
ZREVRANK rank user:1 # 名次
周榜/月榜:按周期生成不同 key(rank:2026w03),并设置过期时间。跨集合运算用 ZUNIONSTORE / SUNIONSTORE。
十五、Stream 消息队列
# 生产
XADD events * type order.created orderId 1001
# 消费(自动确认)
XREADGROUP GROUP g1 c1 COUNT 10 STREAMS events >
# 创建消费组
XGROUP CREATE events g1 $ MKSTREAM
# 确认
XACK events g1 <id>
# 查看待处理
XPENDING events g1
# 认领超时未确认的消息
XCLAIM events g1 c2 60000 <id>
Stream 相比 List 的优势:消费组、消息确认、失败重试、待处理列表。适合轻量事件流;需要复杂路由与持久化保证时用 Kafka/RabbitMQ。
十六、主从、哨兵与集群
| 架构 | 能力 | 适用 |
|---|---|---|
| 单机 | 无冗余 | 本地开发 |
| 主从复制 | 读写分离、备份 | 读多写少 |
| 哨兵 Sentinel | 自动故障转移 | 中小规模生产 |
| Cluster | 分片、水平扩展 | 大数据量、高并发 |
主从配置:
# 从节点
replicaof 10.0.0.1 6379
masterauth <password>
哨兵至少要三个节点才能可靠投票。Cluster 的数据按 16384 个槽分片,多 key 操作要求 key 在同一槽(可用 hash tag:{user1}:name 与 {user1}:age 同槽)。
十七、监控与排查
INFO # 全量信息
INFO memory # used_memory / maxmemory / fragmentation_ratio
INFO stats # 命中率 keyspace_hits / keyspace_misses
INFO replication # 主从状态
INFO clients # 连接数
SLOWLOG GET 10
LATENCY DOCTOR
MEMORY USAGE key
MEMORY DOCTOR
CLIENT LIST
关键指标:
| 指标 | 关注点 |
|---|---|
| 命中率 | keyspace_hits / (hits + misses),低于 90% 需检查 key 设计 |
| 内存碎片率 | mem_fragmentation_ratio 大于 1.5 考虑重启或 activedefrag |
| 连接数 | 接近 maxclients 时新连接被拒 |
| 慢查询 | SLOWLOG 中耗时超过 10ms 的命令 |
| 阻塞命令 | KEYS、FLUSHALL、大 key 的 DEL |
大 key 处理:用 UNLINK 异步删除;拆分大 Hash/List;避免 KEYS *。
十八、安全配置
requirepass <强密码>
bind 127.0.0.1 10.0.0.5
protected-mode yes
rename-command FLUSHALL ""
rename-command CONFIG ""
- 绝不要把 6379 暴露到公网(未授权访问是常见入侵入口)。
- 使用 ACL 按应用最小权限:
ACL SETUSER app on >password ~app:* +@read +@write -@admin
ACL LIST
ACL WHOAMI
十九、常见错误
| 错误 | 原因 | 处理 |
|---|---|---|
NOAUTH Authentication required |
未认证 | 连接时带密码,或先 AUTH |
WRONGTYPE Operation against a key holding the wrong kind of value |
key 类型冲突 | 检查 key 命名,避免不同类型共用前缀 |
OOM command not allowed |
达到 maxmemory 且策略为 noeviction | 调整策略或扩容 |
ERR max number of clients reached |
连接数耗尽 | 检查连接泄漏、调大 maxclients、加连接池 |
MISCONF Redis is configured to save RDB snapshots... |
RDB 写入失败 | 检查磁盘空间与权限 |
CROSSSLOT Keys in request don't hash to the same slot |
集群模式下多 key 不同槽 | 用 hash tag 归并 |
读取 nil 但 key 存在 |
存的是空字符串/序列化问题 | 检查写入逻辑 |
| 内存不降 | 过期 key 未回收、碎片 | MEMORY DOCTOR、activedefrag |
二十、FAQ
Q1:Redis 能做数据库吗?
技术上可以(开 AOF + 主从),但成本高、查询能力弱。定位应为缓存与协调层,数据源放在关系库或文档库。
Q2:KEYS * 为什么不能用?
单线程执行,会遍历全部 key 并阻塞其它请求。用 SCAN 游标遍历。
Q3:DEL 大 key 卡住怎么办?
用 UNLINK 异步删除;或拆分 big key。
Q4:SETNX + EXPIRE 为什么不行?
两步不是原子的,中间崩溃会导致锁永不过期。用 SET key value NX PX ms。
Q5:缓存和数据库不一致怎么办?
先更新库再删缓存,失败重试;关键数据用短 TTL;读多写少场景可接受短暂不一致。
Q6:多 key 操作在集群下报错?
Cluster 要求同槽,用 {tag}:key 让相关 key 落同一槽。
Q7:maxmemory 设了还是被 OOM 杀?
可能未设置淘汰策略(noeviction),或内存碎片导致实际占用超限。检查 used_memory 与 used_memory_rss。
Q8:持久化开哪个?
缓存场景可只开 RDB;需要少丢数据时开 AOF everysec;两者同开时重启优先加载 AOF。
Q9:Redis 单线程为什么快?
操作在内存、无磁盘 IO、无锁竞争、网络用多路复用。6.0 起网络 IO 可多线程,但命令执行仍是单线程。
Q10:生产用哨兵还是集群?
数据量能在单机内存内、只需高可用 → 哨兵;数据量或写入超过单机 → 集群。
附录:命令速查
# 连接
redis-cli -h host -p port -a password
AUTH password
SELECT 0
PING
# String
SET k v [EX s] [PX ms] [NX|XX]
GET k MGET k1 k2 MSET k1 v1 k2 v2
INCR k INCRBY k n DECR k
APPEND k v STRLEN k GETSET k v
# Hash
HSET h f v [f v ...] HGET h f HGETALL h HDEL h f
HINCRBY h f n HKEYS h HVALS h HLEN h HEXISTS h f
# List
LPUSH l v RPUSH l v LPOP l RPOP l LRANGE l 0 -1
LLEN l LREM l 0 v LTRIM l 0 99 BLPOP l 5
# Set
SADD s v SMEMBERS s SISMEMBER s v SREM s v SCARD s
SINTER s1 s2 SUNION s1 s2 SPOP s
# ZSet
ZADD z score member ZINCRBY z n m ZSCORE z m
ZRANGE z 0 -1 [WITHSCORES] ZREVRANGE z 0 9 ZRANK z m
ZREMRANGEBYSCORE z min max ZCARD z
# 键
EXISTS k TYPE k DEL k UNLINK k
EXPIRE k s TTL k PERSIST k
SCAN 0 MATCH pattern COUNT 100
# 其他结构
SETBIT b off val BITCOUNT b
PFADD hll v PFCOUNT hll
GEOADD g lon lat m GEOSEARCH ...
# 管理
INFO [section] DBSIZE SLOWLOG GET 10
BGSAVE BGREWRITEAOF LASTSAVE
MEMORY USAGE k MEMORY DOCTOR
CLIENT LIST CLIENT KILL ID id
CONFIG GET maxmemory CONFIG SET maxmemory 512mb
ACL SETUSER / ACL LIST