logo Fonnpo

联系我
2026年5月30日 2 分钟阅读 Fonnpo

Redis 详细教程

Redis 是高性能的内存键值数据库,支持 String、Hash、List、Set、ZSet 等丰富数据结构。本文从安装连接开始,覆盖所有核心数据类型、过期时间、持久化(RDB/AOF/混合)、发布订阅、事务、Pipeline,以及缓存穿透、击穿、雪崩三大问题的解决方案,一文掌握 Redis 实战所需的所有知识。

#Redis#Database#Cache

Redis 是一个基于内存的键值存储,支持丰富的数据结构,常用于缓存、消息队列、分布式锁、排行榜等场景。本文从安装开始,覆盖所有核心数据类型和常见实践。

安装与连接

macOS

Bash
        brew install redis
brew services start redis   # 后台启动
redis-cli                   # 进入交互式客户端

    

Linux(Ubuntu / Debian)

Bash
        sudo apt update && sudo apt install redis-server
sudo systemctl start redis
redis-cli

    

Docker(最推荐,环境干净)

Bash
        # 启动容器
docker run -d --name redis -p 6379:6379 redis:7-alpine

# 进入 CLI
docker exec -it redis redis-cli

    

基础连接与验证

Bash
        redis-cli -h 127.0.0.1 -p 6379

# 测试连通性
PING              # 返回 PONG

# 如果设置了密码
AUTH your_password

# 选择数据库(默认 0,共 16 个)
SELECT 1

# 查看当前库的 key 数量
DBSIZE

# 查看所有 key(生产环境慎用,会阻塞)
KEYS *

# 模糊查找(推荐用 SCAN 替代 KEYS)
SCAN 0 MATCH user:* COUNT 100

    

数据类型总览

类型典型用途
String缓存、计数器、分布式锁
Hash对象存储(用户信息、商品详情)
List消息队列、最新动态、操作日志
Set去重标签、共同好友、抽奖
ZSet(有序集合)排行榜、带权重的队列
Stream持久化消息队列(Kafka 轻量替代)
Bitmap用户签到、布隆过滤器
HyperLogLog超大规模 UV 统计(近似值)

String(字符串)

String 是最基础的类型,可以存文本、数字、二进制,最大 512 MB。

Bash
        # 存取
SET name "Alice"
GET name              # "Alice"

# 设置并指定过期时间(秒)
SET token "abc123" EX 3600

# 只有 key 不存在时才设置(分布式锁的基础)
SET lock "1" NX EX 10

# 批量操作
MSET k1 v1 k2 v2 k3 v3
MGET k1 k2 k3

# 追加内容
APPEND name " Smith"  # "Alice Smith"

# 数字操作
SET counter 0
INCR counter          # 1
INCRBY counter 5      # 6
DECR counter          # 5
DECRBY counter 2      # 3

# 浮点数递增
SET price 9.99
INCRBYFLOAT price 0.5  # 10.49

# 获取字符串长度
STRLEN name

# 获取旧值并设置新值
GETSET name "Bob"     # 返回 "Alice Smith",设置为 "Bob"

    
收起

Hash(哈希)

Hash 适合存储结构化对象,避免频繁序列化/反序列化。

Bash
        # 设置单个字段
HSET user:1 name "Alice" age 25 email "alice@example.com"

# 获取单个字段
HGET user:1 name      # "Alice"

# 获取所有字段和值
HGETALL user:1

# 获取所有字段名
HKEYS user:1

# 获取所有字段值
HVALS user:1

# 获取字段数量
HLEN user:1

# 判断字段是否存在
HEXISTS user:1 email   # 1(存在)

# 删除字段
HDEL user:1 email

# 批量获取指定字段
HMGET user:1 name age

# 数字字段自增
HINCRBY user:1 age 1

# 只有字段不存在时才设置
HSETNX user:1 role "admin"

    
收起

List(列表)

List 是双端链表,支持从两端 push/pop,适合队列和栈。

Bash
        # 从左侧推入(头部)
LPUSH logs "event1" "event2" "event3"

# 从右侧推入(尾部)
RPUSH queue "task1" "task2"

# 从左侧弹出
LPOP logs

# 从右侧弹出
RPOP queue

# 阻塞弹出(0 表示永久等待,适合消费者场景)
BLPOP queue 0
BRPOP queue 30     # 最多等 30 秒

# 查看范围(0 -1 表示全部)
LRANGE logs 0 -1

# 获取指定下标的值
LINDEX logs 0

# 获取列表长度
LLEN logs

