目录
正在加载目录…
专栏文章
专栏文章
Redis 专栏
1. Redis:核心数据结构与应用场景 2. Redis 数据结构:底层实现与性能取舍 3. Redis 持久化与复制:RDB、AOF 与主从同步 4. Redis 缓存设计:穿透、击穿与雪崩治理 5. Redis 高可用:哨兵、集群与故障转移 6. Redis 多级缓存:一致性、失效与性能优化

Redis:核心数据结构与应用场景

发布于 2026-07-07 10:09 · 最后编辑于 2026-07-31 15:51 · 字数 2,455 👁 93 次阅读

Redis(Remote Dictionary Server)是由 Salvatore Sanfilippo 于 2009 年开发的开源内存数据库,以极高性能、丰富数据结构、原子操作著称。 单线程命令处理模型(6.0+ 支持多线程 IO),读写性能可达 10 万+ QPS,广泛用于缓存、分布式锁、消息队列、排行榜等场景。

⚠️ 版本说明:本文以 Redis 7.x 为基准。7.0 引入了 Redis Functions、Listpack 优化等特性。

目录

章节说明
核心特性单线程模型、与 Memcached 对比、适用场景
安装与配置安装、redis.conf 常用配置、CLI 操作
数据类型String、List、Hash、Set、ZSet 及命令
高级数据类型Bitmap、HyperLogLog、Geo、Stream
过期与淘汰TTL 设置、过期策略、内存淘汰策略
持久化RDB、AOF、混合持久化
高可用主从复制、哨兵模式、Cluster 集群
常用场景缓存、分布式锁、限流、排行榜、消息队列
快速参考卡数据类型速查、常用命令速查

核心特性

为什么 Redis 这么快

原因说明
纯内存操作数据存储在内存,读写无磁盘 IO
单线程命令处理避免多线程上下文切换和锁竞争
IO 多路复用epoll/kqueue 处理大量并发连接
高效数据结构SDS、跳表、压缩列表等专为内存优化
6.0+ 多线程 IO网络读写多线程,命令执行仍单线程

与 Memcached 对比

维度RedisMemcached
数据结构丰富(5 种基础 + 扩展)只有 String
持久化RDB + AOF不支持
集群原生 Cluster客户端分片
事务支持(MULTI/EXEC)不支持
Lua 脚本支持不支持
发布订阅支持不支持
内存效率略低略高

安装与配置

安装

# macOS
brew install redis
brew services start redis

# 验证
redis-cli ping   # 返回 PONG

# Docker
docker run -d --name redis -p 6379:6379 redis:7-alpine
docker exec -it redis redis-cli

redis.conf 常用配置

# 网络
bind 127.0.0.1           # 绑定地址(生产环境不要 bind 0.0.0.0 且无密码)
port 6379
protected-mode yes        # 保护模式,无密码时禁止外网访问

# 安全
requirepass yourpassword  # 设置密码

# 内存
maxmemory 2gb             # 最大内存限制
maxmemory-policy allkeys-lru  # 内存淘汰策略

# 持久化
save 900 1                # 900 秒内有 1 次修改则 RDB 快照
save 300 10               # 300 秒内有 10 次修改
save 60 10000             # 60 秒内有 10000 次修改
appendonly yes            # 开启 AOF
appendfsync everysec      # AOF 每秒刷盘(推荐)

# 日志
loglevel notice
logfile /var/log/redis/redis.log

# 慢查询
slowlog-log-slower-than 10000  # 超过 10ms 记录慢查询(单位微秒)
slowlog-max-len 128

redis-cli 常用操作

# 连接
redis-cli -h 127.0.0.1 -p 6379 -a yourpassword

# 基础命令
redis-cli ping                    # 测试连通性
redis-cli info                    # 查看服务器信息
redis-cli info memory             # 内存信息
redis-cli info replication        # 主从信息
redis-cli dbsize                  # 当前 DB 的 key 数量
redis-cli flushdb                 # 清空当前 DB(⚠️ 谨慎)
redis-cli flushall                # 清空所有 DB(⚠️ 危险)

# 慢查询
redis-cli slowlog get 10          # 查看最近 10 条慢查询
redis-cli slowlog len             # 慢查询日志条数

# 监控
redis-cli monitor                 # 实时打印所有命令(调试用,生产慎用)
redis-cli --latency               # 延迟测试

