永不黑的夜 發表於 2025-11-7 09:22:33

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

<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li><a href="#_label0">一、Redis 在 Spring 项目中的常见使用场景</a></li><li><a href="#_label1">二、环境准备</a></li><li><a href="#_label2">三、RedisTemplate 的作用</a></li><li><a href="#_label3">四、应用场景实战</a></li><ul class="second_class_ul"><li><a href="#_lab2_3_0">4.1 缓存(Cache)</a></li><li><a href="#_lab2_3_1">4.2 分布式 Session 管理</a></li><li><a href="#_lab2_3_2">4.3 分布式锁(Distributed Lock)</a></li><li><a href="#_lab2_3_3">4.4 消息队列(Message Queue)</a></li><li><a href="#_lab2_3_4">4.5 排行榜 / 计数系统</a></li><li><a href="#_lab2_3_5">4.6 限流(Rate Limiting)</a></li><li><a href="#_lab2_3_6">4.7 延时任务 / 定时任务</a></li><li><a href="#_lab2_3_7">4.8 热点数据保护</a></li><li><a href="#_lab2_3_8">4.9 地理位置(Geo)</a></li><li><a href="#_lab2_3_9">4.10 数据共享 / 配置中心缓存</a></li></ul></ul></div><p class="maodian"><a name="_label0"></a></p><h2>一、Redis 在 Spring 项目中的常见使用场景</h2>
<p>在企业开发中,Redis 不只是一个缓存工具,而是一个<strong>高性能分布式中间件</strong>。<br />它既能加速数据访问,也能解决很多分布式问题。</p>
<table><thead><tr><th>场景类别</th><th>场景说明</th><th>使用目的</th></tr></thead><tbody><tr><td><strong>1. 缓存(Cache)</strong></td><td>将数据库查询结果或计算结果暂存到 Redis</td><td>提高访问速度、减轻数据库压力</td></tr><tr><td><strong>2. 分布式 Session 管理</strong></td><td>在多台服务器间共享登录状态</td><td>解决 Session 不一致问题</td></tr><tr><td><strong>3. 分布式锁(Distributed Lock)</strong></td><td>控制分布式环境下的资源竞争</td><td>保证接口幂等性、避免超卖</td></tr><tr><td><strong>4. 消息队列(Message Queue)</strong></td><td>使用 Redis 的 List 或 Stream 实现简单消息队列</td><td>异步处理任务、削峰填谷</td></tr><tr><td><strong>5. 排行榜 / 计数系统</strong></td><td>使用 ZSet、Incr 等结构实现排行榜、点赞、热度统计</td><td>实时性强、性能高</td></tr><tr><td><strong>6. 限流(Rate Limiting)</strong></td><td>基于 Redis 的计数器算法实现请求限流</td><td>防止接口被恶意频繁调用</td></tr><tr><td><strong>7. 数据共享 / 配置中心缓存</strong></td><td>缓存配置、公共数据字典、系统参数</td><td>提高系统访问效率</td></tr><tr><td><strong>8. 地理位置功能(Geo)</strong></td><td>使用 Redis 的 Geo 类型</td><td>实现附近的人 / 店铺功能</td></tr><tr><td><strong>9. 延时任务 / 定时任务</strong></td><td>利用 ZSet 的分数排序实现延迟调度</td><td>替代消息队列中的延迟功能</td></tr><tr><td><strong>10. 热点数据保护</strong></td><td>对热点 Key 设置过期策略或锁</td><td>避免缓存击穿、穿透、雪崩问题</td></tr></tbody></table>
<p class="maodian"><a name="_label1"></a></p><h2>二、环境准备</h2>
<p>在开始编码前,我们需要在 Spring Boot 项目中引入 Redis 依赖。</p>
<p><strong>Maven 依赖:</strong></p>
<div class="jb51code"><pre class="brush:plain;">&lt;dependency&gt;
    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
    &lt;artifactId&gt;spring-boot-starter-data-redis&lt;/artifactId&gt;
&lt;/dependency&gt;</pre></div>
<p><strong>YAML 配置:</strong></p>
<div class="jb51code"><pre class="brush:yaml;">spring:
data:
    redis:
      host: localhost
      port: 6379
      password:
      database: 0
      timeout: 2000ms</pre></div>