# 修剪,只保留指定范围
LTRIM logs 0 99    # 只保留前 100 条

# 在某个元素前/后插入
LINSERT logs BEFORE "event2" "new-event"

# 移除指定个数的匹配值
LREM logs 2 "event1"  # 删除 2 个值为 event1 的元素

    
收起

经典用法:消息队列

Bash
        # 生产者
RPUSH queue "msg1"

# 消费者(阻塞式,有消息立即返回)
BLPOP queue 0

    

Set(集合)

Set 是无序不重复集合,支持集合运算。

Bash
        # 添加成员
SADD tags "redis" "cache" "nosql"

# 判断是否存在
SISMEMBER tags "redis"   # 1

# 获取所有成员
SMEMBERS tags

# 集合大小
SCARD tags

# 移除成员
SREM tags "nosql"

# 随机取出(不移除)
SRANDMEMBER tags 2

# 随机弹出并移除
SPOP tags

# 集合运算
SADD set1 "a" "b" "c"
SADD set2 "b" "c" "d"

SUNION set1 set2       # 并集:a b c d
SINTER set1 set2       # 交集:b c
SDIFF set1 set2        # 差集:a(在 set1 但不在 set2)

# 将运算结果存入新 key
SUNIONSTORE result set1 set2
SINTERSTORE result set1 set2

    
收起

ZSet(有序集合)

ZSet 中每个成员都有一个分数(score),按分数排序,适合排行榜。

Bash
        # 添加成员(分数 成员名)
ZADD leaderboard 1000 "Alice" 850 "Bob" 1200 "Charlie"

# 获取成员的分数
ZSCORE leaderboard "Alice"   # 1000

# 按分数从低到高排名(0 表示第 1 名)
ZRANK leaderboard "Alice"    # 1(0-indexed)

# 按分数从高到低排名
ZREVRANK leaderboard "Alice" # 1

# 按排名范围查询(从低到高)
ZRANGE leaderboard 0 -1 WITHSCORES

# 按排名范围查询(从高到低)
ZREVRANGE leaderboard 0 2 WITHSCORES

# 按分数范围查询
ZRANGEBYSCORE leaderboard 800 1100 WITHSCORES

# 分数自增
ZINCRBY leaderboard 100 "Alice"   # 1100

# 成员数量
ZCARD leaderboard

# 指定分数范围内的成员数
ZCOUNT leaderboard 900 1200

# 移除成员
ZREM leaderboard "Bob"

# 移除排名范围内的成员
ZREMRANGEBYRANK leaderboard 0 1

# 移除分数范围内的成员
ZREMRANGEBYSCORE leaderboard 0 800

    
收起

过期时间

Bash
        # 设置过期时间(秒)
EXPIRE key 3600

# 设置过期时间(毫秒)
PEXPIRE key 3600000

# 设置到具体时间戳(Unix 秒)
EXPIREAT key 1893456000

# 查看剩余时间(秒,-1 表示永不过期,-2 表示 key 不存在)
TTL key

# 查看剩余时间(毫秒)
PTTL key

# 移除过期时间(变为永久)
PERSIST key

    

持久化

Redis 默认是内存数据库,重启数据会丢失。持久化有两种方式:

RDB(快照)

定期将内存数据写入磁盘,是默认的持久化方式。

Bash
        # redis.conf 配置(每 60 秒,至少 1 个 key 变更就保存)
save 60 1
save 300 10
save 900 1

# 手动触发快照
BGSAVE      # 后台异步保存
SAVE        # 同步保存(会阻塞,不推荐生产使用)

    

优点:文件紧凑,恢复快。
缺点:两次快照之间的数据有丢失风险。

AOF(追加日志)

记录每条写操作命令,重启时重放来恢复数据。

Bash
        # redis.conf 开启 AOF
appendonly yes
appendfilename "appendonly.aof"

# 同步策略
appendfsync always    # 每次写都同步,最安全,性能最低
appendfsync everysec  # 每秒同步一次(推荐,最多丢 1 秒数据)
appendfsync no        # 由 OS 决定,性能最高,但风险最大

    

优点:数据安全,最多丢 1 秒。
缺点:文件比 RDB 大,恢复慢。

混合持久化(推荐,Redis 4.0+)

Bash
        # redis.conf
aof-use-rdb-preamble yes

    

AOF 文件开头存 RDB 快照,后面追加增量命令,兼顾速度和安全性。

发布 / 订阅

Bash
        # 订阅频道(会阻塞等待消息)
SUBSCRIBE news sports

