当前位置: 首页 > news >正文

Redis使用规范

一:预防缓存雪崩

方案1:差异化缓存过期时间,不要让大量的 Key 在同一时间过期。
比如,在初始化缓存的时候,设置缓存的过期时间是 30 秒 +10 秒以内的随机延迟(扰动值)。这样,这些Key 不会集中在 30 秒这个时刻过期,而是会分散在 30~40 秒之间过期:

@PostConstruct
public void rightInit1() {IntStream.rangeClosed(1, 1000).forEach(i -> stringRedisTemplate.opsForValue().set("city" + i, getCityFromDb(i), 30 + ThreadLocalRandom.current().nextInt(10), TimeUnit.SECONDS));log.info("Cache init finished");
}

方案2:让缓存不主动过期。
初始化缓存数据的时候设置缓存永不过期,然后启动一个后台线程 30 秒一次定时把所有数据更新到缓存,而且通过适当的休眠,控制从数据库更新数据的频率,降低数据库压力。

@PostConstruct
public void rightInit2() throws InterruptedException {CountDownLatch countDownLatch = new CountDownLatch(1);// 每隔30秒全量更新一次缓存Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(() -> {IntStream.rangeClosed(1, 1000).forEach(i -> {String data = getCityFromDb(i);// 模拟更新缓存需要一定的时间try {TimeUnit.MILLISECONDS.sleep(20);} catch (InterruptedException e) {}if (!StringUtils.isEmpty(data)) {// 缓存永不过期,被动更新stringRedisTemplate.opsForValue().set("city" + i, data);}});log.info("Cache update finished");countDownLatch.countDown();}, 0, 30, TimeUnit.SECONDS);countDownLatch.await();
}

方案一和方案二是截然不同的两种缓存方式,如果无法全量缓存所有数据,那么只能使用方案一;
即使使用了方案二,缓存永不过期,同样需要在查询的时候,确保有回源的逻辑。正如之前所说,我们无法确保缓存系统中的数据永不丢失。
不管是方案一还是方案二,在把数据从数据库加入缓存的时候,都需要判断来自数据库的数据是否合法,比如进行最基本的判空检查。

二:注意缓存击穿问题

