Redis完全指南

目录

Redis 简介

Redis(Remote Dictionary Server)是一个开源(BSD许可)的内存数据结构存储系统,可用作数据库、缓存和消息中间件。它支持多种数据类型,具有内置的复制、Lua脚本、LRU驱动事件、事务以及不同级别的磁盘持久化功能,并通过Redis Sentinel和Redis Cluster提供高可用性服务。

Redis 核心特性

  • 高性能:基于内存操作,单线程模型避免锁竞争,QPS可达10万+
  • 丰富的数据结构:支持字符串、哈希、列表、集合、有序集合等多种数据类型
  • 原子操作:所有操作都是原子性的,多个操作也支持事务功能
  • 持久化:支持RDB和AOF两种持久化方式,确保数据不丢失
  • 高可用:支持主从复制、哨兵模式和集群模式,保证服务稳定性
  • 发布订阅:支持发布/订阅模式的消息机制
  • Lua脚本:支持使用Lua脚本扩展Redis功能

Redis数据类型

String

特性

  • 最基础的数据类型,存储字符串、数字或二进制数据(图片)
  • 最大存储容量为512MB
  • 支持原子操作(自增、自减)
  • 可以对整个字符串或部分字符串进行操作

优势

  • 操作简单高效,适用于缓存、计数器、分布式锁等场景
  • 可存储结构化数据(如JSON字符串)
  • 支持过期策略:可设置键的过期时间(EX 参数),自动清理旧数据,无需手动维护
  • 二进制安全,可以存储任何格式的数据

应用场景

  • 缓存:存储HTML页面、API响应等
  • 计数器:如网站访问量、用户点赞数
  • 分布式锁:利用原子性操作实现分布式锁
  • 会话管理:存储用户会话信息
1
2
3
4
5
6
7
8
9
#设置键值对
#set key value
set username "username1"

#获取值
# get key
get username #输出 "username1"
# 设置过期时间(10秒后过期)
SET session_id "xyz123" EX 10

自增

1
2
3
set views 100
INCR views
get views #输出101

自减

1
2
3
set views 100
DECR views
get views #输出99

设置过期时间

1
2
# 设置  10秒后过期
set token " 4aa5f2ce-f422-4b40-8bee-1f9f3f7fe231" EX 10

Hash

特性

  • 类似字典,存储键值对的集合(field-value)
  • 每个哈希可以存储多达2^32 - 1个键值对
  • 适合存储对象(如用户信息、商品详情)

优势

  • 可单独操作对象的某个字段,无需修改整个对象,节省带宽
  • 内存占用比String存储对象更高效
  • 适合存储结构化数据,比JSON字符串更灵活

应用场景

  • 用户信息管理:存储用户ID、姓名、年龄等信息
  • 商品详情:存储商品ID、名称、价格、库存等
  • 购物车:每个用户的购物车可以使用一个Hash,商品ID作为field,数量作为value
  • 配置信息:应用配置、系统参数等
1
2
3
4
5
6
7
8
9
10
11
# 设置用户信息
HSET user:100 name "bob" age 25 email "[email protected]"

# 获取单个字段
HGET user:100 name # 输出 "bob"

# 获取所有字段和值
HGETALL user:100 # 输出 name "bob" age "25" email "[email protected]"

# 删除字段
HDEL user:100 email

List

特性

  • 有序列表,元素可重复,底层基于双向链表实现
  • 支持从双端插入/删除元素,适合实现队列或栈
  • 每个列表最多可存储2^32 - 1个元素
  • 支持阻塞操作,可用于实现生产者-消费者模式

优势

  • 操作双端元素效率极高(时间复杂度O(1))
  • 内部有序,无需额外排序操作
  • 支持范围操作,可以获取部分元素

应用场景

  • 消息队列:使用LPUSH + RPOP实现简单的消息队列
  • 最新动态:如微博、朋友圈的最新N条动态
  • 任务队列:任务的派发与执行
  • 最近浏览记录:记录用户最近浏览的商品