<p>这就完成了基础配置,Spring Boot 会自动帮我们创建 <code>RedisTemplate</code> 对象。</p>
<p class="maodian"><a name="_label2"></a></p><h2>三、RedisTemplate 的作用</h2>
<p>在 Spring 中,<code>RedisTemplate</code> 是操作 Redis 的核心对象,<br />相当于 Redis 的 JDBC 模板。所有读写都靠它完成。</p>
<table><thead><tr><th>方法</th><th>Redis 类型</th><th>常见业务场景</th><th>示例</th></tr></thead><tbody><tr><td><code>opsForValue()</code></td><td>String</td><td>缓存对象、验证码、Token</td><td><code>set(&quot;user:1001:name&quot;, &quot;Tom&quot;)</code></td></tr><tr><td><code>opsForHash()</code></td><td>Hash</td><td>存储对象属性,如用户、商品</td><td><code>put(&quot;user:1001&quot;, &quot;age&quot;, &quot;25&quot;)</code></td></tr><tr><td><code>opsForList()</code></td><td>List</td><td>评论列表、任务队列</td><td><code>leftPush(&quot;comment:101&quot;, &quot;很好!&quot;)</code></td></tr><tr><td><code>opsForSet()</code></td><td>Set</td><td>点赞去重、好友推荐</td><td><code>add(&quot;like:post:100&quot;, &quot;user1&quot;)</code></td></tr><tr><td><code>opsForZSet()</code></td><td>ZSet</td><td>排行榜、积分榜</td><td><code>add(&quot;rank:score&quot;, &quot;Tom&quot;, 100)</code></td></tr></tbody></table>
<p><strong>一句话理解:</strong></p>
<blockquote><p>RedisTemplate 是万能 钥匙,不管你想存什么类型的数据,它都能帮你搞定。</p></blockquote>
<p class="maodian"><a name="_label3"></a></p><h2>四、应用场景实战</h2>
<p class="maodian"><a name="_lab2_3_0"></a></p><h3>4.1 缓存(Cache)</h3>
<table><thead><tr><th>分类</th><th>场景说明</th><th>使用目的</th><th>Redis 类型</th><th>示例</th></tr></thead><tbody><tr><td>缓存数据库查询结果</td><td>缓存商品详情、用户信息</td><td>提高访问速度</td><td>String / Hash</td><td>user:1001 &rarr; {&hellip;}</td></tr><tr><td>缓存热点数据</td><td>首页推荐、排行榜</td><td>提升并发性能</td><td>String / ZSet</td><td>rank:hot &rarr; </td></tr><tr><td>缓存计算结果</td><td>缓存统计分析结果</td><td>降低重复计算</td><td>String</td><td>report:2025-11 &rarr; value</td></tr><tr><td>本地 + 分布式缓存</td><td>Spring Cache + Redis</td><td>提高系统扩展性</td><td>Hash</td><td><code>@Cacheable(value=&quot;user&quot;)</code></td></tr></tbody></table>
<p><strong>业务背景</strong></p>
<p>在高并发项目中(比如商城、题库、短视频平台),数据库访问往往成为性能瓶颈。<br />而 Redis 的读写性能是数据库的上千倍,因此我们常常将热点数据缓存起来,<br />以&ldquo;读缓存 &rarr; 缓存失效 &rarr; 回源数据库&rdquo;的模式运行。</p>
<p><strong>示例:缓存用户详情数据</strong></p>
<div class="jb51code"><pre class="brush:java;">@Service
public class UserService {
    @Resource
    private RedisTemplate&lt;String, Object&gt; 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;
    }
}</pre></div>
<p><strong>亮点:</strong></p>
<ul><li>每次查询会优先走 Redis;</li><li>避免同一数据被频繁访问数据库;</li><li>可控制缓存时间,防止脏数据积累。</li></ul>
<p class="maodian"><a name="_lab2_3_1"></a></p><h3>4.2 分布式 Session 管理</h3>
<table><thead><tr><th>分类</th><th>场景说明</th><th>使用目的</th><th>Redis 类型</th><th>示例</th></tr></thead><tbody><tr><td>分布式 Session 管理</td><td>多台服务器共享登录状态</td><td>解决 Session 不一致</td><td>String / Hash</td><td>登录共享、SSO、Token 验证</td></tr></tbody></table>
<p><strong>业务背景</strong></p>
<p>假设一个网站部署在三台服务器上:</p>
<ul><li>用户第一次请求被分配到 A 服务器并登录;</li><li>第二次请求被负载均衡到 B;</li><li>B 并不知道这个用户的登录状态。</li></ul>
<blockquote><p>这就是典型的 &ldquo;Session 不一致&rdquo; 问题。</p></blockquote>
<p><strong>方案一:使用 Spring Session(推荐)</strong></p>
<p><strong>依赖:</strong></p>
<div class="jb51code"><pre class="brush:plain;">&lt;dependency&gt;
    &lt;groupId&gt;org.springframework.session&lt;/groupId&gt;
    &lt;artifactId&gt;spring-session-data-redis&lt;/artifactId&gt;
