一、什么是 Redis?
在日常开发中,我们经常会遇到这样的场景:
-
用户每次访问页面,都需要从数据库查询用户信息。
-
电商系统在秒杀活动期间,数据库承受大量并发请求。
-
用户登录之后,需要保存登录状态。
-
系统需要记录商品访问量、点赞数量以及排行榜。
如果所有数据都直接从 MySQL 等关系型数据库中读取,在高并发场景下,数据库可能会成为系统性能瓶颈。
这时候,我们就可以使用 Redis。
Redis(Remote Dictionary Server) 是一个开源的内存数据存储系统,支持多种数据结构,可以用作缓存、消息队列、排行榜、分布式锁等。
Redis 的主要特点:
基于内存存储,读写速度快。
支持丰富的数据结构。
支持数据持久化。
支持主从复制和高可用架构。
支持过期时间和内存淘汰机制。
二、Redis 为什么这么快?
Redis 性能优秀,主要与以下几个方面有关。
1. 内存存储
Redis 将数据主要保存在内存中,相比传统磁盘数据库,减少了大量磁盘 I/O 操作。
2. 高效的数据结构
Redis 针对不同数据类型设计了相应的数据结构,例如哈希表、跳表、紧凑列表等。具体内部实现会随 Redis 版本变化。
3. 单线程命令执行模型
Redis 的命令执行通常采用单线程模型,避免多个线程同时操作共享数据时产生的锁竞争问题。
需要注意的是,Redis 并不是所有工作都只使用一个线程。网络 I/O、后台任务等在不同版本和配置下也可以使用多个线程。
4. 高效的 I/O 多路复用
Redis 使用 I/O 多路复用机制处理大量客户端连接,避免为每个连接都创建独立线程。
三、Redis 五大常见数据类型
Redis 最常见的五种基础数据类型分别是:
数据类型 主要特点 常见应用场景
String 字符串、整数和浮点数等 缓存、计数器、Token List 有序字符串列表 消息列表、任务队列 Hash 字段和值组成的哈希结构 用户信息、商品信息 Set 无序且元素唯一 点赞、标签、共同好友 ZSet 元素关联 Score,可按分数排序 排行榜、积分排名
1. String:字符串类型
String 是 Redis 中最常用的数据类型。
# 设置数据
SET username "admin"
# 获取数据
GET username
# 设置过期时间
SET token "abc123" EX 3600
# 数值自增
SET views 100
INCR views
# 数值自减
DECR views
实际开发场景:
例如用户登录后,可以将 Token 保存到 Redis 中,并设置过期时间:
SET login:token:1001 "abc123" EX 7200
这样就可以实现登录状态缓存,过期后 Redis 会自动删除对应数据。
2. Hash:哈希类型
Hash 适合存储具有多个字段的对象。
# 设置用户信息
HSET user:1001 name "张三" age 22 address "北京"
# 获取用户名称
HGET user:1001 name
# 获取全部用户信息
HGETALL user:1001
# 修改年龄
HSET user:1001 age 23
在实际项目中,Hash 可以用于缓存用户信息、商品信息等。
3. List:列表类型
List 是一个有序的字符串列表,支持从两端插入和弹出元素。
# 从左侧插入
LPUSH message "消息1"
LPUSH message "消息2"
# 从右侧插入
RPUSH message "消息3"
# 获取全部数据
LRANGE message 0 -1
# 从左侧弹出
LPOP message
常见应用场景包括消息列表、最新动态和简单任务队列。若用于生产级消息队列,还需要进一步考虑消息确认、可靠投递和消费组等需求。
4. Set:集合类型
Set 是一个无序且元素唯一的集合。
# 添加点赞用户
SADD article:likes 1001
SADD article:likes 1002
SADD article:likes 1003
# 查看点赞用户
SMEMBERS article:likes
# 判断用户是否点赞
SISMEMBER article:likes 1001
# 获取点赞人数
SCARD article:likes
由于 Set 具有去重特性,因此适合实现文章点赞、用户标签、共同好友等功能。
5. ZSet:有序集合
ZSet 与 Set 类似,但每个元素都会关联一个 Score 分数,并根据分数进行排序。
# 添加排行榜数据
ZADD ranking 100 "张三"
ZADD ranking 95 "李四"
ZADD ranking 88 "王五"
# 获取排行榜(分数从高到低)
ZREVRANGE ranking 0 9 WITHSCORES
# 获取用户排名(从 0 开始)
ZREVRANK ranking "张三"
# 增加用户积分
ZINCRBY ranking 10 "李四"
实际开发场景包括游戏排行榜、商品销量排行和用户积分排行等。
四、Redis 在实际项目中的应用
掌握 Redis 的基本命令还不够,真正重要的是理解它在项目中解决了什么问题。
1. 缓存数据库查询结果
假设项目需要根据用户 ID 查询用户信息。如果每次请求都直接访问 MySQL,会产生大量重复查询。这时可以使用 Redis 缓存。
基本流程:
用户发起查询请求。
首先查询 Redis。
如果 Redis 存在数据,直接返回。
如果 Redis 不存在数据,再查询 MySQL。
将 MySQL 查询结果写入 Redis。
返回查询结果。
这种方式也叫 Cache Aside(旁路缓存)模式。
下面以 Spring Data Redis 风格的 Java 伪代码为例,展示基本思路:
public User getUserById(Long userId) {
String key = "user:" + userId;
// 1. 查询 Redis 缓存
User user = redisTemplate.opsForValue().get(key);
if (user != null) {
return user;
}
// 2. 缓存不存在,查询 MySQL
user = userMapper.selectById(userId);
// 3. 查询到数据后写入缓存
if (user != null) {
redisTemplate.opsForValue()
.set(key, user, 30, TimeUnit.MINUTES);
}
return user;
}
注意:以上示例省略了序列化配置、异常处理、空值缓存和并发重建等生产环境细节。实际使用时,还要考虑缓存与数据库更新之间的一致性问题。
2. 实现分布式锁
在分布式系统中,多个服务实例可能同时操作同一份资源。
例如秒杀活动中,多个请求同时购买最后一件商品,就可能出现超卖问题。
Redis 可以利用 SET 命令实现简单的分布式锁:
SET lock:order:1001 unique_value NX EX 30
其中:
-
NX:只有 Key 不存在时才设置成功。
-
EX 30:锁的过期时间为 30 秒。
-
unique_value:当前请求的唯一标识。
释放锁时,需要先判断锁的持有者是否是当前请求,再删除锁,不能直接执行 DEL。
生产环境通常使用 Lua 脚本保证判断和删除操作的原子性,或者使用成熟的分布式锁客户端,例如 Redisson。同时还需要结合业务执行时间、锁续期和 fencing token 等问题进行设计。
3. 实现访问量统计
例如需要统计某篇文章的浏览量:
# 文章浏览量增加
INCR article:views:1001
# 获取浏览量
GET article:views:1001
Redis 的 INCR 命令可以原子性地完成数值递增,非常适合计数器场景。如果计数数据需要长期保存,可以定期将数据汇总并持久化到数据库。
五、Redis 常见缓存问题
在实际开发中,Redis 并不是只要使用就能保证系统稳定,还需要处理一些常见问题。
1. 缓存穿透
问题:
用户不断查询一个不存在的数据。由于 Redis 和 MySQL 都没有对应记录,每次请求都会访问数据库。
解决方案:
-
缓存空对象,并设置较短的过期时间。
-
使用布隆过滤器提前判断数据是否可能存在。
-
对异常请求进行限流和参数校验。
2. 缓存击穿
问题:
某个热点 Key 突然过期,大量请求同时访问该数据,导致数据库瞬间承受大量压力。
解决方案:
-
使用互斥锁控制缓存重建。
-
对热点数据设置合理的过期策略。
-
使用逻辑过期方案,在后台异步更新缓存。
3. 缓存雪崩
问题:
大量缓存 Key 在同一时间过期,导致大量请求直接访问数据库。
解决方案:
-
给缓存过期时间增加随机值,避免集中失效。
-
建立多级缓存。
-
做好数据库限流和服务降级。
六、Redis 如何保证数据不丢失?
Redis 虽然主要使用内存存储数据,但也支持持久化机制。常见的持久化方式有两种。
1. RDB(Redis Database)
RDB 是快照持久化机制。Redis 可以按照配置的规则生成某个时间点的数据快照,并保存到磁盘。
优点:
-
文件通常比较紧凑。
-
恢复速度通常较快。
-
适合定期备份和数据迁移。
缺点:
-
两次快照之间发生故障,可能丢失尚未保存的数据。
2. AOF(Append Only File)
AOF 通过记录写命令来实现持久化。例如:
SET username "admin"
INCR views
HSET user:1001 age 22
这些写操作会按照 AOF 的配置策略记录到文件中。
优点:
-
可以记录更细粒度的写操作。
-
根据刷盘策略,可以降低数据丢失风险。
缺点:
-
文件可能比较大。
-
恢复时需要重放命令,恢复耗时可能更长。
实际项目中,可以根据业务对数据可靠性的要求选择 RDB、AOF 或两者结合使用。需要注意,持久化策略不同,故障时可能丢失的数据量也不同。
七、Redis 常用性能优化建议
在实际开发中,建议注意以下几点:
控制 Key 和 Value 的大小: 避免存储过大的对象,降低网络传输和内存开销。
合理设置过期时间: 避免无效数据长期占用内存。
谨慎使用耗时命令: 生产环境避免对超大集合执行可能阻塞服务的操作。
使用 Pipeline: 在适合的场景下批量发送命令,减少网络往返次数。
选择合适的数据结构: 根据业务特点选择 String、Hash、Set 或 ZSet。
做好监控: 关注内存使用率、命令耗时、缓存命中率和连接数。
考虑数据一致性: 更新数据库时,设计合理的缓存删除或更新策略。
八、总结
Redis 是现代后端开发中非常重要的基础组件。
从基础的 String、Hash、List、Set、ZSet,到实际项目中的缓存、分布式锁、排行榜和计数器,都能看到 Redis 的应用价值。
对于正在学习后端开发的同学来说,学习 Redis 不应该只停留在记忆命令层面,更重要的是理解:
-
Redis 为什么快?
-
Redis 的数据结构如何工作?
-
Redis 如何解决高并发问题?
-
Redis 如何与 MySQL 配合?
-
缓存出现问题时应该如何排查?
只有把这些知识结合实际项目,才能真正掌握 Redis。
后续还可以继续深入学习 Redis 的底层数据结构、持久化原理、主从复制、哨兵机制以及 Redis Cluster 集群。
参考资料
-
Redis 官方文档
-
Redis 命令参考
-
Redis 数据类型介绍



