Redis

关注公众号 jb51net

关闭
首页 > 数据库 > Redis > Redis  Spring 项目使用

Redis 在 Spring 项目中的使用及操作方法

作者:Alfredorw

本文详细介绍了Redis在Spring项目中的常见使用场景,展示了如何利用Redis解决各种分布式问题,提升系统性能和用户体验,感兴趣的朋友跟随小编一起看看吧

一、Redis 在 Spring 项目中的常见使用场景

在企业开发中,Redis 不只是一个缓存工具,而是一个高性能分布式中间件
它既能加速数据访问,也能解决很多分布式问题。

场景类别场景说明使用目的
1. 缓存(Cache)将数据库查询结果或计算结果暂存到 Redis提高访问速度、减轻数据库压力
2. 分布式 Session 管理在多台服务器间共享登录状态解决 Session 不一致问题
3. 分布式锁(Distributed Lock)控制分布式环境下的资源竞争保证接口幂等性、避免超卖
4. 消息队列(Message Queue)使用 Redis 的 List 或 Stream 实现简单消息队列异步处理任务、削峰填谷
5. 排行榜 / 计数系统使用 ZSet、Incr 等结构实现排行榜、点赞、热度统计实时性强、性能高
6. 限流(Rate Limiting)基于 Redis 的计数器算法实现请求限流防止接口被恶意频繁调用
7. 数据共享 / 配置中心缓存缓存配置、公共数据字典、系统参数提高系统访问效率
8. 地理位置功能(Geo)使用 Redis 的 Geo 类型实现附近的人 / 店铺功能
9. 延时任务 / 定时任务利用 ZSet 的分数排序实现延迟调度替代消息队列中的延迟功能
10. 热点数据保护对热点 Key 设置过期策略或锁避免缓存击穿、穿透、雪崩问题

二、环境准备

在开始编码前,我们需要在 Spring Boot 项目中引入 Redis 依赖。

Maven 依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

YAML 配置:

spring:
  data:
    redis:
      host: localhost
      port: 6379
      password:
      database: 0
      timeout: 2000ms

这就完成了基础配置,Spring Boot 会自动帮我们创建 RedisTemplate 对象。

三、RedisTemplate 的作用

在 Spring 中,RedisTemplate 是操作 Redis 的核心对象,
相当于 Redis 的 JDBC 模板。所有读写都靠它完成。

方法Redis 类型常见业务场景示例
opsForValue()String缓存对象、验证码、Tokenset("user:1001:name", "Tom")
opsForHash()Hash存储对象属性,如用户、商品put("user:1001", "age", "25")
opsForList()List评论列表、任务队列leftPush("comment:101", "很好!")
opsForSet()Set点赞去重、好友推荐add("like:post:100", "user1")
opsForZSet()ZSet排行榜、积分榜add("rank:score", "Tom", 100)

一句话理解:

RedisTemplate 是万能 钥匙,不管你想存什么类型的数据,它都能帮你搞定。

四、应用场景实战

4.1 缓存(Cache)

分类场景说明使用目的Redis 类型示例
缓存数据库查询结果缓存商品详情、用户信息提高访问速度String / Hashuser:1001 → {…}
缓存热点数据首页推荐、排行榜提升并发性能String / ZSetrank:hot → [id, score]
缓存计算结果缓存统计分析结果降低重复计算Stringreport:2025-11 → value
本地 + 分布式缓存Spring Cache + Redis提高系统扩展性Hash@Cacheable(value="user")

业务背景

在高并发项目中(比如商城、题库、短视频平台),数据库访问往往成为性能瓶颈。
而 Redis 的读写性能是数据库的上千倍,因此我们常常将热点数据缓存起来,
以“读缓存 → 缓存失效 → 回源数据库”的模式运行。

示例:缓存用户详情数据

@Service
public class UserService {
    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    @Resource
    private UserMapper userMapper;
    public User getUserById(Long id) {
        String key = "user:" + id;
        // Step 1: 尝试从缓存中读取
        User user = (User) redisTemplate.opsForValue().get(key);
        if (user != null) {
            System.out.println("【命中缓存】userId = " + id);
            return user;
        }
        // Step 2: 缓存未命中 → 查询数据库
        user = userMapper.selectById(id);
        if (user == null) {
            return null;
        }
        // Step 3: 写入缓存,设置过期时间 10 分钟
        redisTemplate.opsForValue().set(key, user, 10, TimeUnit.MINUTES);
        System.out.println("【写入缓存】userId = " + id);
        return user;
    }
}

