一、Redis 到底是什么?
Redis(Remote Dictionary Server)是一个开源的、基于内存的、高性能的键值对数据库。它支持多种数据结构,包括字符串(String)、哈希(Hash)、列表(List)、集合(Set)、有序集合(Sorted Set)等,因此也被称为 "数据结构服务器"。
Redis 的核心特点
- 极致性能:基于内存操作,单线程模型,官方给出的 QPS(每秒查询率)可以达到 10 万 +
- 丰富的数据结构:支持 8 种以上的数据结构,能满足各种业务场景
- 持久化:支持 RDB 和 AOF 两种持久化方式,保证数据不丢失
- 高可用:支持主从复制、哨兵模式和集群模式,能搭建高可用的分布式系统
- 原子性:所有操作都是原子性的,不用担心并发问题
简单来说,Redis 就是一个 "又快又全能" 的数据库。它比 MySQL 快 100 倍以上,又比 Memcached 支持更多的数据结构和功能。
二、Redis 的 8 大核心应用场景
很多人只用 Redis 做缓存,这简直是 "杀鸡用牛刀"。下面我就介绍 Redis 最常用的 8 个应用场景,每一个都能解决你实际开发中的痛点。
1. 数据缓存(最常用)
这是 Redis 最基本也是最常用的功能。把热点数据(比如商品详情、用户信息、配置信息)存到 Redis 中,用户请求时直接从 Redis 获取,不用再查数据库,能极大提升系统性能,降低数据库压力。
典型场景:电商首页的商品列表、用户登录信息、系统配置参数。
2. 分布式锁
在分布式系统中,多个服务实例同时操作同一个资源时,需要用分布式锁来保证数据一致性。Redis 的SETNX命令可以非常方便地实现分布式锁。
典型场景:订单扣库存、秒杀活动、定时任务防重复执行。
3. 计数器
Redis 的INCR命令是原子性的,可以用来实现各种计数器功能。
典型场景:文章阅读量、点赞数、评论数、商品销量、接口调用次数统计。
4. 限流
基于 Redis 的计数器或滑动窗口算法,可以实现接口限流功能,防止系统被恶意请求打垮。
典型场景:短信验证码发送频率限制、API 接口调用频率限制、秒杀活动的流量控制。
5. 排行榜
Redis 的有序集合(Sorted Set)天生就是为排行榜设计的。它可以快速插入、删除和排序,性能非常高。
典型场景:游戏排行榜、商品销量排行榜、热门搜索排行榜。
6. 消息队列
Redis 的列表(List)可以作为简单的消息队列使用,支持先进先出(FIFO)的消息传递。
典型场景:异步任务处理、日志收集、订单状态变更通知。
7. 会话存储
把用户的 Session 信息存到 Redis 中,可以实现分布式系统的会话共享。
典型场景:单点登录(SSO)、分布式系统的用户状态管理。
8. 布隆过滤器
Redis 4.0 之后支持布隆过滤器插件,可以用来快速判断一个元素是否存在于一个集合中。
典型场景:缓存穿透防护、垃圾邮件过滤、重复数据判断。
三、Spring Boot 集成 Redis 完整教程
下面我就以最常用的 Spring Boot 框架为例,教你如何快速集成和使用 Redis。
第一步:引入依赖
在pom.xml中引入 Spring Data Redis 依赖:
xml
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!-- 连接池依赖 --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency>第二步:配置 Redis
在application.yml中配置 Redis 连接信息:
yaml
spring: redis: host: localhost port: 6379 password: 123456 database: 0 # 连接池配置 lettuce: pool: max-active: 8 max-wait: -1ms max-idle: 8 min-idle: 0第三步:配置 RedisTemplate
默认的RedisTemplate会使用 JDK 序列化,序列化后的数据在 Redis 中看起来是乱码。我们需要自定义RedisTemplate,使用 Jackson2JsonRedisSerializer 序列化:
java
运行
@Configuration public class RedisConfig { @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(factory); // 使用Jackson2JsonRedisSerializer序列化值 Jackson2JsonRedisSerializer<Object> jacksonSerializer = new Jackson2JsonRedisSerializer<>(Object.class); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL); jacksonSerializer.setObjectMapper(objectMapper); // String序列化器 StringRedisSerializer stringSerializer = new StringRedisSerializer(); // key和hashKey使用String序列化 template.setKeySerializer(stringSerializer); template.setHashKeySerializer(stringSerializer); // value和hashValue使用Jackson序列化 template.setValueSerializer(jacksonSerializer); template.setHashValueSerializer(jacksonSerializer); template.afterPropertiesSet(); return template; } }第四步:使用 RedisTemplate 操作 Redis
现在你就可以在代码中注入RedisTemplate,操作各种数据结构了:
java
运行
@Service public class UserService { @Autowired private RedisTemplate<String, Object> redisTemplate; // 操作字符串 public void setUser(User user) { redisTemplate.opsForValue().set("user:" + user.getId(), user, 30, TimeUnit.MINUTES); } public User getUser(Long id) { return (User) redisTemplate.opsForValue().get("user:" + id); } // 操作哈希 public void setUserField(Long id, String field, Object value) { redisTemplate.opsForHash().put("user:" + id, field, value); } // 操作有序集合(排行榜) public void addScore(Long userId, double score) { redisTemplate.opsForZSet().incrementScore("rank", userId, score); } public Set<ZSetOperations.TypedTuple<Object>> getTop10() { return redisTemplate.opsForZSet().reverseRangeWithScores("rank", 0, 9); } }四、深入原理:Redis 为什么这么快?
很多人都知道 Redis 很快,但很少有人能说清楚它为什么这么快。其实 Redis 的高性能是由四个关键因素共同决定的:
1. 完全基于内存
Redis 的所有数据都存储在内存中,内存的读写速度比磁盘快几个数量级。这是 Redis 快的最根本原因。
2. 单线程模型
Redis 采用单线程模型来处理请求,避免了多线程之间的上下文切换和竞争条件。很多人会问:"单线程怎么能处理这么多并发请求?"
答案是:Redis 的单线程指的是处理网络请求和命令执行的线程只有一个,但 Redis 还有其他线程负责持久化、集群同步等操作。而且单线程并不意味着性能差,只要 CPU 不是瓶颈,单线程的效率往往比多线程更高。
3. 高效的数据结构
Redis 的每种数据结构都经过了精心设计,底层使用了多种高效的数据结构实现:
- 字符串:SDS(简单动态字符串)
- 哈希:压缩列表 + 哈希表
- 列表:压缩列表 + 双向链表
- 集合:整数集合 + 哈希表
- 有序集合:压缩列表 + 跳表
4. 非阻塞 I/O 多路复用
Redis 使用 I/O 多路复用技术(epoll)来处理多个网络连接,一个线程可以同时处理成千上万的客户端连接。
五、Redis 三大缓存问题与解决方案
在生产环境中使用 Redis,最常见的问题就是缓存穿透、缓存击穿和缓存雪崩。这三个问题如果处理不好,会导致数据库压力骤增,甚至宕机。
1. 缓存穿透
问题描述:用户请求的数据既不在缓存中,也不在数据库中。这样每次请求都会打到数据库上,恶意攻击者可以利用这个漏洞来攻击系统。
解决方案:
- 布隆过滤器:将所有可能存在的数据哈希到一个足够大的布隆过滤器中,请求先经过布隆过滤器过滤
- 缓存空值:如果数据库中也没有数据,就缓存一个空值,并设置一个较短的过期时间
2. 缓存击穿
问题描述:一个热点 key 在过期的瞬间,有大量的并发请求同时访问这个 key。这些请求都会打到数据库上,导致数据库压力骤增。
解决方案:
- 互斥锁:当缓存失效时,只有第一个请求能获取到锁,去数据库查询数据并更新缓存
- 永不过期:对于特别热点的 key,不设置过期时间,后台异步更新缓存
3. 缓存雪崩
问题描述:大量的 key 在同一时间过期,导致所有请求都打到数据库上,数据库压力骤增。
解决方案:
- 过期时间加随机值:给每个 key 的过期时间加上一个随机值,避免同时过期
- 多级缓存:使用本地缓存 + Redis 缓存的多级缓存架构
- 熔断降级:当数据库压力过大时,熔断部分非核心接口,返回默认数据
六、Redis 生产环境最佳实践
最后,我分享几个我在生产环境中总结的 Redis 最佳实践,帮你避开常见的坑:
- key 命名规范:使用冒号分隔的命名方式,如
业务:模块:id,例如user:info:123 - 设置过期时间:所有的 key 都应该设置合理的过期时间,避免内存泄漏
- 避免大 key:单个 key 的大小不要超过 1MB,大 key 会导致网络传输慢、内存碎片多
- 批量操作:使用
pipeline进行批量操作,减少网络往返次数 - 不要在 Redis 中存储大对象:大对象应该存储在数据库或对象存储中,Redis 只存储引用
- 开启持久化:根据业务需求选择合适的持久化方式,重要数据一定要开启持久化
- 监控 Redis 状态:监控 Redis 的内存使用率、QPS、命中率等指标,及时发现问题