数据类型

redis data structures

String(字符串)

最基础的类型,底层是 SDS(Simple Dynamic String),可存字符串、整数、二进制数据,最大 512MB。

# 基本操作
SET key "hello"
GET key                    # "hello"
DEL key
EXISTS key                 # 1(存在)/ 0(不存在)

# 带过期时间
SET key "value" EX 60      # 60 秒后过期
SET key "value" PX 60000   # 60000 毫秒后过期
SETEX key 60 "value"       # 等价于 SET ... EX
TTL key                    # 查看剩余秒数,-1=永不过期,-2=不存在

# 原子操作(不存在时才设置,用于分布式锁)
SET key "value" NX EX 30   # NX = Not eXists,原子性
SETNX key "value"          # 旧写法,不推荐(无法同时设置过期时间)

# 计数器(整数值原子递增/递减)
SET counter 0
INCR counter               # 1
INCRBY counter 5           # 6
DECR counter               # 5
DECRBY counter 2           # 3

# 批量操作(减少网络往返)
MSET k1 v1 k2 v2 k3 v3
MGET k1 k2 k3              # ["v1", "v2", "v3"]

# 追加
APPEND key " world"        # 追加到末尾,返回新长度
STRLEN key                 # 字符串长度

# 获取并设置
GETSET key "new_value"     # 返回旧值并设置新值
GETEX key EX 60            # 获取值同时刷新过期时间(Redis 6.2+)

List(列表)

双端链表(元素少时用 Listpack 压缩),支持从两端推入/弹出,天然适合队列和栈。

# 推入
LPUSH list a b c           # 从左推入,结果:[c, b, a]
RPUSH list x y z           # 从右推入,结果:[c, b, a, x, y, z]

# 弹出
LPOP list                  # 弹出左端:c
RPOP list                  # 弹出右端:z
LPOP list 3                # 弹出左端 3 个(Redis 6.2+)

# 阻塞弹出(用于消费者等待队列,timeout=0 永久阻塞)
BLPOP list 10              # 阻塞等待 10 秒,有元素立即返回
BRPOP list 10

# 查看
LRANGE list 0 -1           # 查看所有元素(-1 表示最后一个)
LRANGE list 0 9            # 查看前 10 个
LLEN list                  # 列表长度
LINDEX list 0              # 获取指定索引的元素

# 修改与删除
LSET list 0 "new_val"      # 设置指定索引的值
LREM list 2 "a"            # 删除 2 个值为 "a" 的元素(正数从头,负数从尾)
LTRIM list 0 99            # 只保留索引 0~99 的元素(常用于限制列表长度)

Hash(哈希)

键值对集合,底层用 Listpack(元素少)或 HashTable,适合存储对象。

# 设置
HSET user:1 name "Alice" age 30 email "alice@example.com"
HSET user:1 city "Beijing"     # 新增字段
HSETNX user:1 name "Bob"       # 字段不存在时才设置

# 获取
HGET user:1 name               # "Alice"
HMGET user:1 name age          # ["Alice", "30"]
HGETALL user:1                 # 所有字段和值

# 查看
HKEYS user:1                   # 所有字段名
HVALS user:1                   # 所有字段值
HLEN user:1                    # 字段数量
HEXISTS user:1 email           # 字段是否存在

# 删除
HDEL user:1 city

# 计数
HINCRBY user:1 age 1           # age 加 1,返回新值 31
HINCRBYFLOAT user:1 score 0.5  # 浮点递增

Set(集合)

无序、不重复的字符串集合,支持集合运算,底层用 Listpack 或 HashTable。

# 添加与删除
SADD tags "java" "redis" "mysql"
SREM tags "mysql"

# 查询
SMEMBERS tags              # 所有成员(无序)
SCARD tags                 # 成员数量
SISMEMBER tags "java"      # 是否存在:1/0
SMISMEMBER tags "java" "go" # 批量判断(Redis 6.2+)

# 随机操作
SRANDMEMBER tags 2         # 随机获取 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            # 差集(set1 有 set2 没有):[a]

# 存储运算结果
SUNIONSTORE dest set1 set2
SINTERSTORE dest set1 set2

ZSet(有序集合)

每个成员关联一个 score(浮点分数),按 score 排序,底层用 Listpack 或跳表(SkipList)。