亮点:

4.2 分布式 Session 管理

分类场景说明使用目的Redis 类型示例
分布式 Session 管理多台服务器共享登录状态解决 Session 不一致String / Hash登录共享、SSO、Token 验证

业务背景

假设一个网站部署在三台服务器上:

这就是典型的 “Session 不一致” 问题。

方案一:使用 Spring Session(推荐)

依赖:

<dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session-data-redis</artifactId>
</dependency>

配置:

spring:
  session:
    store-type: redis
  redis:
    host: localhost
    port: 6379

实现效果:

方案二:自定义 Token(前后端分离项目常用)

登录接口:

@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody LoginRequest request) {
    User user = userMapper.findByUsername(request.getUsername());
    if (user == null || !user.getPassword().equals(request.getPassword())) {
        return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("用户名或密码错误");
    }
    // 生成 token
    String token = UUID.randomUUID().toString();
    redisTemplate.opsForValue().set("login:token:" + token, user, 30, TimeUnit.MINUTES);
    return ResponseEntity.ok(Map.of("token", token));
}

请求拦截器:

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
    String token = request.getHeader("Authorization");
    if (token == null) return false;
    User user = (User) redisTemplate.opsForValue().get("login:token:" + token);
    if (user == null) {
        response.setStatus(401);
        return false;
    }
    request.setAttribute("user", user);
    return true;
}

效果:

4.3 分布式锁(Distributed Lock)

分类场景说明使用目的Redis 类型示例
分布式锁控制资源竞争保证幂等、防止超卖String秒杀、库存控制

业务背景

电商场景中,秒杀活动高并发下会导致:

此时可以用 Redis 分布式锁,确保一个商品同一时刻只能被一个线程修改。

代码实现:

@Service
public class OrderService {
    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    public void placeOrder(Long productId) {
        String lockKey = "lock:product:" + productId;
        String clientId = UUID.randomUUID().toString();
        try {
            Boolean locked = redisTemplate.opsForValue()
                .setIfAbsent(lockKey, clientId, 10, TimeUnit.SECONDS);
            if (Boolean.FALSE.equals(locked)) {
                throw new RuntimeException("系统繁忙,请稍后再试");
            }
            // 模拟扣库存逻辑
            Integer stock = getStock(productId);
            if (stock > 0) {
                updateStock(productId, stock - 1);
                System.out.println("下单成功,库存剩余:" + (stock - 1));
            } else {
                System.out.println("库存不足");
            }
        } finally {
            // 释放锁(防止误删)
            String value = (String) redisTemplate.opsForValue().get(lockKey);
            if (clientId.equals(value)) {
                redisTemplate.delete(lockKey);
            }
        }
    }
    private Integer getStock(Long id) { return 5; } // 模拟
    private void updateStock(Long id, Integer newStock) {}
}

要点说明:

4.4 消息队列(Message Queue)

分类场景说明使用目的Redis 类型示例
消息队列使用 List / Stream 实现异步任务异步处理任务、削峰填谷List / Stream下单通知、邮件发送

业务背景

假设用户下单成功后,需要:

这些都可以异步执行,不应阻塞主线程。

方案一:基于 List 的简单队列

// 生产者:发送任务
public void sendMailTask(String mailJson) {
    redisTemplate.opsForList().leftPush("queue:mail", mailJson);
}
// 消费者:异步线程处理任务
@Scheduled(fixedDelay = 2000)
public void consumeMailTask() {
    String task = (String) redisTemplate.opsForList().rightPop("queue:mail");
    if (task != null) {
        System.out.println("发送邮件任务:" + task);
        // 执行邮件发送逻辑
    }
}

方案二:使用 Stream(支持消费组)

XADD queue:order * orderId 1001 userId 2002
XGROUP CREATE queue:order group1 0 MKSTREAM

消费者代码:

@Scheduled(fixedDelay = 3000)
public void consumeOrder() {
    List<MapRecord<String, Object, Object>> messages = redisTemplate.opsForStream()
        .read(Consumer.from("group1", "consumerA"),
              StreamReadOptions.empty().count(1),
              StreamOffset.create("queue:order", ReadOffset.lastConsumed()));
    if (messages != null) {
        for (MapRecord<String, Object, Object> msg : messages) {
            System.out.println("消费订单消息:" + msg.getValue());
            redisTemplate.opsForStream().acknowledge("queue:order", "group1", msg.getId());
        }
    }
}

