目录
- [🔴 40 Redis与微服务入门](#🔴 40 Redis与微服务入门)
-
- [1. Redis概述](#1. Redis概述)
-
- [1.1 什么是Redis](#1.1 什么是Redis)
- [1.2 Redis特点](#1.2 Redis特点)
- [1.3 安装与启动](#1.3 安装与启动)
- [2. Redis数据类型](#2. Redis数据类型)
-
- [2.1 String(字符串)](#2.1 String(字符串))
- [2.2 Hash(哈希)](#2.2 Hash(哈希))
- [2.3 List(列表)](#2.3 List(列表))
- [2.4 Set(集合)](#2.4 Set(集合))
- [2.5 ZSet(有序集合)](#2.5 ZSet(有序集合))
- [2.6 数据类型对比](#2.6 数据类型对比)
- [3. Spring Data Redis](#3. Spring Data Redis)
-
- [3.1 添加依赖](#3.1 添加依赖)
- [3.2 配置](#3.2 配置)
- [3.3 使用RedisTemplate](#3.3 使用RedisTemplate)
- [3.4 配置序列化](#3.4 配置序列化)
- [4. 缓存策略](#4. 缓存策略)
-
- [4.1 Cache-Aside(旁路缓存)](#4.1 Cache-Aside(旁路缓存))
- [4.2 Read/Write Through](#4.2 Read/Write Through)
- [4.3 Write Behind(异步写入)](#4.3 Write Behind(异步写入))
- [4.4 策略对比](#4.4 策略对比)
- [5. 分布式锁](#5. 分布式锁)
-
- [5.1 基于Redis的分布式锁](#5.1 基于Redis的分布式锁)
- [5.2 使用示例](#5.2 使用示例)
- [5.3 Redisson(推荐)](#5.3 Redisson(推荐))
- [6. 缓存三大问题](#6. 缓存三大问题)
-
- [6.1 缓存穿透](#6.1 缓存穿透)
- [6.2 缓存击穿](#6.2 缓存击穿)
- [6.3 缓存雪崩](#6.3 缓存雪崩)
- [6.4 三大问题对比](#6.4 三大问题对比)
- [7. 微服务架构入门](#7. 微服务架构入门)
-
- [7.1 单体 vs 微服务](#7.1 单体 vs 微服务)
- [7.2 微服务拆分原则](#7.2 微服务拆分原则)
- [8. Spring Cloud核心组件](#8. Spring Cloud核心组件)
-
- [8.1 组件全景](#8.1 组件全景)
- [8.2 Nacos注册中心](#8.2 Nacos注册中心)
- [8.3 OpenFeign远程调用](#8.3 OpenFeign远程调用)
- [8.4 Spring Cloud Gateway](#8.4 Spring Cloud Gateway)
- [8.5 Sentinel限流降级](#8.5 Sentinel限流降级)
- [9. 总结](#9. 总结)
- [📚 参考资料](#📚 参考资料)
🔴 40 Redis与微服务入门
📅 更新于 2026年6月 | ✍️ 原创文章,转载请注明出处
1. Redis概述
1.1 什么是Redis
Redis(Remote Dictionary Server)是一个开源的内存数据库,支持多种数据结构,常用作缓存、消息队列、会话存储。
1.2 Redis特点
| 特点 | 说明 |
|---|---|
| 高性能 | 10万+ QPS,数据在内存中 |
| 丰富数据结构 | String/Hash/List/Set/ZSet等 |
| 持久化 | RDB快照 + AOF日志 |
| 高可用 | 主从复制、哨兵、集群 |
| 原子操作 | 单线程模型,天然线程安全 |
1.3 安装与启动
bash
# macOS
brew install redis
brew services start redis
# Linux
sudo apt install redis-server
sudo systemctl start redis
# Docker
docker run -d -p 6379:6379 --name redis redis:latest
# 连接测试
redis-cli ping
# PONG
2. Redis数据类型
2.1 String(字符串)
最基础的类型,可以存储字符串、数字、二进制数据。
bash
# 基本操作
SET name "Alice"
GET name # "Alice"
SET count 100
INCR count # 101
DECR count # 100
INCRBY count 10 # 110
# 过期时间
SET token "abc123" EX 3600 # 1小时后过期
TTL token # 3599
# 批量操作
MSET k1 "v1" k2 "v2" k3 "v3"
MGET k1 k2 k3 # ["v1", "v2", "v3"]
# 不存在才设置(分布式锁基础)
SETNX lock "holder" # 1 (成功)
SETNX lock "other" # 0 (已存在,失败)
应用场景:缓存、计数器、分布式锁、Session
2.2 Hash(哈希)
键值对集合,适合存储对象。
bash
# 存储用户信息
HSET user:1001 name "Alice" age 25 email "alice@example.com"
HGET user:1001 name # "Alice"
HGETALL user:1001 # 获取所有字段
HDEL user:1001 email # 删除字段
HINCRBY user:1001 age 1 # 年龄+1
# 判断字段存在
HEXISTS user:1001 name # 1
HEXISTS user:1001 phone # 0
应用场景:对象存储、用户信息、商品详情
2.3 List(列表)
有序列表,支持两端操作。
bash
# 左右插入
LPUSH queue "task1" "task2" # [task2, task1]
RPUSH queue "task3" # [task2, task1, task3]
# 弹出
LPOP queue # "task2"
RPOP queue # "task3"
# 获取
LRANGE queue 0 -1 # 获取所有元素
LLEN queue # 列表长度
# 阻塞弹出(消息队列)
BLPOP queue 30 # 阻塞等待30秒
应用场景:消息队列、最新消息列表、任务队列
2.4 Set(集合)
无序集合,元素唯一。
bash
# 添加元素
SADD tags "java" "python" "go"
SADD tags "java" # 已存在,忽略
SMEMBERS tags # 获取所有成员
SISMEMBER tags "java" # 1 (存在)
SCARD tags # 集合大小
# 集合运算
SADD user1:friends "A" "B" "C"
SADD user2:friends "B" "C" "D"
SINTER user1:friends user2:friends # 交集: [B, C]
SUNION user1:friends user2:friends # 并集: [A, B, C, D]
SDIFF user1:friends user2:friends # 差集: [A]
应用场景:标签、共同好友、去重
2.5 ZSet(有序集合)
带分数的有序集合,按分数排序。
bash
# 添加元素
ZADD leaderboard 100 "Alice"
ZADD leaderboard 85 "Bob"
ZADD leaderboard 92 "Charlie"
# 获取排名(从高到低)
ZREVRANGE leaderboard 0 -1 WITHSCORES
# 1) "Alice" 2) "100"
# 3) "Charlie" 4) "92"
# 5) "Bob" 6) "85"
# 获取分数
ZSCORE leaderboard "Alice" # 100
# 排名(从高到低,0开始)
ZREVRANK leaderboard "Alice" # 0 (第一名)
# 范围查询
ZRANGEBYSCORE leaderboard 80 100 # 分数80-100的成员
应用场景:排行榜、延迟队列、优先级队列
2.6 数据类型对比
| 类型 | 特点 | 典型场景 |
|---|---|---|
| String | 简单KV | 缓存、计数器 |
| Hash | 对象字段 | 用户信息、商品详情 |
| List | 有序可重复 | 消息队列、最新列表 |
| Set | 无序唯一 | 标签、共同好友 |
| ZSet | 有序带分 | 排行榜、延迟队列 |
3. Spring Data Redis
3.1 添加依赖
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>
3.2 配置
yaml
spring:
data:
redis:
host: localhost
port: 6379
password:
database: 0
timeout: 3000ms
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 0
max-wait: -1ms
3.3 使用RedisTemplate
java
@Service
public class RedisService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
// String操作
public void setString(String key, String value) {
redisTemplate.opsForValue().set(key, value);
}
public void setStringWithExpire(String key, String value, long seconds) {
redisTemplate.opsForValue().set(key, value, seconds, TimeUnit.SECONDS);
}
public String getString(String key) {
return (String) redisTemplate.opsForValue().get(key);
}
// Hash操作
public void setHash(String key, String field, Object value) {
redisTemplate.opsForHash().put(key, field, value);
}
public Object getHash(String key, String field) {
return redisTemplate.opsForHash().get(key, field);
}
// List操作
public void leftPush(String key, Object value) {
redisTemplate.opsForList().leftPush(key, value);
}
public Object rightPop(String key) {
return redisTemplate.opsForList().rightPop(key);
}
// Set操作
public void addSet(String key, Object... values) {
redisTemplate.opsForSet().add(key, values);
}
public boolean isMember(String key, Object value) {
return Boolean.TRUE.equals(redisTemplate.opsForSet().isMember(key, value));
}
// ZSet操作
public void addZSet(String key, Object value, double score) {
redisTemplate.opsForZSet().add(key, value, score);
}
public Set<Object> getTopN(String key, long n) {
return redisTemplate.opsForZSet().reverseRange(key, 0, n - 1);
}
// 删除
public Boolean delete(String key) {
return redisTemplate.delete(key);
}
// 设置过期
public Boolean expire(String key, long seconds) {
return redisTemplate.expire(key, seconds, TimeUnit.SECONDS);
}
}
3.4 配置序列化
java
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
// Key序列化:String
template.setKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
// Value序列化:JSON
Jackson2JsonRedisSerializer<Object> jsonSerializer =
new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
mapper.activateDefaultTyping(
mapper.getPolymorphicTypeValidator(),
ObjectMapper.DefaultTyping.NON_FINAL
);
jsonSerializer.setObjectMapper(mapper);
template.setValueSerializer(jsonSerializer);
template.setHashValueSerializer(jsonSerializer);
template.afterPropertiesSet();
return template;
}
}
4. 缓存策略
4.1 Cache-Aside(旁路缓存)
最常用的策略,应用层控制缓存。
java
public User getUser(Long id) {
String key = "user:" + id;
// 1. 先查缓存
User user = (User) redisTemplate.opsForValue().get(key);
if (user != null) {
return user;
}
// 2. 缓存未命中,查数据库
user = userMapper.selectById(id);
if (user != null) {
// 3. 写入缓存
redisTemplate.opsForValue().set(key, user, 30, TimeUnit.MINUTES);
}
return user;
}
// 更新时:先更新DB,再删除缓存
public void updateUser(User user) {
userMapper.updateById(user); // 更新数据库
redisTemplate.delete("user:" + user.getId()); // 删除缓存
}
4.2 Read/Write Through
缓存层代理数据库读写,应用只和缓存交互。
java
// 缓存层
public class UserCache {
public User read(Long id) {
User user = getFromCache(id);
if (user == null) {
user = getFromDB(id);
putToCache(id, user);
}
return user;
}
public void write(User user) {
updateDB(user);
putToCache(user.getId(), user);
}
}
4.3 Write Behind(异步写入)
缓存立即更新,异步批量写入数据库。
优点:写入性能极高
缺点:可能丢数据
场景:允许短暂数据不一致的场景
4.4 策略对比
| 策略 | 一致性 | 性能 | 复杂度 | 场景 |
|---|---|---|---|---|
| Cache-Aside | 高 | 中 | 低 | 通用场景 |
| Read/Write Through | 高 | 中 | 中 | 统一缓存层 |
| Write Behind | 低 | 高 | 高 | 写密集场景 |
5. 分布式锁
5.1 基于Redis的分布式锁
java
@Service
public class DistributedLockService {
@Autowired
private RedisTemplate<String, String> redisTemplate;
/**
* 尝试获取锁
* @param lockKey 锁的key
* @param requestId 请求标识(解锁时验证)
* @param expireTime 过期时间(秒)
* @return 是否获取成功
*/
public boolean tryLock(String lockKey, String requestId, long expireTime) {
Boolean result = redisTemplate.opsForValue()
.setIfAbsent(lockKey, requestId, expireTime, TimeUnit.SECONDS);
return Boolean.TRUE.equals(result);
}
/**
* 释放锁(Lua脚本保证原子性)
*/
public boolean releaseLock(String lockKey, String requestId) {
String script =
"if redis.call('get', KEYS[1]) == ARGV[1] then " +
" return redis.call('del', KEYS[1]) " +
"else " +
" return 0 " +
"end";
Long result = redisTemplate.execute(
new DefaultRedisScript<>(script, Long.class),
Collections.singletonList(lockKey),
requestId
);
return Long.valueOf(1L).equals(result);
}
}
5.2 使用示例
java
public void deductStock(Long productId, int quantity) {
String lockKey = "lock:stock:" + productId;
String requestId = UUID.randomUUID().toString();
try {
// 尝试获取锁,最多等待3秒
boolean locked = false;
for (int i = 0; i < 30; i++) {
locked = distributedLockService.tryLock(lockKey, requestId, 10);
if (locked) break;
Thread.sleep(100);
}
if (!locked) {
throw new RuntimeException("获取锁失败");
}
// 业务逻辑
Stock stock = stockMapper.selectById(productId);
if (stock.getQuantity() >= quantity) {
stock.setQuantity(stock.getQuantity() - quantity);
stockMapper.updateById(stock);
} else {
throw new RuntimeException("库存不足");
}
} finally {
// 释放锁
distributedLockService.releaseLock(lockKey, requestId);
}
}
5.3 Redisson(推荐)
java
// 使用Redisson(更完善的分布式锁实现)
@Autowired
private RedissonClient redisson;
public void deductStock(Long productId, int quantity) {
RLock lock = redisson.getLock("lock:stock:" + productId);
try {
// 尝试获取锁,最多等待3秒,锁自动过期10秒
boolean locked = lock.tryLock(3, 10, TimeUnit.SECONDS);
if (!locked) {
throw new RuntimeException("获取锁失败");
}
// 业务逻辑
// ...
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
6. 缓存三大问题
6.1 缓存穿透
问题:查询不存在的数据,每次都穿透缓存查数据库。
请求 → 缓存(无) → 数据库(无) → 返回空
请求 → 缓存(无) → 数据库(无) → 返回空
...(每次都查数据库)
解决方案:
java
// 方案1:缓存空值
public User getUser(Long id) {
String key = "user:" + id;
User user = (User) redisTemplate.opsForValue().get(key);
if (user != null) {
return user.equals(NULL_USER) ? null : user;
}
user = userMapper.selectById(id);
if (user == null) {
// 缓存空值,短过期时间
redisTemplate.opsForValue().set(key, NULL_USER, 5, TimeUnit.MINUTES);
} else {
redisTemplate.opsForValue().set(key, user, 30, TimeUnit.MINUTES);
}
return user;
}
// 方案2:布隆过滤器
// 在缓存前加一层布隆过滤器,快速判断key是否存在
6.2 缓存击穿
问题:热点key过期瞬间,大量请求同时查数据库。
热点Key过期 → 100个请求同时查DB → 数据库压力骤增
解决方案:
java
// 方案1:互斥锁
public User getUser(Long id) {
String key = "user:" + id;
User user = (User) redisTemplate.opsForValue().get(key);
if (user == null) {
String lockKey = "lock:" + key;
try {
// 获取分布式锁
boolean locked = tryLock(lockKey, 5);
if (locked) {
// 双重检查
user = (User) redisTemplate.opsForValue().get(key);
if (user == null) {
user = userMapper.selectById(id);
redisTemplate.opsForValue().set(key, user, 30, TimeUnit.MINUTES);
}
}
} finally {
releaseLock(lockKey);
}
}
return user;
}
// 方案2:逻辑过期(不设置TTL)
// 在value中存储过期时间,发现过期时异步更新
6.3 缓存雪崩
问题:大量key同时过期,或Redis宕机,请求全部打到数据库。
解决方案:
java
// 方案1:过期时间加随机值
Random random = new Random();
int baseExpire = 30 * 60; // 30分钟
int randomExpire = random.nextInt(10 * 60); // 0-10分钟随机
redisTemplate.opsForValue().set(key, value,
baseExpire + randomExpire, TimeUnit.SECONDS);
// 方案2:Redis集群/哨兵保证高可用
// 方案3:多级缓存(本地缓存 + Redis)
// 方案4:限流降级
6.4 三大问题对比
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 穿透 | 查询不存在的数据 | 缓存空值、布隆过滤器 |
| 击穿 | 热点key过期 | 互斥锁、逻辑过期 |
| 雪崩 | 大量key同时过期 | 随机过期时间、集群、多级缓存 |
7. 微服务架构入门
7.1 单体 vs 微服务
| 对比项 | 单体架构 | 微服务架构 |
|---|---|---|
| 部署 | 整体部署 | 独立部署 |
| 技术栈 | 统一 | 可以不同 |
| 扩展 | 整体扩展 | 按需扩展 |
| 复杂度 | 代码复杂 | 运维复杂 |
| 通信 | 方法调用 | HTTP/RPC |
| 数据库 | 共享 | 独立 |
7.2 微服务拆分原则
| 原则 | 说明 |
|---|---|
| 单一职责 | 每个服务只做一件事 |
| 高内聚低耦合 | 相关功能放一起,服务间松耦合 |
| 按业务域拆分 | 用户服务、订单服务、商品服务 |
| 数据独立 | 每个服务有自己的数据库 |
8. Spring Cloud核心组件
8.1 组件全景
| 组件 | 功能 | 主流实现 |
|---|---|---|
| 注册中心 | 服务注册与发现 | Nacos、Eureka、Consul |
| 配置中心 | 统一配置管理 | Nacos Config、Apollo |
| 网关 | 统一入口、路由、限流 | Spring Cloud Gateway |
| 负载均衡 | 请求分发 | Spring Cloud LoadBalancer |
| 远程调用 | 服务间通信 | OpenFeign |
| 熔断降级 | 防止雪崩 | Sentinel、Resilience4j |
| 链路追踪 | 全链路监控 | Micrometer Tracing、SkyWalking |
8.2 Nacos注册中心
yaml
# pom.xml
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
# application.yml
spring:
cloud:
nacos:
discovery:
server-addr: localhost:8848
java
// 启动类
@SpringBootApplication
@EnableDiscoveryClient
public class UserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class, args);
}
}
8.3 OpenFeign远程调用
yaml
# pom.xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
java
// 启动类
@EnableFeignClients
@SpringBootApplication
public class OrderServiceApplication {}
// Feign客户端
@FeignClient(name = "user-service")
public interface UserClient {
@GetMapping("/api/users/{id}")
UserDTO getUser(@PathVariable("id") Long id);
}
// 使用
@Service
public class OrderService {
@Autowired
private UserClient userClient;
public OrderDTO getOrderWithUser(Long orderId) {
Order order = orderMapper.selectById(orderId);
UserDTO user = userClient.getUser(order.getUserId());
OrderDTO dto = new OrderDTO();
dto.setOrder(order);
dto.setUser(user);
return dto;
}
}
8.4 Spring Cloud Gateway
yaml
# pom.xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
# application.yml
spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://user-service
predicates:
- Path=/api/users/**
- id: order-service
uri: lb://order-service
predicates:
- Path=/api/orders/**
8.5 Sentinel限流降级
java
// 资源定义
@SentinelResource(value = "getUser", fallback = "getUserFallback")
public UserDTO getUser(Long id) {
return userClient.getUser(id);
}
// 降级方法
public UserDTO getUserFallback(Long id, Throwable t) {
return new UserDTO("默认用户", 0);
}
9. 总结
| 知识点 | 核心内容 |
|---|---|
| Redis数据类型 | String/Hash/List/Set/ZSet |
| Spring Data Redis | RedisTemplate、序列化配置 |
| 缓存策略 | Cache-Aside、Read/Write Through |
| 分布式锁 | SETNX + Lua脚本、Redisson |
| 缓存问题 | 穿透(空值/布隆)、击穿(互斥锁)、雪崩(随机TTL) |
| 微服务 | 注册中心、网关、远程调用、限流降级 |
💬 你在项目中用过Redis吗?遇到过缓存穿透或雪崩的问题吗?微服务架构给你带来过哪些挑战?
🎉 恭喜你完成了Java入门到精通全部40篇文章!从HelloWorld到微服务,你已经具备了Java企业级开发的核心能力。继续加油!