Redis 在用户管理系统中的典型应用场景
结合你的用户增删改查接口,以下是 Redis 的实用场景和具体实现方案:
场景 | 作用 | 实现方案 |
---|---|---|
用户信息缓存 | 减少数据库压力,加速查询响应 | 使用 Spring Cache + Redis 注解缓存 |
登录 Token 存储 | 分布式 Session 或 JWT Token 管理 | 将 Token 与用户信息绑定,设置过期时间 |
接口限流 | 防止恶意刷接口 | 基于 Redis 计数器实现滑动窗口限流 |
重复提交拦截 | 防止用户重复提交表单 | 用 Redis 存储请求唯一标识,设置短期过期 |
热点数据预加载 | 提前缓存高频访问数据 | 定时任务 + Redis 存储 |
Mac M1 安装 Redis 详细步骤
1. 通过 Homebrew 安装 Redis
# 安装 Homebrew(如果尚未安装)
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"# 安装 Redis
brew install redis
2. 启动 Redis 服务
# 前台启动(测试用,Ctrl+C 退出)
redis-server# 后台启动(推荐)
brew services start redis
3. 验证安装
# 连接 Redis 客户端
redis-cli ping # 应返回 "PONG"
Spring Boot 3 整合 Redis
1. 添加依赖
在 pom.xml
中:
<!-- Spring Cache 核心依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-cache</artifactId></dependency><!-- Redis 驱动 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId></dependency>
2. 配置 Redis 连接
application.yml
:
spring:data:redis:host: localhostport: 6379# password: your-password # 如果设置了密码lettuce:pool:max-active: 8max-idle: 8
3. 示例
配置类
package com.example.spring_demo01.config;import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.*;import java.time.Duration;@Configuration
public class RedisConfig {// 配置 RedisTemplate@Beanpublic RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {RedisTemplate<String, Object> template = new RedisTemplate<>();template.setConnectionFactory(factory);// Key 序列化template.setKeySerializer(new StringRedisSerializer());// Value 序列化为 JSONtemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());// Hash 结构序列化template.setHashKeySerializer(new StringRedisSerializer());template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());return template;}// 配置缓存管理器@Beanpublic CacheManager cacheManager(RedisConnectionFactory factory) {RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig().serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())).entryTtl(Duration.ofMinutes(30)); // 设置默认过期时间return RedisCacheManager.builder(factory).cacheDefaults(config).build();}
}
接口限流工具类
package com.example.spring_demo01.utils;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;import java.util.UUID;
import java.util.concurrent.TimeUnit;@Component
public class RateLimiter {@Autowiredprivate RedisTemplate<String, Object> redisTemplate;public boolean allowRequest(String userId) {String key = "rate_limit:" + userId;long now = System.currentTimeMillis();long windowMs = 60_000; // 1 分钟// 移除窗口外的请求记录redisTemplate.opsForZSet().removeRangeByScore(key, 0, now - windowMs);// 统计当前窗口内请求数Long count = redisTemplate.opsForZSet().zCard(key);if (count != null && count >= 10) {return false; // 超过限制}// 记录本次请求redisTemplate.opsForZSet().add(key, UUID.randomUUID().toString(), now);redisTemplate.expire(key, windowMs, TimeUnit.MILLISECONDS);return true;}
}
实体类
package com.example.spring_demo01.entity;import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;import java.io.Serializable;@Data
@TableName("user")
@JsonIgnoreProperties(ignoreUnknown = true) // 防止 JSON 反序列化问题
public class User implements Serializable { // 实现 Serializable@TableId(type = IdType.AUTO) // 主键自增private Long id;private String name;private Integer age;private String email;
}
service层
package com.example.spring_demo01.service.impl;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.spring_demo01.entity.User;
import com.example.spring_demo01.mapper.UserMapper;
import com.example.spring_demo01.service.UserService;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;import java.io.Serializable;@Service
public class UserServiceImplextends ServiceImpl<UserMapper, User>implements UserService {// 对 MyBatis Plus 的 getById 方法添加缓存@Cacheable(value = "user", key = "#id")@Overridepublic User getById(Serializable id) {return super.getById(id);}// 更新时清除缓存@CacheEvict(value = "user", key = "#entity.id")@Overridepublic boolean updateById(User entity) {return super.updateById(entity);}// 删除时清除缓存@CacheEvict(value = "user", key = "#id")@Overridepublic boolean removeById(Serializable id) {return super.removeById(id);}
}
controller层
package com.example.spring_demo01.controller;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.spring_demo01.annotation.AdminOnly;
import com.example.spring_demo01.entity.User;
import com.example.spring_demo01.service.UserService;
import com.example.spring_demo01.utils.RateLimiter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;import java.time.Duration;
import java.util.List;
import java.util.UUID;@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {@Autowiredprivate UserService userService;@Autowiredprivate RedisTemplate<String, Object> redisTemplate;@Autowiredprivate RateLimiter rateLimiter;// ------------------------------ 增 ------------------------------@PostMappingpublic String addUser(@RequestBody User user, @RequestHeader String clientId) {String key = "SUBMIT_LOCK:" + clientId + ":" + user.hashCode();// 10秒内不允许重复提交Boolean success = redisTemplate.opsForValue().setIfAbsent(key, "", Duration.ofSeconds(10));if (Boolean.FALSE.equals(success)) {throw new RuntimeException("请勿重复提交");}userService.save(user);return "新增成功";}// ------------------------------ 删 ------------------------------@DeleteMapping("/{id}")public String deleteUser(@PathVariable Long id) {userService.removeById(id);return "删除成功";}@DeleteMapping("/batch")public String deleteBatch(@RequestBody List<Long> ids) {userService.removeByIds(ids);return "批量删除成功";}// ------------------------------ 改 ------------------------------@PutMappingpublic String updateUser(@RequestBody User user) {userService.updateById(user);return "更新成功";}// ------------------------------ 查 ------------------------------@GetMapping("/{id}")@AdminOnlypublic User getUserById(@PathVariable Long id) {return userService.getById(id);}@GetMapping("/list")public List<User> listUsers(@RequestParam(required = false) String name,@RequestParam(required = false) Integer age) {QueryWrapper<User> wrapper = new QueryWrapper<>();if (name != null) {wrapper.like("name", name); // 模糊查询姓名}if (age != null) {wrapper.eq("age", age); // 精确查询年龄}return userService.list(wrapper);}@GetMapping("/page")public Page<User> pageUsers(@RequestParam(defaultValue = "1") Integer pageNum,@RequestParam(defaultValue = "10") Integer pageSize,@RequestHeader(value = "Authorization") String token) {// 从 Token 中获取用户IDlog.info("token:{}", token);log.info("User:{}", redisTemplate.opsForValue().get(token.split(" ")[1]));User user = (User) redisTemplate.opsForValue().get(token.split(" ")[1]);if (user == null) throw new RuntimeException("未登录");// 限流校验if (!rateLimiter.allowRequest("PAGE_" + user.getId())) {throw new RuntimeException("请求过于频繁");}return userService.page(new Page<>(pageNum, pageSize));}// ------------------------------ other ------------------------------@GetMapping("/error")public String getError() {throw new RuntimeException();}@PostMapping("/login")public String login(@RequestBody User user) {log.info("login user:{}", user);// 验证用户逻辑(示例简化)User dbUser = userService.getOne(new QueryWrapper<User>().eq("id", user.getId()).eq("name", user.getName()));if (dbUser == null) {throw new RuntimeException("登录失败");}// 生成 Token 并存储String token = "TOKEN_" + UUID.randomUUID();redisTemplate.opsForValue().set(token,dbUser,Duration.ofMinutes(30));return token;}
}
在启动类添加注解:
package com.example.spring_demo01;import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.cache.annotation.EnableCaching;@SpringBootApplication
@MapperScan("com.example.spring_demo01.mapper")
@ServletComponentScan // 启用 Servlet 组件扫描(如 Filter、Servlet)
@EnableCaching // 启动缓存,Redis使用
public class SpringDemo01Application {public static void main(String[] args) {SpringApplication.run(SpringDemo01Application.class, args);}}
常见问题排查
Q1: 连接 Redis 超时
- 检查服务状态:运行
redis-cli ping
确认 Redis 是否正常运行 - 查看端口占用:
lsof -i :6379
- 关闭防火墙:
sudo ufw allow 6379
Q2: Spring Boot 无法注入 RedisTemplate
- 确认配置类:添加
@EnableCaching
和@Configuration
- 检查序列化器:显式配置序列化方式避免 ClassCastException
@Configuration public class RedisConfig {@Beanpublic RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {RedisTemplate<String, Object> template = new RedisTemplate<>();template.setConnectionFactory(factory);template.setKeySerializer(new StringRedisSerializer());template.setValueSerializer(new GenericJackson2JsonRedisSerializer());return template;} }
总结
通过 Redis 你可以为项目快速实现:
- 高性能缓存层 - 降低数据库负载
- 分布式会话管理 - 支持横向扩展
- 精细化流量控制 - 保障系统稳定性