3113 字
16 分钟
Redis 完全指南:从基础数据结构到集群部署与性能优化 | 2026最新实践
Redis 是当今最流行的内存数据库之一,广泛用于缓存、消息队列、分布式锁、排行榜等场景。2026 年,Redis 已成为现代 Web 应用的标配组件。本文从基础数据结构到集群部署,全面讲解 Redis 的完整知识体系。

本文内容包括:
- Redis 五大基础数据结构与使用场景
- 高级特性:发布订阅、事务、Lua 脚本、Stream
- 持久化机制:RDB 与 AOF 对比与配置
- 主从复制与读写分离
- 哨兵模式(Sentinel)自动故障转移
- Cluster 集群部署与数据分片
- 缓存策略:穿透/击穿/雪崩解决方案
- 性能优化与监控
- Python/Node.js 集成实战
一、Redis 数据结构
1.1 String(字符串)
# 基本操作SET name "Alice"GET name # "Alice"DEL name
# 计数器SET counter 100INCR counter # 101INCRBY counter 10 # 111DECR counter # 110
# 过期时间SET token "abc123" EX 3600 # 3600 秒后过期TTL token # 查看剩余时间PERSIST token # 移除过期时间
# 批量操作MSET k1 "v1" k2 "v2" k3 "v3"MGET k1 k2 k3# Python 示例import redis
r = redis.Redis(host='localhost', port=6379, db=0)
# 基本操作r.set('name', 'Alice', ex=3600) # 带过期时间name = r.get('name') # b'Alice'
# 计数器r.set('counter', 100)r.incr('counter') # 101r.incrby('counter', 10) # 111适用场景:缓存、计数器、分布式锁、Session 存储
1.2 List(列表)
# 队列操作(先进先出)LPUSH queue "msg1" "msg2" # 左侧插入RPOP queue # 右侧弹出
# 栈操作(后进先出)LPUSH stack "item1" "item2"LPOP stack
# 查看列表LRANGE queue 0 -1 # 查看所有元素LLEN queue # 列表长度
# 阻塞式弹出(消息队列)BRPOP queue 30 # 阻塞30秒等待消息适用场景:消息队列、最新文章列表、操作日志
1.3 Hash(哈希表)
# 存储对象HSET user:1 name "Alice" age 30 email "alice@example.com"HGET user:1 name # "Alice"HGETALL user:1 # 所有字段和值
# 批量设置HMSET user:2 name "Bob" age 25 email "bob@example.com"
# 数值字段操作HINCRBY user:1 age 1 # age + 1
# 删除字段HDEL user:1 email# Python 示例r.hset('user:1', mapping={ 'name': 'Alice', 'age': 30, 'email': 'alice@example.com'})user = r.hgetall('user:1')# {b'name': b'Alice', b'age': b'30', b'email': b'alice@example.com'}适用场景:用户信息、商品属性、配置存储
1.4 Set(集合)
# 集合操作SADD tags "python" "redis" "docker"SMEMBERS tags # 查看所有成员SISMEMBER tags "python" # 判断是否存在SCARD tags # 集合大小
# 集合运算SADD set1 "a" "b" "c"SADD set2 "b" "c" "d"SINTER set1 set2 # 交集 {"b", "c"}SUNION set1 set2 # 并集 {"a", "b", "c", "d"}SDIFF set1 set2 # 差集 {"a"}适用场景:标签系统、共同好友、去重
1.5 ZSet(有序集合)
# 排行榜ZADD ranking 100 "Alice" 85 "Bob" 92 "Charlie"
# 按分数排序ZRANGE ranking 0 -1 WITHSCORES # 升序ZREVRANGE ranking 0 -1 WITHSCORES # 降序ZREVRANGE ranking 0 2 WITHSCORES # Top 3
# 分数操作ZINCRBY ranking 10 "Bob" # Bob 分数+10
# 范围查询ZRANGEBYSCORE ranking 80 100 # 80-100分之间ZRANK ranking "Alice" # 排名(升序)ZREVRANK ranking "Alice" # 排名(降序)# Python 示例:游戏排行榜r.zadd('game_ranking', { 'Alice': 100, 'Bob': 85, 'Charlie': 92})
# 获取 Top 3top3 = r.zrevrange('game_ranking', 0, 2, withscores=True)# [(b'Alice', 100.0), (b'Charlie', 92.0), (b'Bob', 85.0)]
# 增加分数r.zincrby('game_ranking', 10, 'Bob') # Bob: 95适用场景:排行榜、延时队列、带权重的消息
二、高级特性
2.1 发布订阅(Pub/Sub)
# 终端1:订阅频道SUBSCRIBE news
# 终端2:发布消息PUBLISH news "Hello World"
# 模式订阅PSUBSCRIBE news.*# Python 发布者r.publish('news', 'Hello World')
# Python 订阅者pubsub = r.pubsub()pubsub.subscribe('news')for message in pubsub.listen(): if message['type'] == 'message': print(message['data']) # b'Hello World'2.2 事务
# MULTI/EXEC 事务MULTISET key1 "value1"INCR counterEXEC
# WATCH 实现乐观锁WATCH counterval = GET counterMULTIINCR counterEXEC # 如果 counter 被其他客户端修改,EXEC 返回 nil2.3 Lua 脚本
# 原子性操作:分布式锁EVAL " if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end" 1 lock_key "unique_value"# Python 执行 Lua 脚本lua_script = """if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1])else return 0end"""
# 分布式锁释放r.eval(lua_script, 1, 'lock_key', 'unique_value')2.4 Stream(消息流)
# 生产消息XADD stream1 "*" name "Alice" action "login"
# 消费消息XREAD COUNT 10 STREAMS stream1 0
# 消费者组XGROUP CREATE stream1 group1 0XREADGROUP GROUP group1 consumer1 COUNT 10 STREAMS stream1 ># Python Stream 示例# 生产r.xadd('events', {'name': 'Alice', 'action': 'login'})
# 消费r.xread({'events': '0'}, count=10)三、持久化机制
3.1 RDB vs AOF
| 特性 | RDB | AOF |
|---|---|---|
| 原理 | 定时快照 | 追加日志 |
| 文件 | 二进制紧凑 | 文本命令 |
| 恢复速度 | 快 | 慢 |
| 数据安全 | 可能丢失 | 更安全 |
| 性能影响 | 小(fork子进程) | 较大(每条写命令) |
3.2 RDB 配置
# redis.conf
# 快照策略:N 秒内 M 次修改触发save 900 1 # 15分钟内1次修改save 300 10 # 5分钟内10次修改save 60 10000 # 1分钟内1万次修改
# 快照文件名dbfilename dump.rdb
# 存储目录dir /var/lib/redis
# 压缩rdbcompression yes
# 手动触发快照# redis-cli BGSAVE3.3 AOF 配置
# redis.conf
# 开启 AOFappendonly yes
# 文件名appendfilename "appendonly.aof"
# 刷盘策略# always: 每条命令都刷盘(最安全,最慢)# everysec: 每秒刷盘(推荐,平衡)# no: 由操作系统决定(最快,可能丢失数据)appendfsync everysec
# AOF 重写:压缩 AOF 文件auto-aof-rewrite-percentage 100 # 文件大小翻倍时重写auto-aof-rewrite-min-size 64mb # 最小重写大小3.4 混合持久化(Redis 4.0+)
# 混合持久化:RDB 做全量 + AOF 做增量aof-use-rdb-preamble yes
# 重写时:前半部分 RDB 格式,后半部分 AOF 格式# 恢复时:先加载 RDB(快速),再回放 AOF(补全)四、主从复制
4.1 架构图
主从复制架构:
Master (读写) │ ┌──┴──┐ │ │ Slave1 Slave2 (只读)
特点:- 读写分离:主节点写,从节点读- 数据冗余:从节点保存数据副本- 故障恢复:主节点故障可手动切换4.2 配置
# 从节点 redis.confreplicaof 192.168.1.100 6379
# 从节点只读replica-read-only yes
# 主节点认证密码masterauth yourpassword
# 验证主从redis-cli INFO replication# 主节点信息# role:master# connected_slaves:2
# 从节点信息# role:slave# master_host:192.168.1.100# master_port:6379五、哨兵模式(Sentinel)
5.1 架构图
哨兵模式架构:
Sentinel1 Sentinel2 Sentinel3 │ │ │ └────────────┼────────────┘ │ ┌───────┴───────┐ │ │ Master Slave
哨兵职责:1. 监控:检测主从节点是否存活2. 通知:通知运维人员故障3. 自动故障转移:主节点故障时自动选举新主4. 配置提供:客户端连接哨兵获取主节点地址5.2 哨兵配置
# sentinel.conf
# 监控主节点(名称 IP 端口 quorum)sentinel monitor mymaster 192.168.1.100 6379 2
# 主节点密码sentinel auth-pass mymaster yourpassword
# 判断主节点下线的超时时间(毫秒)sentinel down-after-milliseconds mymaster 30000
# 故障转移时从节点数量sentinel parallel-syncs mymaster 1
# 故障转移超时时间sentinel failover-timeout mymaster 180000# 启动哨兵redis-sentinel /etc/redis/sentinel.conf
# 查看哨兵状态redis-cli -p 26379 SENTINEL mastersredis-cli -p 26379 SENTINEL get-master-addr-by-name mymaster六、Cluster 集群
6.1 架构图
Redis Cluster 架构(6个节点,3主3从):
节点A (master) ←→ 节点B (slave) slot 0-5460
节点C (master) ←→ 节点D (slave) slot 5461-10922
节点E (master) ←→ 节点F (slave) slot 10923-16383
特点:- 数据分片:16384 个 slot 分配到各节点- 去中心化:节点间通过 Gossip 协议通信- 自动故障转移:主节点故障,从节点升级- 水平扩展:可动态添加/移除节点6.2 集群搭建
# 方法1:使用 redis-cli 快速创建(Redis 5.0+)
# 创建6个节点的配置文件for port in 7000 7001 7002 7003 7004 7005; do mkdir -p /redis/cluster/${port} cat > /redis/cluster/${port}/redis.conf << EOFport ${port}cluster-enabled yescluster-config-file nodes.confcluster-node-timeout 5000appendonly yesdir /redis/cluster/${port}EOFdone
# 启动所有节点for port in 7000 7001 7002 7003 7004 7005; do redis-server /redis/cluster/${port}/redis.conf &done
# 创建集群(3主3从)redis-cli --cluster create \ 127.0.0.1:7000 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 \ --cluster-replicas 1
# 验证集群redis-cli -p 7000 CLUSTER INFOredis-cli -p 7000 CLUSTER NODES6.3 集群操作
# 集群模式连接redis-cli -c -p 7000
# 自动路由到正确节点SET key1 "value1" # 可能自动跳转到 7001
# 查看slot分配CLUSTER KEYSLOT key1 # 计算key所属slot
# 添加新节点redis-cli --cluster add-node 127.0.0.1:7006 127.0.0.1:7000
# 重新分片redis-cli --cluster reshard 127.0.0.1:7000七、缓存策略
7.1 缓存穿透
# 问题:查询不存在的数据,绕过缓存直接打到数据库
# 方案1:缓存空值def get_user(user_id): cache_key = f"user:{user_id}" user = r.get(cache_key)
if user is not None: if user == b'': return None # 缓存的空值 return json.loads(user)
# 查询数据库 user = db.query("SELECT * FROM users WHERE id = %s", user_id)
if user is None: # 缓存空值,短过期时间 r.setex(cache_key, 300, '') else: r.setex(cache_key, 3600, json.dumps(user))
return user
# 方案2:布隆过滤器from redisbloom.client import Client
rb = Client()rb.bfCreate('users_filter', 0.01, 1000000) # 错误率1%,容量100万
# 添加存在的用户IDrb.bfAdd('users_filter', 'user_123')
def get_user_safe(user_id): # 先检查布隆过滤器 if not rb.bfExists('users_filter', user_id): return None # 一定不存在
# 再查缓存和数据库 return get_user(user_id)7.2 缓存击穿
# 问题:热点key过期瞬间,大量请求同时打到数据库
# 方案:互斥锁import timeimport uuid
def get_hot_data(key): # 先查缓存 data = r.get(key) if data is not None: return json.loads(data)
# 获取互斥锁 lock_key = f"lock:{key}" lock_value = str(uuid.uuid4())
# 尝试获取锁(设置过期时间防止死锁) if r.set(lock_key, lock_value, nx=True, ex=10): try: # 查询数据库 data = db.query_hot_data(key) # 写入缓存 r.setex(key, 3600, json.dumps(data)) return data finally: # 释放锁(Lua脚本保证原子性) lua = """ if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end """ r.eval(lua, 1, lock_key, lock_value) else: # 等待后重试 time.sleep(0.1) return get_hot_data(key)7.3 缓存雪崩
# 问题:大量key同时过期,请求全部打到数据库
# 方案1:过期时间加随机值def cache_data(key, data, base_ttl=3600): import random # 过期时间加 0-300 秒随机值 ttl = base_ttl + random.randint(0, 300) r.setex(key, ttl, json.dumps(data))
# 方案2:多级缓存import functools
@functools.lru_cache(maxsize=1000) # 本地缓存def get_data_l1(key): # L2: Redis 缓存 data = r.get(key) if data: return json.loads(data)
# L3: 数据库 data = db.query(key) if data: cache_data(key, data) return data八、性能优化
8.1 关键配置优化
# redis.conf 性能相关
# 内存策略maxmemory 4gbmaxmemory-policy allkeys-lru # LRU 淘汰策略
# 网络配置tcp-keepalive 300timeout 0
# 慢查询日志slowlog-log-slower-than 10000 # 10msslowlog-max-len 128
# 客户端缓冲区限制client-output-buffer-limit normal 0 0 0client-output-buffer-limit replica 256mb 64mb 60client-output-buffer-limit pubsub 32mb 8mb 60
# 后台任务频率(清理过期key等)hz 108.2 内存优化
# 使用 Hash 替代多个 String# 不推荐:1000个 StringSET user:1:name "Alice"SET user:1:age "30"
# 推荐:1个 HashHSET user:1 name "Alice" age 30
# 使用 ziplist 编码(小数据量自动启用)hash-max-ziplist-entries 512hash-max-ziplist-value 64list-max-ziplist-size -2set-max-intset-entries 512zset-max-ziplist-entries 128zset-max-ziplist-value 648.3 Pipeline 批量操作
# 不推荐:逐条执行for i in range(1000): r.set(f'key:{i}', i)# 1000 次网络往返
# 推荐:Pipelinepipe = r.pipeline()for i in range(1000): pipe.set(f'key:{i}', i)pipe.execute()# 1 次网络往返
# 事务型 Pipelinepipe = r.pipeline(transaction=True)pipe.set('key1', 'value1')pipe.incr('counter')pipe.execute()8.4 监控指标
# 关键监控指标redis-cli INFO
# Server: 版本、运行时间# Clients: 连接数# Memory: 内存使用# Stats: 命中率、QPS# Replication: 主从状态
# 重要指标used_memory_human # 内存使用connected_clients # 客户端连接数keyspace_hits # 缓存命中keyspace_misses # 缓存未命中hit_rate = hits / (hits + misses) # 命中率
# 查看慢查询redis-cli SLOWLOG GET 10
# 大Key扫描redis-cli --bigkeys
# 内存分析redis-cli MEMORY STATS九、Python/Node.js 集成
9.1 Python(redis-py)
import redisimport json
# 连接池pool = redis.ConnectionPool( host='localhost', port=6379, db=0, max_connections=20, decode_responses=True # 自动解码为字符串)
r = redis.Redis(connection_pool=pool)
# 基本操作r.set('key', 'value', ex=3600)value = r.get('key')
# JSON 缓存def cache_json(key, data, ttl=3600): r.setex(key, ttl, json.dumps(data, ensure_ascii=False))
def get_json(key): data = r.get(key) return json.loads(data) if data else None
# 分布式锁def acquire_lock(lock_name, timeout=10): identifier = str(uuid.uuid4()) end = time.time() + timeout while time.time() < end: if r.set(lock_name, identifier, nx=True, ex=timeout): return identifier time.sleep(0.001) return None
def release_lock(lock_name, identifier): lua = """ if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end """ return r.eval(lua, 1, lock_name, identifier)9.2 Node.js(ioredis)
const Redis = require('ioredis');
// 连接集群const redis = new Redis.Cluster([ { host: '127.0.0.1', port: 7000 }, { host: '127.0.0.1', port: 7001 }, { host: '127.0.0.1', port: 7002 },]);
// 基本操作await redis.set('key', 'value', 'EX', 3600);const value = await redis.get('key');
// Pipelineconst pipeline = redis.pipeline();for (let i = 0; i < 1000; i++) { pipeline.set(`key:${i}`, i);}const results = await pipeline.exec();
// 事务const multi = redis.multi();multi.set('key1', 'value1');multi.incr('counter');const results = await multi.exec();十、总结
Redis 应用场景速查
| 场景 | 数据结构 | 关键命令 |
|---|---|---|
| 缓存 | String | SET/GET/SETEX |
| 计数器 | String | INCR/INCRBY |
| 排行榜 | ZSet | ZADD/ZREVRANGE |
| 消息队列 | List/Stream | LPUSH/BRPOP, XADD/XREAD |
| 用户信息 | Hash | HSET/HGETALL |
| 标签系统 | Set | SADD/SINTER |
| 分布式锁 | String+Lua | SET NX + Lua |
| 限流器 | ZSet/List | ZADD/ZREMRANGEBYSCORE |
| 地理位置 | Geo | GEOADD/GEOSEARCH |
| 签到统计 | Bitmap | SETBIT/BITCOUNT |
部署建议
小型项目:单机 Redis(开启持久化)中型项目:主从 + 哨兵(高可用)大型项目:Cluster 集群(水平扩展)缓存场景:可关闭持久化,使用最大内存策略数据重要:RDB + AOF 混合持久化Redis 是现代应用不可或缺的基础设施。掌握数据结构选择、持久化配置、集群部署和缓存策略,是构建高性能应用的基础。从单机到集群,从缓存到消息队列,Redis 的应用场景远超想象。
Redis 完全指南:从基础数据结构到集群部署与性能优化 | 2026最新实践
https://971918.xyz/posts/docs/redis-complete-guide/