1
2
3
4
5
6
7
8
9
10
11
# 从右侧插入元素
RPUSH fruits "apple" "banana"

# 从左侧插入元素
LPUSH fruits "orange"

# 获取列表元素(索引 0 到 2)
LRANGE fruits 0 2 # 输出 ["orange", "apple", "banana"]

# 从右侧弹出元素
RPOP fruits # 输出 "banana"

Set

特性

  • 无序集合,元素唯一,底层基于哈希表实现
  • 支持交集、并集、差集等集合运算
  • 每个集合最多可存储2^32 - 1个元素
  • 可以随机获取集合中的元素

优势

  • 天然去重,适合存储标签、好友列表等
  • 集合运算可用于社交场景(如共同好友)
  • 添加、删除、查找的复杂度都是O(1)
  • 支持并发读写,线程安全

应用场景

  • 用户标签系统:为用户添加标签,实现兴趣推荐
  • 社交关系:粉丝、关注、共同好友等
  • 黑白名单:IP黑名单、敏感词过滤
  • 随机抽奖:利用SRANDMEMBER或SPOP实现

添加元素

1
SADD tags "user1" "user2" "user3"

元素是否存在

1
SISMEMBER tags "user1"

集合交集

1
2
3
SADD user:1:follows 101 102 103
SADD user:2:follows 102 103 104
SINTER user:1:follows user:2:follows #输出[102,103]

Sorted Set

特性

  • 类似Set,但每个元素关联一个分数(Score),按分数排序
  • 元素不允许重复,但分数可以重复
  • 底层使用跳表和哈希表实现,支持高效的范围查询
  • 每个有序集合最多可存储2^32 - 1个元素

优势

  • 可高效实现排行榜、延迟队列等场景
  • 分数可动态更新,排序实时生效
  • 支持按分数范围或元素位置范围查询
  • 支持 ZREVRANGE(倒序取前 N 名)、ZRANK(查询某个元素的排名)等命令,时间复杂度为 O(logN)

应用场景

  • 排行榜系统:游戏积分排行、直播礼物榜、商品销量榜
  • 带权重的消息队列:根据优先级处理任务
  • 延迟队列:使用时间戳作为score,实现定时任务
  • 实时排名更新:用户分数变化后立即刷新名次

添加元素

1
2
#添加元素(用户ID以及分数)
ZADD ranking 80 "user1" 95 "user2" 70 "user3"

获取分数从高到低的前2名

1
ZREVRANGE ranking 0 1 WITHSCORES  # 输出 "user2" 95  "user1" 80

查看某个元素的分数

1
ZSCORE ranking "user2"  # 输出 95

增加分数

1
ZINCRBY ranking 5 "user2"  # 1001 的分数变为 100

其他数据类型

Bitmap

特性

  • 本质上是字符串的位操作,可以将字符串看作是二进制位的数组
  • 可对每一位进行设置(0或1)和获取操作
  • 最大支持2^32位,约512MB

优势

  • 极其节省空间,每个位只占用1bit
  • 适合存储布尔类型的数据
  • 提供位运算操作,如与、或、异或等

应用场景

  • 用户签到记录:每一位代表一天,签到为1,未签到为0
  • 在线状态统计:在线为1,离线为0
  • 用户行为统计:如是否阅读过某篇文章
  • 布隆过滤器:快速判断元素是否存在

记录用户签到

1
2
3
SETBIT sign:user100 1 1
SETBIT sign:user100 3 1
# 统计签到天数

统计签到天数

1
BITCOUNT sign:user100  # 输出 2

HyperLogLog

特性

  • 用于估算集合中唯一元素的数量(基数统计)
  • 占用内存极小,无论数据量多大,只需要12KB内存
  • 有一定的误差率,标准误差约为0.81%

优势

  • 节省内存:相比Set存储所有元素,内存占用极小
  • 性能高效:计算速度快,不随集合大小增长
  • 适合海量数据:可处理数十亿级别的不重复数据统计

应用场景

  • 网站UV(独立访客)统计
  • APP日活、月活用户数统计
  • 大规模数据去重统计