优势:

4.5 排行榜 / 计数系统

分类场景说明使用目的Redis 类型示例
排行榜 / 计数实现积分榜、热度榜、点赞统计实时计算 + 高性能排序ZSet / String热门文章榜、游戏积分榜

业务背景

在很多应用中(例如:短视频热榜、游戏积分榜、帖子点赞排行),我们都需要根据“分数”动态排序。
数据库的排序操作成本高,而 Redis 的 ZSet 类型天然支持「按分数排序」,
非常适合做排行榜类功能。

代码示例:用户积分排行榜

@Service
public class RankService {
    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    private static final String RANK_KEY = "rank:user:score";
    // 增加用户积分
    public void addScore(String userId, double score) {
        redisTemplate.opsForZSet().incrementScore(RANK_KEY, userId, score);
    }
    // 获取前 N 名用户
    public Set<String> getTopUsers(int limit) {
        return redisTemplate.opsForZSet().reverseRange(RANK_KEY, 0, limit - 1);
    }
    // 查询某个用户的排名
    public Long getRank(String userId) {
        return redisTemplate.opsForZSet().reverseRank(RANK_KEY, userId);
    }
    // 查询某个用户的分数
    public Double getUserScore(String userId) {
        return redisTemplate.opsForZSet().score(RANK_KEY, userId);
    }
}

使用效果:

rankService.addScore("Tom", 10);
rankService.addScore("Jerry", 20);
rankService.addScore("Alice", 15);
System.out.println(rankService.getTopUsers(3)); // [Jerry, Alice, Tom]

扩展应用

4.6 限流(Rate Limiting)

分类场景说明使用目的Redis 类型示例
接口限流限制单位时间内访问次数防止接口被刷、控制 QPSString登录防暴力 破解、API 限速

业务背景

在接口开放或秒杀活动中,往往会出现某些接口被频繁请求,
轻则浪费资源,重则打垮系统。

此时我们可以利用 Redis 做“计数限流”:

以 IP / 用户 ID 为维度,统计在某个时间窗口内的访问次数。

代码示例:基于固定窗口的限流实现

@Service
public class RateLimitService {
    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    // 每分钟最多请求 60 次
    private static final int LIMIT_COUNT = 60;
    private static final int EXPIRE_TIME = 60;
    public void checkRateLimit(String ip) {
        String key = "limit:ip:" + ip;
        Long count = redisTemplate.opsForValue().increment(key);
        if (count == 1) {
            redisTemplate.expire(key, EXPIRE_TIME, TimeUnit.SECONDS);
        }
        if (count > LIMIT_COUNT) {
            throw new RuntimeException("请求过于频繁,请稍后再试!");
        }
    }
}

示例使用:

@GetMapping("/api/test")
public String test(HttpServletRequest request) {
    String ip = request.getRemoteAddr();
    rateLimitService.checkRateLimit(ip);
    return "正常访问成功";
}

升级方案:滑动窗口限流

使用 Redis 的 ZSet 来记录时间戳,实现更精确的滑动窗口限流:

public boolean allowRequest(String key, int limit, int seconds) {
    long now = System.currentTimeMillis();
    long windowStart = now - seconds * 1000L;
    redisTemplate.opsForZSet().removeRangeByScore(key, 0, windowStart);
    redisTemplate.opsForZSet().add(key, String.valueOf(now), now);
    long count = redisTemplate.opsForZSet().zCard(key);
    redisTemplate.expire(key, seconds, TimeUnit.SECONDS);
    return count <= limit;
}

4.7 延时任务 / 定时任务

分类场景说明使用目的Redis 类型示例
延时任务 / 定时任务基于 ZSet 时间戳实现延迟执行实现订单超时取消、消息延迟发送ZSet延迟队列、定时提醒

业务背景

在很多业务中存在“延时触发”的逻辑,例如:

使用 Redis 的 ZSet 可以轻松实现。

代码示例:订单超时取消任务