&lt;/dependency&gt;</pre></div>
<p><strong>配置:</strong></p>
<div class="jb51code"><pre class="brush:plain;">spring:
session:
    store-type: redis
redis:
    host: localhost
    port: 6379</pre></div>
<p><strong>实现效果:</strong></p>
<ul><li>自动拦截所有 <code>HttpSession</code>;</li><li>自动序列化到 Redis;</li><li>多台服务器访问同一 Redis,即可共享登录状态。</li></ul>
<p><strong>方案二:自定义 Token(前后端分离项目常用)</strong></p>
<p><strong>登录接口:</strong></p>
<div class="jb51code"><pre class="brush:java;">@PostMapping("/login")
public ResponseEntity&lt;?&gt; 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));
}</pre></div>
<p><strong>请求拦截器:</strong></p>
<div class="jb51code"><pre class="brush:java;">@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;
}</pre></div>
<p><strong>效果:</strong></p>
<ul><li>Token 与用户信息缓存在 Redis;</li><li>任意节点可通过 token 验证身份;</li><li>登录状态共享、可过期控制。</li></ul>
<p class="maodian"><a name="_lab2_3_2"></a></p><h3>4.3 分布式锁(Distributed Lock)</h3>
<table><thead><tr><th>分类</th><th>场景说明</th><th>使用目的</th><th>Redis 类型</th><th>示例</th></tr></thead><tbody><tr><td>分布式锁</td><td>控制资源竞争</td><td>保证幂等、防止超卖</td><td>String</td><td>秒杀、库存控制</td></tr></tbody></table>
<p><strong>业务背景</strong></p>
<p>电商场景中,秒杀活动高并发下会导致:</p>
<ul><li>多人同时下单同一商品;</li><li>超卖、库存负数。</li></ul>
<p>此时可以用 Redis 分布式锁,确保一个商品同一时刻只能被一个线程修改。</p>
<p><strong>代码实现:</strong></p>
<div class="jb51code"><pre class="brush:java;">@Service
public class OrderService {
    @Resource
    private RedisTemplate&lt;String, Object&gt; 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 &gt; 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) {}
}</pre></div>
<p><strong>要点说明:</strong></p>
<ul><li><code>setIfAbsent()</code> = Redis 实现的原子加锁;</li><li>过期时间避免死锁;</li><li>校验 <code>clientId</code> 防止误删他人锁。</li></ul>
<p class="maodian"><a name="_lab2_3_3"></a></p><h3>4.4 消息队列(Message Queue)</h3>
<table><thead><tr><th>分类</th><th>场景说明</th><th>使用目的</th><th>Redis 类型</th><th>示例</th></tr></thead><tbody><tr><td>消息队列</td><td>使用 List / Stream 实现异步任务</td><td>异步处理任务、削峰填谷</td><td>List / Stream</td><td>下单通知、邮件发送</td></tr></tbody></table>
<p><strong>业务背景</strong></p>
<p>假设用户下单成功后,需要:</p>
<ul><li>发送邮件;</li><li>推送短信;</li><li>更新统计数据。</li></ul>
<p>这些都可以异步执行,不应阻塞主线程。</p>
<p><strong>方案一:基于 List 的简单队列</strong></p>
<div class="jb51code"><pre class="brush:java;">// 生产者:发送任务
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);
      // 执行邮件发送逻辑
    }
}</pre></div>
<p><strong>方案二:使用 Stream(支持消费组)</strong></p>
<div class="jb51code"><pre class="brush:sql;">XADD queue:order * orderId 1001 userId 2002
XGROUP CREATE queue:order group1 0 MKSTREAM</pre></div>
<p><strong>消费者代码:</strong></p>
<div class="jb51code"><pre class="brush:java;">@Scheduled(fixedDelay = 3000)
public void consumeOrder() {
    List&lt;MapRecord&lt;String, Object, Object&gt;&gt; messages = redisTemplate.opsForStream()
      .read(Consumer.from("group1", "consumerA"),
            StreamReadOptions.empty().count(1),
            StreamOffset.create("queue:order", ReadOffset.lastConsumed()));
    if (messages != null) {
      for (MapRecord&lt;String, Object, Object&gt; msg : messages) {
            System.out.println("消费订单消息:" + msg.getValue());
            redisTemplate.opsForStream().acknowledge("queue:order", "group1", msg.getId());
      }
    }
}</pre></div>
<p><strong>优势:</strong></p>
<ul><li>支持多消费者组;</li><li>消息可持久化;</li><li>可追溯未消费记录。</li></ul>
<p class="maodian"><a name="_lab2_3_4"></a></p><h3>4.5 排行榜 / 计数系统</h3>
<table><thead><tr><th>分类</th><th>场景说明</th><th>使用目的</th><th>Redis 类型</th><th>示例</th></tr></thead><tbody><tr><td>排行榜 / 计数</td><td>实现积分榜、热度榜、点赞统计</td><td>实时计算 + 高性能排序</td><td>ZSet / String</td><td>热门文章榜、游戏积分榜</td></tr></tbody></table>
<p><strong>业务背景</strong></p>
<p>在很多应用中(例如:<strong>短视频热榜、游戏积分榜、帖子点赞排行</strong>),我们都需要根据&ldquo;分数&rdquo;动态排序。<br />数据库的排序操作成本高,而 Redis 的 <code>ZSet</code> 类型天然支持「按分数排序」,<br />非常适合做排行榜类功能。</p>
<p><strong>代码示例:用户积分排行榜</strong></p>
<div class="jb51code"><pre class="brush:java;">@Service
public class RankService {
    @Resource
    private RedisTemplate&lt;String, Object&gt; 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&lt;String&gt; 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);
    }
}</pre></div>
<p><strong>使用效果:</strong></p>
<div class="jb51code"><pre class="brush:java;">rankService.addScore("Tom", 10);
rankService.addScore("Jerry", 20);
rankService.addScore("Alice", 15);
System.out.println(rankService.getTopUsers(3)); // </pre></div>
<p><strong>扩展应用</strong></p>
<ul><li>文章热度排行榜(根据阅读量 / 点赞数累计)</li><li>商品销量榜单</li><li>实时在线活跃用户榜</li><li>视频播放量榜单</li></ul>
<p class="maodian"><a name="_lab2_3_5"></a></p><h3>4.6 限流(Rate Limiting)</h3>
<table><thead><tr><th>分类</th><th>场景说明</th><th>使用目的</th><th>Redis 类型</th><th>示例</th></tr></thead><tbody><tr><td>接口限流</td><td>限制单位时间内访问次数</td><td>防止接口被刷、控制 QPS</td><td>String</td><td>登录防暴力 破解、API 限速</td></tr></tbody></table>
<p><strong>业务背景</strong></p>
<p>在接口开放或秒杀活动中,往往会出现某些接口被频繁请求,<br />轻则浪费资源,重则打垮系统。</p>
<p>此时我们可以利用 Redis 做&ldquo;计数限流&rdquo;:</p>
<blockquote><p>以 IP / 用户 ID 为维度,统计在某个时间窗口内的访问次数。</p></blockquote>
<p><strong>代码示例:基于固定窗口的限流实现</strong></p>
<div class="jb51code"><pre class="brush:java;">@Service
public class RateLimitService {
    @Resource
    private RedisTemplate&lt;String, Object&gt; 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 &gt; LIMIT_COUNT) {
            throw new RuntimeException("请求过于频繁,请稍后再试!");
      }
    }
}</pre></div>
<p><strong>示例使用:</strong></p>
<div class="jb51code"><pre class="brush:sql;">@GetMapping("/api/test")
public String test(HttpServletRequest request) {
    String ip = request.getRemoteAddr();
    rateLimitService.checkRateLimit(ip);
    return "正常访问成功";
}
</pre></div>
<p><strong>升级方案:滑动窗口限流</strong></p>
<p>使用 Redis 的 <code>ZSet</code> 来记录时间戳,实现更精确的滑动窗口限流:</p>
<div class="jb51code"><pre class="brush:java;">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 &lt;= limit;
}</pre></div>
<p class="maodian"><a name="_lab2_3_6"></a></p><h3>4.7 延时任务 / 定时任务</h3>
<table><thead><tr><th>分类</th><th>场景说明</th><th>使用目的</th><th>Redis 类型</th><th>示例</th></tr></thead><tbody><tr><td>延时任务 / 定时任务</td><td>基于 ZSet 时间戳实现延迟执行</td><td>实现订单超时取消、消息延迟发送</td><td>ZSet</td><td>延迟队列、定时提醒</td></tr></tbody></table>
<p><strong>业务背景</strong></p>
<p>在很多业务中存在&ldquo;延时触发&rdquo;的逻辑,例如:</p>
<ul><li>订单 30 分钟未支付自动取消;</li><li>延迟发送消息;</li><li>提前 10 分钟推送会议提醒。</li></ul>
<p>使用 Redis 的 <strong>ZSet</strong> 可以轻松实现。</p>
<p><strong>代码示例:订单超时取消任务</strong></p>
<div class="jb51code"><pre class="brush:java;">@Service
public class DelayTaskService {
    @Resource
    private RedisTemplate&lt;String, Object&gt; 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&lt;Object&gt; 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);
            }
      }
    }
}</pre></div>
<p><strong>测试示例:</strong></p>
<div class="jb51code"><pre class="brush:sql;">delayTaskService.addDelayTask(1001L, 60000); // 1分钟后取消订单
</pre></div>
<p class="maodian"><a name="_lab2_3_7"></a></p><h3>4.8 热点数据保护</h3>
<table><thead><tr><th>分类</th><th>场景说明</th><th>使用目的</th><th>Redis 类型</th><th>示例</th></tr></thead><tbody><tr><td>热点数据保护</td><td>防止缓存击穿、穿透、雪崩</td><td>保证系统稳定性</td><td>String / Hash</td><td>商品详情缓存、热点新闻</td></tr></tbody></table>
<p><strong>业务背景</strong></p>
<p>在高并发系统中常见三种缓存问题:</p>
<table><thead><tr><th>问题类型</th><th>含义</th><th>解决方案</th></tr></thead><tbody><tr><td>缓存穿透</td><td>查询不存在的 Key 导致每次都打数据库</td><td>设置空值缓存、布隆过滤器</td></tr><tr><td>缓存击穿</td><td>热点 Key 失效瞬间被大量请求打爆</td><td>加互斥锁、延迟失效</td></tr><tr><td>缓存雪崩</td><td>大量 Key 同时过期</td><td>设置随机过期时间、预热机制</td></tr></tbody></table>
<p><strong>代码示例:缓存击穿防护</strong></p>
<div class="jb51code"><pre class="brush:java;">@Service
public class ProductService {
    @Resource
    private RedisTemplate&lt;String, Object&gt; 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;
    }
}</pre></div>
<p class="maodian"><a name="_lab2_3_8"></a></p><h3>4.9 地理位置(Geo)</h3>
<table><thead><tr><th>分类</th><th>场景说明</th><th>使用目的</th><th>Redis 类型</th><th>示例</th></tr></thead><tbody><tr><td>地理位置功能</td><td>使用 Geo 存储经纬度信息</td><td>实现&ldquo;附近的人 / 店铺&rdquo;功能</td><td>Geo</td><td>外卖、打车、社交定位</td></tr></tbody></table>
<p><strong>示例:查找附近商铺</strong></p>
<div class="jb51code"><pre class="brush:java;">@Service
public class GeoService {
    @Resource
    private RedisTemplate&lt;String, Object&gt; 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&lt;RedisGeoCommands.GeoLocation&lt;Object&gt;&gt; results =
            redisTemplate.opsForGeo().radius(GEO_KEY, area);
      results.forEach(r -&gt; System.out.println("附近店铺:" + r.getContent().getName()));
    }
}</pre></div>
<p class="maodian"><a name="_lab2_3_9"></a></p><h3>4.10 数据共享 / 配置中心缓存</h3>
<table><thead><tr><th>分类</th><th>场景说明</th><th>使用目的</th><th>Redis 类型</th><th>示例</th></tr></thead><tbody><tr><td>数据共享 / 配置缓存</td><td>缓存公共配置与数据字典</td><td>减少数据库压力</td><td>Hash / String</td><td>系统参数缓存</td></tr></tbody></table>
<p><strong>代码示例:系统配置缓存</strong></p>
<div class="jb51code"><pre class="brush:java;">@Service
public class ConfigService {
    @Resource
    private RedisTemplate&lt;String, Object&gt; 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";
    }
}</pre></div>
頁: [1]
查看完整版本: Redis 在 Spring 项目中的使用及操作方法