添加UV记录

1
PFADD uv:2025-7-2 "user1" "user2" "user3"

统计UV

1
PFCOUNT uv:2025-7-2  # 输出 3

Geospatial

特性

  • 专门用于存储地理位置信息(经纬度坐标)
  • 底层基于Sorted Set和地理哈希(Geohash)算法实现
  • 支持存储、查询、计算地理位置信息

优势

  • 原生支持经纬度存储:提供 GEOADD(添加地点)、GEORADIUS(搜索指定范围内的地点)等命令
  • 高效空间计算:底层基于地理哈希(Geohash)实现,距离计算和范围查询效率高
  • 简化开发:无需依赖专门的GIS系统,可直接在Redis中实现位置服务

应用场景

  • 附近的人、周边商家搜索(如外卖APP的附近餐厅)
  • 两地距离计算(如打车软件的起点到终点距离)
  • 地理围栏(Geo-fencing):判断用户是否进入或离开特定区域
  • 位置共享和追踪:如共享单车定位、物流配送跟踪

添加地点

1
2
#添加地点    经度,维度     名称
GEOADD places 116.40 39.90 "北京" 121.47 31.23 "上海"

计算两地距离(单位:km)

1
GEODIST places "北京" "上海" km

搜索附近地点

1
2
# 搜索距离北京1000公里内的地点
GEORADIUS places 116.40 39.90 1000 km

Redis整体优势

  1. 高性能:基于内存操作,单线程模型避免锁竞争,QPS可达10万+
  2. 持久化:支持RDB和AOF两种持久化方式,确保数据不丢失
  3. 高可用:支持主从复制、哨兵模式和集群(Cluster),保证服务稳定性
  4. 丰富功能:除基础类型外,还支持发布订阅、Lua脚本、事务等

Redis持久化

Redis是内存数据库,为了保证数据在服务器重启后不丢失,Redis提供了持久化机制。Redis支持两种持久化方式:RDB和AOF。

RDB持久化

原理

RDB(Redis DataBase)持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是Snapshot快照,它恢复时是将快照文件直接读到内存中。

触发方式

  • 自动触发:根据redis.conf中配置的save规则自动触发
    1
    2
    3
    save 900 1      # 900秒内至少有1个key被修改
    save 300 10 # 300秒内至少有10个key被修改
    save 60 10000 # 60秒内至少有10000个key被修改
  • 手动触发
    • SAVE命令:阻塞Redis服务器进程,直到RDB文件创建完毕
    • BGSAVE命令:派生出一个子进程来创建RDB文件,不阻塞服务器进程

优点

  • 适合大规模数据恢复,恢复速度比AOF快
  • 对于灾难恢复来说是一个很好的选择
  • 子进程负责持久化,主进程不进行任何IO操作,保证了Redis的高性能

缺点

  • 可能会丢失最后一次持久化后的数据
  • 数据量大时,fork子进程会导致服务短暂暂停

AOF持久化

原理

AOF(Append Only File)持久化以日志的形式记录服务器所处理的每一个写操作,以文本的方式记录,可以打开文件看到详细的操作记录。

触发方式

  • 通过配置appendonly yes开启AOF持久化
  • 可以设置同步策略(fsync):
    1
    2
    3
    appendfsync always    # 每次有数据修改发生时都会写入AOF文件,性能最差但数据最完整
    appendfsync everysec # 每秒钟同步一次,推荐,性能和安全的折中
    appendfsync no # 让操作系统决定何时进行同步,性能最好但数据最不安全

重写机制

AOF文件会随着时间越来越大,Redis提供了AOF重写功能,通过BGREWRITEAOF命令用重写的方式来缩小AOF文件的体积。

优点

  • 数据安全性高,支持秒级持久化
  • 可读性高,AOF文件是纯文本,可以直接读懂
  • 支持重写机制,文件体积可控

缺点

  • 文件体积通常大于RDB文件
  • 恢复速度比RDB慢
  • 对性能影响较大,尤其是fsync策略为always时

持久化策略选择

