Redis(Remote Dictionary Server)是一个高性能的键值存储数据库,以其丰富的数据结构、持久化机制和高可用特性著称。本文将深入剖析 Redis 的核心原理、数据结构、持久化机制以及集群架构。
Redis 简介
Redis 是一个开源的、基于内存的键值对存储系统,支持多种数据结构,并提供持久化、复制、集群等功能。
Redis 的核心特性:
- 高性能:基于内存存储,单节点可达 10万+ QPS
- 丰富的数据结构:String、List、Set、Hash、ZSet 等
- 原子操作:所有操作都是原子的
- 持久化:支持 RDB 和 AOF 两种持久化方式
- 主从复制:支持主从复制和读写分离
- 高可用:支持哨兵模式和集群模式
- 支持事务:通过 MULTI/EXEC 实现事务
数据结构
String(字符串)
String 是最基本的数据类型,可以存储字符串、整数、浮点数。
# 设置值
SET key value
# 获取值
GET key
# 设置过期时间
SETEX key seconds value
# 自增自减
INCR key
DECR key
INCRBY key increment
# 批量操作
MSET key1 value1 key2 value2
MGET key1 key2
底层实现:使用 SDS(Simple Dynamic String)
- 空间预分配:减少内存重分配次数
- 惰性释放:延迟内存释放
- 二进制安全:可以存储任意二进制数据
Hash(哈希)
Hash 是一个键值对集合,适合存储对象。
# 设置字段
HSET key field value
# 获取字段
HGET key field
# 获取所有字段
HGETALL key
# 获取字段数量
HLEN key
# 批量设置
HMSET key field1 value1 field2 value2
底层实现:使用 ziplist 或 hashtable
- ziplist:元素少且值小时使用,节省内存
- hashtable:元素多或值大时使用,O(1) 查找
List(列表)
List 是一个有序的字符串集合,支持从两端插入和弹出。
# 从左插入
LPUSH key value1 value2
# 从右插入
RPUSH key value1 value2
# 从左弹出
LPOP key
# 从右弹出
RPOP key
# 获取列表长度
LLEN key
# 获取范围内的元素
LRANGE key start stop
底层实现:使用 ziplist 或 linkedlist
- ziplist:元素少且值小时使用
- linkedlist:元素多或值大时使用
Set(集合)
Set 是一个无序的唯一字符串集合,支持集合运算。
# 添加元素
SADD key member1 member2
# 获取所有元素
SMEMBERS key
# 判断元素是否存在
SISMEMBER key member
# 获取元素数量
SCARD key
# 集合运算
SINTER key1 key2 # 交集
SUNION key1 key2 # 并集
SDIFF key1 key2 # 差集
底层实现:使用 intset 或 hashtable
- intset:元素都是整数且数量少时使用
- hashtable:其他情况使用
ZSet(有序集合)
ZSet 是一个有序的唯一字符串集合,每个元素关联一个分数。
# 添加元素
ZADD key score1 member1 score2 member2
# 获取排名范围内的元素
ZRANGE key start stop
# 获取分数范围内的元素
ZRANGEBYSCORE key min max
# 获取元素排名
ZRANK key member
# 获取元素分数
ZSCORE key member
# 增加分数
ZINCRBY key increment member
底层实现:使用 skiplist(跳跃表)+ hashtable
- skiplist:保持有序,支持范围查询
- hashtable:快速查找元素和分数
持久化机制
RDB(RDB)
RDB 是 Redis 默认的持久化方式,通过生成快照实现。
RDB 的特点:
- 文件紧凑,适合备份
- 恢复速度快
- 数据可能丢失(最后一次快照后的数据)
RDB 配置:
# 900秒内至少1个key变化则保存
save 900 1
save 300 10
save 60 10000
# RDB文件名
dbfilename dump.rdb
# RDB文件目录
dir /var/lib/redis
# 是否压缩RDB文件
rdbcompression yes
# RDB文件校验
rdbchecksum yes
手动触发 RDB:
# 同步生成RDB
SAVE
# 异步生成RDB
BGSAVE
AOF(AOF)
AOF 通过记录每个写操作实现持久化。
AOF 的特点:
- 数据安全性高,最多丢失1秒数据
- 文件较大
- 恢复速度较慢
AOF 配置:
# 开启AOF
appendonly yes
# AOF文件名
appendfilename "appendonly.aof"
# AOF持久化策略
appendfsync always # 每次写操作都同步(最安全但最慢)
appendfsync everysec # 每秒同步一次(推荐)
appendfsync no # 由操作系统决定(最快但可能丢失数据)
# AOF重写配置
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
AOF 重写:
# 手动触发AOF重写
BGREWRITEAOF
RDB vs AOF
| 特性 | RDB | AOF |
|---|---|---|
| 文件大小 | 小 | 大 |
| 恢复速度 | 快 | 慢 |
| 数据安全性 | 可能丢失数据 | 高 |
| 写入性能 | 高 | 低 |
混合持久化(Redis 4.0+):
- RDB 做基础全量
- AOF 做增量增量
- 结合两者优势
# 开启混合持久化
aof-use-rdb-preamble yes
过期策略
过期时间设置
# 设置过期时间(秒)
EXPIRE key seconds
# 设置过期时间(毫秒)
PEXPIRE key milliseconds
# 设置键并指定过期时间
SETEX key seconds value
# 查看剩余过期时间
TTL key
PTTL key
# 移除过期时间
PERSIST key
过期删除策略
Redis 使用惰性删除 + 定期删除的组合策略。
惰性删除:
- 访问键时检查是否过期
- 过期则删除,否则返回值
- 优点:节省 CPU
- 缺点:占用内存
定期删除:
- 每隔一段时间随机检查一定数量的键
- 删除过期的键
- 如果过期键比例超过 25%,继续检查
- 优点:平衡 CPU 和内存
内存淘汰策略
当内存达到上限时,Redis 会根据配置淘汰键。
# 最大内存限制
maxmemory 256mb
# 淘汰策略
maxmemory-policy allkeys-lru
# 可选策略:
# noeviction 不淘汰,返回错误
# allkeys-lru 淘汰最少使用的键
# allkeys-lfu 淘汰最不频繁使用的键
# allkeys-random 随机淘汰
# volatile-lru 淘汰设置了过期时间的最少使用键
# volatile-lfu 淘汰设置了过期时间的最不频繁使用键
# volatile-random 随机淘汰设置了过期时间的键
# volatile-ttl 淘汰即将过期的键
主从复制
复制原理
Redis 主从复制采用异步复制机制。
复制流程:
- 从节点连接主节点,发送 SYNC 命令
- 主节点执行 BGSAVE,生成 RDB 文件
- 主节点将 RDB 文件发送给从节点
- 从节点加载 RDB 文件
- 主节点将缓冲区的写命令发送给从节点
- 从节点执行写命令,保持数据同步
增量复制
Redis 2.8+ 支持增量复制,减少全量复制的开销。
增量复制流程:
- 从节点记录主节点的复制偏移量
- 连接断开后,从节点记录偏移量
- 重新连接时,从节点发送偏移量
- 主节点发送增量数据
主从配置
# 主节点配置
port 6379
bind 0.0.0.0
# 从节点配置
port 6380
replicaof 127.0.0.1 6379
masterauth password
# 只读模式
replica-read-only yes
哨兵模式
哨兵简介
哨兵(Sentinel)是 Redis 的高可用解决方案,用于监控主从节点,实现自动故障转移。
哨兵的功能:
- 监控:监控主从节点的运行状态
- 通知:通知管理员节点故障
- 自动故障转移:将从节点提升为主节点
- 配置提供:为客户端提供主节点信息
哨兵配置
# 哨兵端口
port 26379
# 监控主节点
sentinel monitor mymaster 127.0.0.1 6379 2
# 主节点密码
sentinel auth-pass mymaster password
# 故障转移超时时间
sentinel down-after-milliseconds mymaster 30000
# 故障转移超时时间
sentinel failover-timeout mymaster 180000
# 同时进行故障转移的从节点数量
sentinel parallel-syncs mymaster 1
故障转移流程
- 哨兵检测到主节点下线
- 多个哨兵确认主节点下线(客观下线)
- 哨兵选举 Leader
- Leader 选择新的主节点
- Leader 提升新主节点
- Leader 通知其他从节点复制新主节点
- Leader 通知客户端新主节点信息
集群模式
集群简介
Redis Cluster 是 Redis 的分布式解决方案,支持数据分片和高可用。
集群特点:
- 数据分片:使用哈希槽(16384 个)
- 高可用:每个主节点有多个从节点
- 自动故障转移:类似哨兵模式
- 无中心:所有节点地位平等
数据分片
Redis Cluster 使用 CRC16 算法计算键的哈希值,然后对 16384 取模,确定键所在的哈希槽。
# 计算键的哈希槽
slot = CRC16(key) % 16384
哈希槽分配:
- 每个主节点负责一部分哈希槽
- 槽可以迁移,实现扩缩容
- 支持手动和自动迁移
集群配置
# 开启集群模式
cluster-enabled yes
# 集群配置文件
cluster-config-file nodes.conf
# 集群超时时间
cluster-node-timeout 15000
# 集群总线端口
cluster-port 16379
集群操作
# 创建集群
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 --cluster add-node new_node:7000 existing_node:7000
# 槽迁移
redis-cli --cluster reshard existing_node:7000
# 检查集群状态
redis-cli --cluster check 127.0.0.1:7000
事务
Redis 通过 MULTI、EXEC、DISCARD、WATCH 实现事务。
# 开启事务
MULTI
# 执行命令
SET key1 value1
SET key2 value2
GET key1
# 提交事务
EXEC
# 取消事务
DISCARD
# 监控键
WATCH key
# 如果被监控的键被修改,EXEC 会失败
事务特性:
- 原子性:事务中的命令要么全部执行,要么全部不执行
- 隔离性:事务执行期间不会被其他命令打断
- 不支持回滚:如果命令执行失败,不会回滚已执行的命令
性能优化
内存优化
- 选择合适的数据结构:根据场景选择最优数据结构
- 设置过期时间:避免内存无限增长
- 使用压缩列表:减少内存占用
- 禁用不必要的功能:如禁用慢查询日志
# 禁用慢查询日志
slowlog-log-slower-than -1
# 设置最大内存
maxmemory 256mb
# 设置淘汰策略
maxmemory-policy allkeys-lru
网络优化
- 使用管道:减少网络往返
- 批量操作:使用 MGET、MSET 等命令
- 启用 TCP Keepalive:保持连接活跃
# 使用管道
echo -e "SET key1 value1\nSET key2 value2\nGET key1" | redis-cli --pipe
持久化优化
- 合理配置 RDB:根据数据量调整保存频率
- 使用 AOF 重写:减少 AOF 文件大小
- 使用混合持久化:结合 RDB 和 AOF 优势
最佳实践
- 使用连接池:避免频繁创建连接
- 设置合理的过期时间:避免内存泄漏
- 使用 Pipeline:提高批量操作性能
- 监控 Redis:使用 INFO 命令监控 Redis 状态
- 定期备份:定期备份 RDB 文件
- 使用集群:数据量大时使用集群模式
- 避免大 Key:大 Key 会阻塞 Redis
- 使用 Lua 脚本:减少网络往返
总结
Redis 是一个功能强大的键值存储系统,提供了丰富的数据结构、持久化机制和高可用方案。本文介绍了 Redis 的核心数据结构、持久化机制、主从复制、哨兵模式、集群架构以及性能优化技巧。掌握这些知识后,可以更好地应用 Redis 解决缓存、消息队列等场景的问题。