40 Redis与微服务入门

目录

  • [🔴 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企业级开发的核心能力。继续加油!


📚 参考资料

相关推荐
TPBoreas1 小时前
springboot我们项目中的常见注解
java·spring boot·后端
asdfg12589631 小时前
三层架构(Controller-Service-DAO)模式中的controller 和 dao/mapper的通俗理解
java·架构模式
真实的菜1 小时前
Nacos单机部署入门:避坑指南与实战
java
霸道流氓气质1 小时前
JWT 认证全面解析:原理、流程与 Spring Boot 实战
java·spring boot·后端
TeamDev1 小时前
JxBrowser 9.1.2 版本发布啦!
java·跨平台·混合应用·jxbrowser·浏览器控件·compose 多平台
逢君学术论文AI写作2 小时前
Java第21课:JavaWeb入门——Tomcat+第一个Servlet
java·servlet·tomcat
就叫_这个吧2 小时前
Java使用tomcat+servlet+filter实现简单的登录功能,需先登录再进行页面数据管理操作
java·开发语言·servlet·tomcat·jsp·filter
我是大猴子2 小时前
Stream流式编程
数据库·sql
Bert.Cai2 小时前
Oracle ASCII函数详解
数据库·oracle