仅使用RDB

  • 适合数据相对不重要,可以容忍数分钟的数据丢失
  • 追求高性能,数据集较大

仅使用AOF

  • 对数据安全性要求较高,能承受较低的性能
  • 可以接受AOF文件尺寸较大和恢复速度较慢

RDB + AOF混合使用(推荐)

  • 同时开启RDB和AOF,Redis重启时优先使用AOF恢复数据,这样既保证了数据安全性,又具备RDB快照用于备份
  • Redis 4.0引入了混合持久化,在AOF重写时,将重写这一刻之前的内存数据以RDB方式写入AOF文件头部,之后的操作再以AOF方式追加,兼具了RDB和AOF的优点

Redis事务

事务概念

Redis事务是一组命令的集合,可以一次执行多个命令,本质是一个命令队列。Redis事务具有以下特点:

  • 批量操作:一次执行多个命令
  • 顺序执行:事务中的命令按照添加顺序依次执行
  • 隔离性:事务执行期间,其他客户端提交的命令不会插入到事务执行命令序列中

事务相关命令

Redis事务主要由以下命令组成:

  • MULTI:标记一个事务块的开始
  • EXEC:执行所有事务块内的命令
  • DISCARD:取消事务,放弃执行事务块内的所有命令
  • WATCH:监视一个或多个key,如果在事务执行之前这些key被其他命令所改动,那么事务将被打断
  • UNWATCH:取消WATCH命令对所有key的监视

事务执行流程

  1. 开始事务:使用MULTI命令标记事务开始
  2. 命令入队:将多个命令加入到事务队列中,这些命令不会立即执行
  3. 执行事务:使用EXEC命令执行事务队列中的所有命令
1
2
3
4
5
6
7
8
# 开始事务
MULTI
# 命令入队
SET user:1:name "Tom"
SET user:1:age 25
INCR visitor_count
# 执行事务
EXEC

事务中的错误处理

Redis事务中的错误分为两种:

  1. 编译时错误:命令入队时的错误,如语法错误。这种情况下,Redis会拒绝执行整个事务。
1
2
3
4
MULTI
SET user:1:name "Tom"
SETNX user:1:age # 语法错误,缺少参数
EXEC # 事务会被拒绝执行
  1. 运行时错误:命令执行时的错误,如对字符串执行数学运算。这种情况下,Redis会继续执行事务中的其他命令。
1
2
3
4
5
MULTI
SET user:1:name "Tom"
INCR user:1:name # 对字符串执行INCR会出错
SET user:1:age 25
EXEC # 除了INCR命令,其他命令会正常执行

事务的ACID特性

Redis事务与传统数据库事务的区别:

  • 原子性(Atomicity):Redis事务不保证原子性。如果事务中的某个命令执行失败,其他命令仍会被执行,不会回滚。
  • 一致性(Consistency):Redis事务可以保证一致性,即执行事务前后数据库从一个一致性状态变换到另一个一致性状态。
  • 隔离性(Isolation):Redis事务是完全隔离的,事务执行期间不会被其他命令打断。
  • 持久性(Durability):取决于Redis的持久化配置,如果开启了AOF持久化且设置为always同步策略,则可以保证持久性。

事务与乐观锁(WATCH)

Redis提供了WATCH命令实现乐观锁,可以在EXEC执行之前,监视任意数量的key。如果在事务执行之前,这些key被其他命令所改动,那么事务将被打断。

1
2
3
4
5
WATCH account:1  # 监视账户余额
MULTI
DECRBY account:1 100 # 减少100元
INCRBY account:2 100 # 增加100元
EXEC # 如果account:1在此期间被修改,事务会失败

分布式锁

适用场景

  • 分布式系统中的资源竞争控制(如并发修改同一条数据、分布式任务调度)
  • 避免重复执行(订单支付状态更新、库存扣减)

优势

  • 实现简单:通过SET key value NX EX命令(仅当键不存在时设置,同时加过期时间)实现分布式锁,确保唯一性
  • 安全性:过期时间防止锁持有者崩溃后锁永久有效(死锁)