# 添加
ZADD leaderboard 100 "Alice"
ZADD leaderboard 95 "Bob" 88 "Charlie"
ZADD leaderboard NX 120 "Dave"    # 不存在时才添加
ZADD leaderboard XX 110 "Alice"   # 只更新已存在的成员

# 查询(按排名)
ZRANGE leaderboard 0 -1              # 升序所有成员
ZRANGE leaderboard 0 -1 WITHSCORES  # 带分数
ZREVRANGE leaderboard 0 2            # 降序前 3 名(旧写法)
ZRANGE leaderboard 0 2 REV WITHSCORES  # 降序带分数(Redis 6.2+)

# 查询(按分数)
ZRANGEBYSCORE leaderboard 90 120         # score 在 90~120 的成员
ZRANGEBYSCORE leaderboard -inf +inf      # 所有成员
ZRANGEBYSCORE leaderboard 90 +inf LIMIT 0 10  # 分页

# 排名
ZRANK leaderboard "Alice"            # 升序排名(0 开始)
ZREVRANK leaderboard "Alice"         # 降序排名
ZSCORE leaderboard "Alice"           # 获取分数

# 修改分数
ZINCRBY leaderboard 10 "Bob"         # Bob 分数加 10

# 删除
ZREM leaderboard "Charlie"
ZREMRANGEBYRANK leaderboard 0 1      # 删除排名 0~1 的成员
ZREMRANGEBYSCORE leaderboard 0 60    # 删除分数 0~60 的成员

# 数量
ZCARD leaderboard                    # 成员总数
ZCOUNT leaderboard 90 100            # score 在 90~100 的成员数

高级数据类型

Bitmap(位图)

# 用 bit 记录用户签到(节省内存:1 亿用户只需 12.5MB)
SETBIT sign:user:1001 20240101 1   # 用户 1001 在第 20240101 天签到
GETBIT sign:user:1001 20240101     # 查询:1
BITCOUNT sign:user:1001            # 统计签到总天数
BITCOUNT sign:user:1001 0 6        # 统计第 0~6 字节范围内的 1 的数量
BITPOS sign:user:1001 1            # 第一个签到的 bit 位置

HyperLogLog(基数统计)

# 近似去重计数,误差约 0.81%,每个 key 最多 12KB
PFADD uv:page1 "user1" "user2" "user3"
PFADD uv:page1 "user1"             # 重复,不计入
PFCOUNT uv:page1                   # 约等于 3

# 合并多个 HyperLogLog
PFMERGE uv:total uv:page1 uv:page2
PFCOUNT uv:total

Geo(地理位置)

# 添加地理位置(底层用 ZSet 存储)
GEOADD locations 116.397128 39.916527 "北京"
GEOADD locations 121.480539 31.235929 "上海"
GEOADD locations 113.264385 23.129112 "广州"

# 获取坐标
GEOPOS locations "北京"

# 计算两点距离
GEODIST locations "北京" "上海" km   # 单位:m/km/mi/ft

# 查找附近的点(6.2+ 推荐 GEOSEARCH)
GEOSEARCH locations FROMMEMBER "北京" BYRADIUS 1500 km ASC COUNT 5

Stream(消息流)

# 生产者:添加消息(* 表示自动生成 ID)
XADD orders * user_id 1001 product_id 2001 amount 99.9
XADD orders * user_id 1002 product_id 3001 amount 199.9

# 消费者:读取消息
XRANGE orders - +                  # 读取所有消息
XRANGE orders - + COUNT 10         # 读取最多 10 条
XLEN orders                        # 消息数量

# 消费者组(保证消息不重复消费)
XGROUP CREATE orders order-group $ MKSTREAM
XREADGROUP GROUP order-group consumer1 COUNT 10 STREAMS orders >
# > 表示读取未投递给该组的新消息

# 确认消费
XACK orders order-group <message-id>

过期与淘汰

设置过期时间

EXPIRE key 60              # 60 秒后过期
PEXPIRE key 60000          # 60000 毫秒后过期
EXPIREAT key 1735689600    # Unix 时间戳过期
PEXPIREAT key 1735689600000

TTL key                    # 剩余秒数(-1=永不过期,-2=key不存在)
PTTL key                   # 剩余毫秒数
PERSIST key                # 移除过期时间,变为永久

过期删除策略