# 发布消息
PUBLISH news "Breaking: Redis 8.0 released"

# 模式订阅
PSUBSCRIBE news.*

# 查看订阅信息
PUBSUB CHANNELS        # 活跃的频道列表
PUBSUB NUMSUB news     # 某频道的订阅者数量

    

注意:发布订阅是"即发即忘",没有持久化。需要可靠消息队列,推荐使用 Stream 或 Redis + MQ。

事务

Redis 事务通过 MULTI / EXEC 实现,保证命令的原子性执行(但不支持回滚)。

Bash
        MULTI          # 开启事务
SET k1 "v1"   # 加入队列
SET k2 "v2"
INCR counter
EXEC           # 执行所有命令

# 放弃事务
DISCARD

    

Watch(乐观锁):

Bash
        WATCH balance         # 监听 key

MULTI
DECRBY balance 100
EXEC                  # 如果 balance 在 WATCH 之后被其他客户端修改,EXEC 返回 nil

    

Pipeline(批量请求)

Pipeline 将多条命令打包一次性发送,减少网络往返,大幅提升吞吐量。

Bash
        # redis-cli 中
redis-cli --pipe << EOF
SET k1 v1
SET k2 v2
INCR counter
EOF

    

各语言客户端都有 Pipeline 支持,以 Node.js(ioredis)为例:

Javascript
        const pipeline = redis.pipeline()
pipeline.set('k1', 'v1')
pipeline.set('k2', 'v2')
pipeline.incr('counter')
await pipeline.exec()

    

常见使用场景

缓存

Bash
        # 旁路缓存模式(Cache Aside)
# 1. 读:先查 Redis,命中直接返回;未命中查数据库,再写入 Redis
# 2. 写:先更新数据库,再删除 Redis 缓存(保证一致性)

SET user:1:profile "{...}" EX 300

    

分布式锁

Bash
        # 加锁:NX 保证只有一个客户端能设置成功,EX 避免死锁
SET lock:order:123 "client-uuid" NX EX 30

# 释放锁:必须校验 value 是自己的,再删除(防止误删他人的锁)
# 用 Lua 脚本保证原子性
EVAL "
  if redis.call('get', KEYS[1]) == ARGV[1] then
    return redis.call('del', KEYS[1])
  else
    return 0
  end
" 1 lock:order:123 client-uuid

    

计数器与限流

Bash
        # 接口限流:每个用户每分钟最多 100 次
INCR rate:user:123
EXPIRE rate:user:123 60

# 或使用原子操作
SET rate:user:123 0 EX 60 NX  # 第一次请求时初始化
INCR rate:user:123             # 每次请求递增

    

排行榜

Bash
        # 更新分数
ZINCRBY leaderboard 10 "user:123"

# 获取 Top 10
ZREVRANGE leaderboard 0 9 WITHSCORES

# 查询某用户排名
ZREVRANK leaderboard "user:123"

    

Session 存储

Bash
        SET session:abc123 "{userId: 1, role: admin}" EX 86400
GET session:abc123
EXPIRE session:abc123 86400   # 续期

    

缓存三大问题

缓存穿透

现象: 请求的 key 在缓存和数据库中都不存在,每次都打到数据库。

解决:

  1. 缓存空值:即使数据库没有,也在 Redis 缓存一个空值,并设置较短过期时间
  2. 布隆过滤器:请求进来先判断是否可能存在,不存在直接拒绝

缓存击穿

现象: 某个热点 key 过期的瞬间,大量请求同时打到数据库。

解决:

  1. 热点 key 不过期(或逻辑过期)
  2. 互斥锁:第一个请求查数据库并重建缓存,其余等待

缓存雪崩

现象: 大量 key 在同一时间集中过期,导致数据库被瞬间压垮。

解决:

  1. 过期时间加随机值,避免集中过期
  2. 热点数据永不过期,异步刷新
  3. 服务降级 + 熔断保护数据库

常用管理命令

Bash
        # 查看 Redis 信息(内存、连接、持久化等)
INFO
INFO memory
INFO server

# 查看慢查询日志
SLOWLOG GET 10

# 清空当前数据库(慎用)
FLUSHDB

# 清空所有数据库(慎用)
FLUSHALL

# 查看 key 类型
TYPE key

# 删除 key
DEL key1 key2

# 异步删除(大 key 推荐,避免阻塞)
UNLINK key

# 重命名 key
RENAME old-key new-key

# 查看内存使用(单个 key)
MEMORY USAGE key

    
Fonnpo