@Service
public class DelayTaskService {
    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    private static final String DELAY_KEY = "delay:order";
    // 添加延时任务
    public void addDelayTask(Long orderId, long delayMs) {
        long executeTime = System.currentTimeMillis() + delayMs;
        redisTemplate.opsForZSet().add(DELAY_KEY, orderId, executeTime);
        System.out.println("添加延迟任务: orderId=" + orderId + ", 执行时间=" + executeTime);
    }
    // 定时扫描执行任务
    @Scheduled(fixedDelay = 5000)
    public void executeTasks() {
        long now = System.currentTimeMillis();
        Set<Object> tasks = redisTemplate.opsForZSet().rangeByScore(DELAY_KEY, 0, now);
        if (tasks != null) {
            for (Object task : tasks) {
                System.out.println("执行超时任务: orderId=" + task);
                // 执行业务逻辑:关闭订单
                redisTemplate.opsForZSet().remove(DELAY_KEY, task);
            }
        }
    }
}

测试示例:

delayTaskService.addDelayTask(1001L, 60000); // 1分钟后取消订单

4.8 热点数据保护

分类场景说明使用目的Redis 类型示例
热点数据保护防止缓存击穿、穿透、雪崩保证系统稳定性String / Hash商品详情缓存、热点新闻

业务背景

在高并发系统中常见三种缓存问题:

问题类型含义解决方案
缓存穿透查询不存在的 Key 导致每次都打数据库设置空值缓存、布隆过滤器
缓存击穿热点 Key 失效瞬间被大量请求打爆加互斥锁、延迟失效
缓存雪崩大量 Key 同时过期设置随机过期时间、预热机制

代码示例:缓存击穿防护

@Service
public class ProductService {
    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    @Resource
    private ProductMapper productMapper;
    public Product getProduct(Long id) {
        String key = "product:" + id;
        // 1. 先查缓存
        Product product = (Product) redisTemplate.opsForValue().get(key);
        if (product != null) {
            return product;
        }
        // 2. 缓存为空,加锁防止击穿
        synchronized (this) {
            product = (Product) redisTemplate.opsForValue().get(key);
            if (product == null) {
                product = productMapper.selectById(id);
                if (product == null) {
                    // 防止穿透:缓存空对象
                    redisTemplate.opsForValue().set(key, new Product(), 2, TimeUnit.MINUTES);
                } else {
                    // 防止雪崩:过期时间随机
                    int expireTime = 10 + new Random().nextInt(5);
                    redisTemplate.opsForValue().set(key, product, expireTime, TimeUnit.MINUTES);
                }
            }
        }
        return product;
    }
}

4.9 地理位置(Geo)

分类场景说明使用目的Redis 类型示例
地理位置功能使用 Geo 存储经纬度信息实现“附近的人 / 店铺”功能Geo外卖、打车、社交定位

示例:查找附近商铺

@Service
public class GeoService {
    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    private static final String GEO_KEY = "shop:geo";
    // 添加商铺坐标
    public void addShop(String shopName, double lon, double lat) {
        redisTemplate.opsForGeo().add(GEO_KEY, new Point(lon, lat), shopName);
    }
    // 查找附近商铺
    public void findNearby(double lon, double lat, double distanceKm) {
        Circle area = new Circle(new Point(lon, lat), new Distance(distanceKm, Metrics.KILOMETERS));
        GeoResults<RedisGeoCommands.GeoLocation<Object>> results =
            redisTemplate.opsForGeo().radius(GEO_KEY, area);
        results.forEach(r -> System.out.println("附近店铺:" + r.getContent().getName()));
    }
}

4.10 数据共享 / 配置中心缓存

分类场景说明使用目的Redis 类型示例
数据共享 / 配置缓存缓存公共配置与数据字典减少数据库压力Hash / String系统参数缓存

代码示例:系统配置缓存

@Service
public class ConfigService {
    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    public String getConfig(String key) {
        String value = (String) redisTemplate.opsForHash().get("config:system", key);
        if (value == null) {
            // 假设从数据库加载配置
            value = loadFromDb(key);
            redisTemplate.opsForHash().put("config:system", key, value);
        }
        return value;
    }
    private String loadFromDb(String key) {
        // 模拟数据库
        if ("maxLoginRetry".equals(key)) return "5";
        return "default";
    }
}

到此这篇关于Redis 在 Spring 项目中的使用(包看包会)的文章就介绍到这了,更多相关Redis Spring 项目使用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文