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>

必须满足的三个条件:

  1. 加锁与设置过期是同一条命令(SET NX PX),不能分两步。
  2. 释放时校验 token(Lua 原子判断)。
  3. 过期时间要大于业务最长耗时,必要时续期(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
← 返回帖子 更多数据库内容 →