1
2
3
4
# 获取锁(仅当 lock:order 不存在时设置,10秒后自动释放)
SET lock:order "12345" NX EX 10
# 释放锁(需校验值,避免误删他人的锁)
if GET lock:order == "12345" then DEL lock:order

分布式锁的实现方式

基本实现

1
2
3
4
5
# 获取锁
SET resource_name unique_value NX PX 30000

# 释放锁(使用Lua脚本保证原子性)
eval "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end" 1 resource_name unique_value

进阶实现(Redlock算法)

对于高可用的分布式锁,可以使用Redlock算法,它通过多个Redis实例来避免单点故障:

  1. 获取当前时间(毫秒)
  2. 按顺序向N个Redis实例尝试获取锁
  3. 计算获取锁消耗的时间,如果成功获取锁的实例数量超过半数,并且获取锁消耗的时间小于锁的有效时间,则认为获取锁成功
  4. 如果获取锁失败,向所有Redis实例发送释放锁命令

Redis发布订阅

发布订阅概念

Redis的发布订阅(pub/sub)是一种消息通信模式:发送者(发布者)发送消息,订阅者接收消息。Redis客户端可以订阅任意数量的频道。

发布订阅相关命令

  • SUBSCRIBE channel [channel …]:订阅一个或多个频道的消息
  • PSUBSCRIBE pattern [pattern …]:订阅一个或多个符合给定模式的频道
  • PUBLISH channel message:将信息发送到指定的频道
  • UNSUBSCRIBE [channel [channel …]]:退订指定的频道
  • PUNSUBSCRIBE [pattern [pattern …]]:退订所有给定模式的频道
  • PUBSUB subcommand [argument [argument …]]:查看订阅与发布系统状态

发布订阅示例

1
2
3
4
5
6
7
8
9
10
# 订阅者(客户端1)订阅channel1频道
SUBSCRIBE channel1

# 发布者(客户端2)向channel1发布消息
PUBLISH channel1 "Hello Redis Pub/Sub"

# 订阅者(客户端1)会收到消息
1) "message"
2) "channel1"
3) "Hello Redis Pub/Sub"

发布订阅的特点

  • 松耦合:发布者和订阅者之间没有直接联系,它们只关注频道
  • 一对多:一个发布者可以向多个订阅者发送消息
  • 即时性:消息发布后立即传递给订阅者
  • 无持久化:Redis的发布订阅模式不支持持久化,如果订阅者断线,将会丢失断线期间的所有消息

应用场景

  • 实时消息系统:如聊天室、实时通知
  • 实时数据更新:如股票价格、体育比分更新
  • 系统监控:服务器状态变化通知
  • 任务分发:将任务分发给多个工作节点

与专业消息队列的比较

Redis的发布订阅功能相比专业的消息队列系统(如RabbitMQ、Kafka)有以下区别:

  • 持久化:Redis的发布订阅不支持消息持久化,而专业MQ支持
  • 消息确认:Redis没有消息确认机制,而专业MQ支持ACK确认
  • 消息重试:Redis没有消息重试机制,而专业MQ支持
  • 消息过滤:Redis支持简单的模式匹配,而专业MQ支持更复杂的过滤

Redis高级特性

Lua脚本

Redis支持使用Lua脚本来执行原子操作,通过EVAL命令可以执行Lua脚本。

优势

  • 原子性:脚本的执行是原子性的,不会被其他命令打断
  • 减少网络开销:将多个操作放在一个脚本中执行,减少网络往返
  • 可复用:可以通过SCRIPT LOAD和EVALSHA复用脚本

示例

1
2
3
4
5
# 使用Lua脚本实现计数器,如果计数器不存在则创建并设置为1,否则增加1
EVAL "return redis.call('INCR', KEYS[1])" 1 counter

# 使用Lua脚本实现原子性的获取并删除操作
EVAL "local val = redis.call('GET', KEYS[1]); redis.call('DEL', KEYS[1]); return val" 1 mykey

管道(Pipeline)