策略说明
惰性删除访问时检查是否过期,过期则删除;节省 CPU,可能内存泄漏
定期删除每 100ms 随机抽取一批 key 检查删除;两种策略配合使用

内存淘汰策略(maxmemory-policy)

策略说明适用场景
noeviction内存满时写入报错(默认)不允许丢数据
allkeys-lru所有 key 中淘汰最近最少使用缓存场景首选
allkeys-lfu所有 key 中淘汰最不常用(7.0+ 优化)热点数据明显时
volatile-lru只淘汰有过期时间的 key(LRU)混合存储场景
volatile-lfu只淘汰有过期时间的 key(LFU)混合存储场景
volatile-ttl淘汰剩余 TTL 最短的 key
allkeys-random随机淘汰所有 key

持久化

RDB(快照)

# 手动触发
SAVE        # 同步(阻塞主线程,生产慎用)
BGSAVE      # 异步(fork 子进程,推荐)

# 查看最后一次 RDB 保存时间
LASTSAVE    # 返回 Unix 时间戳

# 配置(redis.conf)
save 900 1        # 900 秒内有 1 次修改
save 300 10
save 60 10000
dbfilename dump.rdb
dir /var/lib/redis
优点缺点
文件紧凑,恢复速度快可能丢失最后一次快照后的数据
fork 子进程,不影响主线程数据量大时 fork 耗时

AOF(追加日志)

# 配置
appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec     # always(最安全)/ everysec(推荐)/ no(最快)

# 手动触发 AOF 重写(压缩 AOF 文件)
BGREWRITEAOF

# 自动重写配置
auto-aof-rewrite-percentage 100   # AOF 文件比上次重写后增长 100% 时触发
auto-aof-rewrite-min-size 64mb    # AOF 文件最小 64MB 才触发重写
优点缺点
数据最多丢 1 秒(everysec)文件比 RDB 大
可读性强,便于数据恢复恢复速度比 RDB 慢

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

# redis.conf
aof-use-rdb-preamble yes   # AOF 文件头部嵌入 RDB 快照,尾部追加 AOF 日志

兼顾 RDB 的快速恢复和 AOF 的数据安全,生产环境推荐开启。

高可用

主从复制

# 从库配置(redis.conf)
replicaof 192.168.1.100 6379
masterauth yourpassword

# 查看复制状态
INFO replication
# 关注:role、master_link_status、master_last_io_seconds_ago

哨兵模式(Sentinel)

# sentinel.conf
sentinel monitor mymaster 192.168.1.100 6379 2  # 2 个哨兵同意才故障转移
sentinel auth-pass mymaster yourpassword
sentinel down-after-milliseconds mymaster 30000  # 30 秒无响应则认为主库下线
sentinel failover-timeout mymaster 180000

# 启动哨兵
redis-sentinel /etc/redis/sentinel.conf

# 查询主库地址(客户端通过哨兵发现主库)
redis-cli -p 26379 SENTINEL get-master-addr-by-name mymaster

Cluster 集群(生产推荐)

16384 个 slot(哈希槽)分配给各节点
key 通过 CRC16(key) % 16384 映射到对应 slot
每个主节点负责一段 slot,配置至少 3 主 3 从
# 创建集群(redis-cli 方式)
redis-cli --cluster create \
  127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 \
  127.0.0.1:7004 127.0.0.1:7005 127.0.0.1:7006 \
  --cluster-replicas 1

# 查看集群状态
redis-cli -p 7001 CLUSTER INFO
redis-cli -p 7001 CLUSTER NODES

# 连接集群(-c 自动重定向)
redis-cli -c -p 7001

常用场景

缓存(最常见)

# 缓存穿透防护:缓存空值
SET cache:user:999 "NULL" EX 60   # 不存在的 key 也缓存短时间

# 缓存击穿防护:热点 key 永不过期 + 后台异步刷新
SET cache:hot:product:1 "{...}" EX 3600

# 缓存雪崩防护:过期时间加随机值
# Java 伪代码:ttl = baseTtl + random.nextInt(300)

分布式锁

# 加锁(原子性:SET NX EX)
SET lock:order:1001 "uuid-xxx" NX EX 30
# NX:不存在才设置(互斥)
# EX 30:30 秒自动释放(防止死锁)
# value 用唯一值(防止误删其他线程的锁)