@GetMapping("right")
public String right() {String data = stringRedisTemplate.opsForValue().get("hotsopt");if (StringUtils.isEmpty(data)) {// 获取分布式锁RLock locker = redissonClient.getLock("locker");if (locker.tryLock()) {try {data = stringRedisTemplate.opsForValue().get("hotsopt");// 双重检查if (StringUtils.isEmpty(data)) {// 回源到数据库查询data = getExpensiveData();stringRedisTemplate.opsForValue().set("hotsopt", data, 5, TimeUnit.SECONDS);}} finally {locker.unlock();}}}return data;
}

在真实的业务场景下,不一定要这么严格地使用双重检查分布式锁进行全局的并发限制,因
为这样虽然可以把数据库回源并发降到最低,但也限制了缓存失效时的并发。可以考虑的方
式是:
方案一,使用进程内的锁进行限制,这样每一个节点都可以以一个并发回源数据库;
方案二,不使用锁进行限制,而是使用类似 Semaphore 的工具限制并发数,比如限制为 10,这样既限制了回源并发数不至于太大,又能使得一定量的线程可以同时回源。

三:注意缓存穿透问题

方案一:缓存空数据

@GetMapping("right")
public String right(@RequestParam("id") int id) {String key = "user" + id;String data = stringRedisTemplate.opsForValue().get(key);if (StringUtils.isEmpty(data)) {data = getCityFromDb(id);//校验从数据库返回的数据是否有效if (!StringUtils.isEmpty(data)) {stringRedisTemplate.opsForValue().set(key, data, 30, TimeUnit.SECONDS);} else {//如果无效,直接在缓存中设置一个NODATA,这样下次查询时即使是无效用户还是可以命中stringRedisTemplate.opsForValue().set(key, "NODATA", 30, TimeUnit.SECONDS);}}return data;
}


方案二:使用布隆过滤器

@GetMapping("right2")
public String right2(@RequestParam("id") int id) {String data = "";// 通过布隆过滤器先判断if (bloomFilter.mightContain(id)) {String key = "user" + id;// 走缓存查询data = stringRedisTemplate.opsForValue().get(key);if (StringUtils.isEmpty(data)) {data = getCityFromDb(id);stringRedisTemplate.opsForValue().set(key, data, 30, TimeUnit.SECONDS);}}return data;
}

方案一,对于不存在的数据,同样设置一个特殊的 Value 到缓存中,比如当数据库中查出
的用户信息为空的时候,设置 NODATA 这样具有特殊含义的字符串到缓存中。这样下次请
求缓存的时候还是可以命中缓存,即直接从缓存返回结果,不查询数据库。但,这种方式可能会把大量无效的数据加入缓存中,如果担心大量无效数据占满缓存的话还
可以考虑方案二,即使用布隆过滤器做前置过滤。

对于方案二,我们需要同步所有可能存在的值并加入布隆过滤器,这是比较麻烦的地方。如
果业务规则明确的话,你也可以考虑直接根据业务规则判断值是否存在。
其实,方案二可以和方案一同时使用,即将布隆过滤器前置,对于误判的情况再保存特殊值
到缓存,双重保险避免无效数据查询请求打到数据库。

redis缓存更新策略

1、先更新缓存,再更新数据库;
“先更新缓存再更新数据库”策略不可行。数据库设计复杂,压力集中,数据库因为超时等原因更新操作失败的可能性较大,此外还会涉及事务,很可能因为数据库更新失败,

导致缓存和数据库的数据不一致。



2、先更新数据库,再更新缓存;
“先更新数据库再更新缓存”策略不可行。一是,如果线程 A 和 B 先后完成数据库更新,但更新缓存时却是 B 和 A 的顺序,那很可能会把旧数据更新到缓存中引起数据不一致;
二是,我们不确定缓存中的数据是否会被访问,不一定要把所有数据都更新到缓存中去。

3、先删除缓存,再更新数据库,访问的时候按需加载数据到缓存;
“先删除缓存再更新数据库,访问的时候按需加载数据到缓存”策略也不可行。在并发的情况下,很可能删除缓存后还没来得及更新数据库,就有另一个线程先读取了旧值到缓存中,
如果并发量很大的话这个概率也会很大。



4、先更新数据库,再删除缓存,访问的时候按需加载数据到缓存。
“先更新数据库再删除缓存,访问的时候按需加载数据到缓存”策略是最好的。虽然在极端情况下,这种策略也可能出现数据不一致的问题,但概率非常低,基本可以忽略。举一
个“极端情况”的例子,比如更新数据的时间节点恰好是缓存失效的瞬间,这时 A 先读取到了旧值,随后在 B 操作数据库完成更新并且删除了缓存之后,A 再把旧值加入缓存。

相关文章:

  • Redis Sentinel 非集群模式高可用部署指南
  • agentformer论文阅读
  • Vue-8-前端框架Vue之应用基础响应式数据和计算属性
  • 数据库系统概论(二十)数据库恢复技术
  • linux防火墙讲解
  • 封号零风险」策略:用亚矩阵云手机解锁Telegram的100%隐匿工作流
  • MacOS15.5 MySQL8 开启 mysql_native_password
  • python在word创建w:t元素
  • 城市生命线安全运行“一网统管”体系建设思路
  • Excel将表格文件由宽数据转为长数据的方法
  • llama_index chromadb实现RAG的简单应用
  • Spring Cloud LoadBalancer深度解析:官方负载均衡方案迁移指南与避坑实践
  • 第七节:Vben Admin 最新 v5.0 (vben5) 快速入门 - 用户管理(上)
  • 基于CNN的FashionMNIST数据集识别6——DenseNet模型
  • Tomcat 核心配置解析:4 大文件、乱码处理、端口与 Manager 配置
  • 技术分享:UMI机器人操作通用框架在Franka机器人上的配置方法
  • UE5 游戏模板 —— Puzzle 拼图游戏
  • BERT介绍
  • leetcode:263. 丑数(python3解法,数学相关算法题)
  • 浅谈 Unity XR:从混战到统一,OpenXR 的演进与现实困境
  • 上海网站建设的网站/搜索引擎有哪些
  • 网站做301重定向的作用/东莞做网站公司
  • 网站高端设计/河南郑州网站顾问
  • 初创企业网站建设流程/引流推广的句子
  • 建网站做商城个体户资质可以/合肥网站制作推广
  • 好的买手表网站/合肥seo服务商