管道技术可以在一次通信中执行多个命令,减少通信次数,提高性能。

优势

  • 减少网络往返:一次性发送多个命令,减少网络延迟
  • 提高吞吐量:批量处理命令,提高处理效率

与事务的区别

  • 管道不保证原子性,而事务保证所有命令一起执行
  • 管道不会被打断,但不保证命令的执行顺序

内存优化

数据结构优化

  • 使用Hash存储对象,而不是String存储序列化的JSON
  • 使用Bitmap代替Set来存储布尔值信息
  • 使用HyperLogLog代替Set来统计唯一元素数量

内存淘汰策略

Redis提供了多种内存淘汰策略,可以在redis.conf中配置:

  • noeviction:当内存不足时,新写入操作会报错
  • allkeys-lru:从所有键中移除最近最少使用的键
  • volatile-lru:从设置了过期时间的键中移除最近最少使用的键
  • allkeys-random:从所有键中随机移除键
  • volatile-random:从设置了过期时间的键中随机移除键
  • volatile-ttl:从设置了过期时间的键中移除即将过期的键

性能优化

慢查询分析

Redis提供了慢查询日志功能,可以记录执行时间超过指定阈值的命令:

1
2
3
4
5
6
7
8
# 设置慢查询阈值为100微秒
CONFIG SET slowlog-log-slower-than 100

# 设置慢查询日志最大长度
CONFIG SET slowlog-max-len 1000

# 获取慢查询日志
SLOWLOG GET 10

持久化优化

  • 使用RDB进行定期备份,AOF进行实时持久化
  • 对于AOF,使用everysec同步策略,平衡性能和安全性
  • 定期执行BGREWRITEAOF,控制AOF文件大小

连接优化

  • 使用连接池复用连接,减少连接创建和销毁的开销
  • 合理设置连接池大小,避免连接数过多导致资源浪费

高可用方案

主从复制

主从复制是Redis实现高可用的基础,一个主节点可以有多个从节点:

  • 主节点负责写操作,从节点负责读操作,实现读写分离
  • 从节点可以提升为主节点,实现故障转移
  • 支持链式复制,从节点可以有自己的从节点

哨兵模式(Sentinel)

Redis Sentinel是Redis的高可用解决方案,提供自动故障转移、监控、通知等功能:

  • 监控:监控主从节点是否正常运行
  • 通知:当监控的节点出现问题时,通知管理员或其他程序
  • 自动故障转移:当主节点不可用时,自动选择一个从节点升级为主节点

集群模式(Cluster)

Redis Cluster是Redis的分布式解决方案,支持数据自动分片和高可用:

  • 数据自动分片:将数据自动分散到多个节点上
  • 无中心架构:所有节点都是平等的,没有中心节点
  • 高可用:部分节点不可用时,集群仍能继续提供服务

Redis最佳实践

键值设计

  • 键名设计:使用冒号分隔的命名空间,如user:1000:profile
  • 避免大键:拆分大对象,避免一次性操作大量数据
  • 设置过期时间:合理设置过期时间,避免内存持续增长

命令使用

  • 避免使用KEYS命令,使用SCAN命令代替
  • 避免使用FLUSHALLFLUSHDB等危险命令
  • 使用MGETMSET等批量操作命令提高效率

安全配置

  • 设置密码认证
  • 禁用危险命令
  • 限制最大连接数
  • 设置网络访问控制

监控指标

  • 内存使用:used_memory、used_memory_rss
  • 命令统计:total_commands_processed、instantaneous_ops_per_sec
  • 连接数:connected_clients、rejected_connections
  • 缓存命中率:keyspace_hits、keyspace_misses
  • 持久化状态:rdb_last_save_time、aof_current_size

Redis 适合处理 高频访问、低延迟要求、结构简单 的数据,尤其在缓存、计数、队列、实时排行等场景中表现优异。实际使用时需结合业务需求(如数据量级、持久化要求、并发量),与其他数据库(如 MySQLElasticsearch)配合,构建高效的系统架构。

参考资料