# 解锁(Lua 脚本保证原子性:判断 + 删除)
# 伪代码等价:
# if GET lock:order:1001 == "uuid-xxx" then DEL lock:order:1001 end
EVAL "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end" 1 lock:order:1001 uuid-xxx

# 查看锁状态
GET lock:order:1001
TTL lock:order:1001

限流(滑动窗口)

# 固定窗口限流:1 分钟内最多 100 次
INCR rate:user:1001:202401011200   # key 包含分钟时间戳
EXPIRE rate:user:1001:202401011200 60

# 使用 Lua 脚本保证原子性
EVAL "
  local key = KEYS[1]
  local limit = tonumber(ARGV[1])
  local current = redis.call('incr', key)
  if current == 1 then
    redis.call('expire', key, ARGV[2])
  end
  return current
" 1 rate:user:1001 100 60

排行榜

# 实时排行榜(ZSet 天然支持)
ZADD rank:daily 1500 "user:1001"
ZADD rank:daily 2000 "user:1002"
ZADD rank:daily 1800 "user:1003"

# 获取前 10 名(降序)
ZRANGE rank:daily 0 9 REV WITHSCORES

# 获取用户排名
ZREVRANK rank:daily "user:1001"    # 降序排名

# 增加积分
ZINCRBY rank:daily 100 "user:1001"

消息队列

# 简单队列(List)
RPUSH queue:email "msg1"           # 生产者
BLPOP queue:email 0                # 消费者(阻塞等待)

# 可靠队列(Stream,支持消费者组、ACK)
XADD stream:order * order_id 1001 amount 99
XGROUP CREATE stream:order grp1 0
XREADGROUP GROUP grp1 c1 COUNT 10 STREAMS stream:order >
XACK stream:order grp1 <msg-id>

快速参考卡

数据类型选型

场景数据类型理由
缓存对象(JSON)String简单直接
缓存对象(字段级访问)Hash可单独读写字段,节省内存
消息队列List / StreamList 简单,Stream 可靠
标签/关注关系Set去重 + 集合运算
排行榜/积分ZSet自动排序
签到/在线状态Bitmap极省内存
UV 统计(近似)HyperLogLog12KB 统计亿级基数
附近的人Geo内置距离计算
分布式锁String(SET NX EX)原子性 + 自动释放
计数器String(INCR)原子递增

常用命令速查

命令说明
KEYS pattern查找匹配的 key(⚠️ 生产禁用,用 SCAN)
SCAN 0 MATCH * COUNT 100安全迭代 key
TYPE key查看 key 的数据类型
OBJECT ENCODING key查看底层编码
DEBUG OBJECT key查看 key 的详细信息
DUMP key + RESTORE序列化与反序列化
OBJECT IDLETIME keykey 的空闲时间(秒)
MEMORY USAGE keykey 占用的内存字节数
WAIT 1 0等待至少 1 个从库同步完成
DEBUG SLEEP 5让 Redis 睡眠 5 秒(测试用)

性能排查命令

redis-cli INFO stats            # 命令统计、命中率
redis-cli INFO memory           # 内存使用详情
redis-cli INFO clients          # 连接数
redis-cli SLOWLOG GET 10        # 慢查询
redis-cli --bigkeys             # 扫描大 key(⚠️ 对生产有影响)
redis-cli --hotkeys             # 热 key 统计(需 maxmemory-policy 为 LFU)
redis-cli OBJECT FREQ key       # key 的访问频率(LFU 模式)

IO 多路复用与单线程模型

Redis 用一个主线程处理所有命令执行,靠 IO 多路复用(epoll)处理大量并发连接:

客户端连接 → epoll 事件循环 → 命令队列 → 单线程顺序执行 → 返回结果

Redis 6.0 多线程 IO

  • 网络读写(解析请求、写回响应)使用多线程
  • 命令执行仍然是单线程(保证原子性)
  • 配置:io-threads 4io-threads-do-reads yes

参考资料

⚠️ 深度专题笔记

  • 底层数据结构原理 → 02 Redis 底层数据结构.md
  • 持久化与主从复制 → 03 Redis 持久化与主从复制.md
  • 缓存设计与问题排查 → 04 Redis 缓存设计与问题处理.md
  • 高可用与集群 → 05 Redis 高可用与集群.md
← 返回列表

评论 (0)

暂无评论,来留下第一条吧。
登录注册 后才能发表评论