Redis中的某一热点数据缓存过期了,此时有大量请求访问怎么办?

1、提前设置热点数据永不过期

2、分布式中用redis分布式锁(锁可以在多个 JVM 实例之间协调)、单体中用synchronized(锁只在同一个 JVM 内有效)

编写服务类
复制代码
import com.redisson.api.RLock;
import com.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

@Service
public class CacheService {

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private RedissonClient redissonClient;

    private static final String HOT_DATA_KEY = "hotData";
    private static final String LOCK_KEY = "hotDataLock";

    public String getHotData() {
        // 尝试从 Redis 中获取热点数据
        String hotData = redisTemplate.opsForValue().get(HOT_DATA_KEY);
        if (hotData == null) {
            // 获取分布式锁
            RLock lock = redissonClient.getLock(LOCK_KEY);
            try {
                // 尝试加锁,最多等待100ms,锁的过期时间为30秒
                if (lock.tryLock(100, 30, TimeUnit.SECONDS)) {
                    try {
                        // 再次检查缓存是否过期(双重检查)
                        hotData = redisTemplate.opsForValue().get(HOT_DATA_KEY);
                        if (hotData == null) {
                            // 缓存确实过期,从数据库加载数据
                            hotData = loadHotDataFromDatabase();
                            // 将数据存入 Redis,设置过期时间为10分钟
                            redisTemplate.opsForValue().set(HOT_DATA_KEY, hotData, 10, TimeUnit.MINUTES);
                        }
                    } finally {
                        // 释放锁
                        lock.unlock();
                    }
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
        return hotData;
    }

    private String loadHotDataFromDatabase() {
        // 模拟从数据库加载数据
        return "Hot Data from Database";
    }
}
模拟多个请求
复制代码
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@Component
public class CacheTestRunner implements CommandLineRunner {

    @Autowired
    private CacheService cacheService;

    @Override
    public void run(String... args) throws Exception {
        // 模拟 10 个请求同时访问热点数据
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 10; i++) {
            executorService.submit(() -> {
                String hotData = cacheService.getHotData();
                System.out.println("Thread " + Thread.currentThread().getId() + " got hot data: " + hotData);
            });
        }
        executorService.shutdown();
    }
}

Thread 12 got hot data: Hot Data from Database
Thread 13 got hot data: Hot Data from Database
Thread 14 got hot data: Hot Data from Database
...

所有线程最终都会获取到相同的数据,但只有第一个线程会去加载数据,避免了缓存击穿问题。

单体应用

在单体应用中,所有请求都运行在同一个 JVM 实例中,因此可以使用 synchronized 来同步线程。

java复制

复制代码
@Service
public class CacheService {

    @Autowired
    private StringRedisTemplate redisTemplate;

    private static final String HOT_DATA_KEY = "hotData";
    private static final Object lock = new Object(); // 用于同步的锁对象

    public String getHotData() {
        // 尝试从 Redis 中获取热点数据
        String hotData = redisTemplate.opsForValue().get(HOT_DATA_KEY);
        if (hotData == null) {
            synchronized (lock) { // 使用 synchronized 同步
                // 再次检查缓存是否过期(双重检查)
                hotData = redisTemplate.opsForValue().get(HOT_DATA_KEY);
                if (hotData == null) {
                    // 缓存确实过期,从数据库加载数据
                    hotData = loadHotDataFromDatabase();
                    // 将数据存入 Redis,设置过期时间为10分钟
                    redisTemplate.opsForValue().set(HOT_DATA_KEY, hotData, 10, TimeUnit.MINUTES);
                }
            }
        }
        return hotData;
    }

    private String loadHotDataFromDatabase() {
        // 模拟从数据库加载数据
        return "Hot Data from Database";
    }
}
相关推荐
蓝色 - Lanse11 分钟前
模型推理如何利用非前缀缓存
人工智能·缓存
问道飞鱼13 分钟前
【数据库知识】PGSQL常见命令行与函数
数据库·oracle·pgsql
CoookeCola14 分钟前
MovieNet (paper) :推动电影理解研究的综合数据集与基准
数据库·论文阅读·人工智能·计算机视觉·视觉检测·database
酷柚易汛智推官15 分钟前
MySQL到达梦数据库快速替换操作指南
数据库·mysql·酷柚易汛
PawSQL16 分钟前
企业级SQL审核优化工具 PawSQL 介绍(3)- 审核工单管理
数据库·sql·pawsql·sql审核
TDengine (老段)1 小时前
TDengine 数学函数 TRUNCATE 用户手册
大数据·数据库·物联网·时序数据库·iot·tdengine·涛思数据
鹓于1 小时前
Excel图片批量插入与文件瘦身
java·服务器·数据库
TDengine (老段)1 小时前
TDengine 数据函数 CORR 用户手册
大数据·数据库·物联网·时序数据库·tdengine·1024程序员节
倔强的石头_2 小时前
【金仓数据库】ksql 指南(四) —— 创建与管理表(KingbaseES 数据存储核心)
数据库
赵渝强老师2 小时前
【赵渝强老师】TiDB PD集群存储的信息
数据库·mysql·tidb