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

Redis缓存技术详解与实战

Redis缓存技术详解与实战

Redis作为一个开源的内存数据结构存储系统,它可以用作数据库、缓存和消息代理。在现代高并发、大数据量处理的系统中,Redis作为缓存层的应用越来越广泛。本文将详细讲解Redis在查询、添加缓存、更新缓存、缓存预热、缓存穿透、缓存雪崩、缓存击穿等场景下的应用及解决方案,并通过实例代码来加深理解。

1. 查询缓存

1.1 场景描述

在Web应用中,对于读多写少的场景,为了提升性能,我们通常会选择将热点数据放入Redis中进行缓存。当应用需要读取数据时,首先会尝试从Redis中获取,如果Redis中存在该数据,则直接返回;否则,从数据库中查询并放入Redis中。

1.2 如何使用redis

在应用程序中,当需要获取某个数据项时,首先会尝试从Redis缓存中获取。如果缓存中存在该数据项,则直接返回给应用程序,从而避免了访问后端数据库的开销。如果缓存中不存在该数据项,则从数据库中获取数据,并将其存储在Redis缓存中以备后用。

1.3 解决方案

import redis.clients.jedis.Jedis;  public class RedisCache {  private static final String REDIS_HOST = "localhost";  private static final int REDIS_PORT = 6379;  private static Jedis jedis;  static {  jedis = new Jedis(REDIS_HOST, REDIS_PORT);  }  public static String getData(String key) {  String data = jedis.get(key);  if (data == null) {  // 模拟从数据库查询数据  data = "Data from DB";  // 将数据存入Redis缓存  jedis.set(key, data);  // 可以设置过期时间  jedis.expire(key, 60); // 60秒后过期  }  return data;  }  
}

2. 添加缓存

2.1 场景描述

当数据发生更新时,为了保证缓存与数据库的一致性,我们需要将数据同步更新到Redis中。

2.2 如何使用redis

当有新数据需要存储时,可以直接将数据添加到Redis缓存中。这通常发生在数据首次被创建或修改时。将数据存储在Redis中可以使后续的数据访问更加快速。

2.3 解决方案

// 实际上,在查询缓存的示例中,如果Redis中没有数据,就已经包含了添加缓存的逻辑  
// 但为了明确说明,这里再提供一个简单的添加缓存方法  
public static void addData(String key, String value) {  jedis.set(key, value);  // 可以选择性地设置过期时间  jedis.expire(key, 60); // 60秒后过期  
}

3. 更新缓存

3.1 场景描述

在某些场景下,我们可能需要对缓存的数据进行批量更新或定期更新。

3.2 如何使用redis

当数据发生变化时,需要更新Redis缓存中的相应项以确保缓存中的数据是最新的。这可以通过简单地使用Redis的SET命令(或相应的库函数)来实现,用新的数据值替换旧的数据值。

3.3 解决方案

public static void updateData(String key, String newValue) {  // 假设newValue是新的数据值  jedis.set(key, newValue);  // 可以选择性地更新过期时间  jedis.expire(key, 60); // 如果需要的话  
}

4. 缓存预热

4.1 场景描述

在系统启动或低峰时段,为了提升后续访问的性能,我们可以预先将热点数据加载到Redis缓存中。

4.2 如何使用redis

缓存预热是在系统启动或低峰时段预先加载缓存数据的过程。通过预先加载数据到缓存中,可以确保在高峰时段应用程序可以更快地响应请求,因为所需的数据已经在缓存中可用。缓存预热通常涉及从数据库中读取数据并将其存储在Redis缓存中。

4.3 解决方案

编写一个预热脚本,在系统启动或低峰时段,将预计会被频繁访问的数据加载到Redis中。

public static void warmUpCache() {  // 假设我们有一个需要预热的key列表  List<String> keysToWarmUp = Arrays.asList("key1", "key2", "key3");  for (String key : keysToWarmUp) {  // 模拟从数据库查询数据  String data = "Data for " + key;  // 将数据添加到Redis缓存中  jedis.set(key, data);  // 设置过期时间(如果需要)  jedis.expire(key, 60 * 60 * 24); // 一天后过期  }  
}

5. 缓存穿透

5.1 场景描述

缓存穿透是指查询一个不存在的数据,由于缓存中不存在该数据,导致每次请求都会去数据库中查询,从而失去缓存的意义。

5.2 如何使用redis

缓存穿透是指查询一个不存在的数据,由于缓存中不存在该数据,因此每次查询都会去数据库查询,从而增加了数据库的负载。为了解决这个问题,可以使用布隆过滤器来快速判断一个数据是否存在于Redis缓存中。如果布隆过滤器判断该数据不存在于缓存中,则可以直接返回结果,而无需去数据库查询。

5.3 解决方案

  1. 布隆过滤器
    使用布隆过滤器来过滤掉不存在的数据,减少对数据库的无效查询。

  2. 空值缓存
    对于不存在的数据,在Redis中缓存一个空值或特殊标记,并设置一个较短的过期时间。

由于Java本身不直接支持布隆过滤器,但可以使用Google的Guava库或者Redis的Bitmaps来实现类似的功能。这里仅提供一个概念性的描述。

6. 缓存雪崩

6.1 场景描述

缓存雪崩是指缓存中大量数据同时过期,导致大量请求直接涌入数据库,给数据库带来巨大压力。

6.2 如何使用redis

缓存雪崩是指由于大量的缓存数据同时过期,导致大量的请求都去数据库查询,从而增加了数据库的负载。为了解决这个问题,可以采取以下几种策略:

  • 随机过期时间:为缓存数据设置不同的过期时间,以避免大量的缓存数据同时过期。
  • 缓存降级:当缓存不可用或查询压力过大时,可以临时关闭缓存查询,直接返回默认数据或执行一些降级逻辑。
  • 缓存预热:在系统启动或低峰时段预先加载缓存数据,以减轻高峰时段的压力。

6.3 解决方案

  1. 随机过期时间
    设置缓存过期时间时,避免大量数据同时过期,可以为每个key设置一个随机的过期时间。

  2. 降级策略
    当数据库压力过大时,可以暂时关闭部分非核心功能,保证核心功能的正常运行。

在Java中,防止缓存雪崩的策略主要是确保缓存的key不是同时过期。

// 当设置缓存过期时间时,使用随机时间  
public static void setWithRandomExpire(String key, String value) {  int randomSeconds = 60 + new Random().nextInt(60 * 60); // 1分钟到1小时之间的随机时间  jedis.setex(key, randomSeconds, value);  
}

7. 缓存击穿

7.1 场景描述

缓存击穿是指某个热点key突然过期,此时大量请求会涌入数据库,导致数据库压力骤增。

7.2 如何使用redis

缓存击穿是指某个热点数据的缓存突然失效,导致大量的请求都去数据库查询该数据。为了解决这个问题,可以使用锁或其他同步机制来确保只有一个请求去数据库查询数据,而其他请求则等待第一个请求返回数据并更新缓存。这样,其他请求就可以直接从缓存中获取数据,而无需再去数据库查询。

7.3 解决方案

  1. 互斥锁
    在访问缓存之前,使用互斥锁(如Redis的SETNX命令)来确保只有一个请求能够去数据库中查询数据,其他请求则等待该请求将数据加载到缓存中。

  2. 热点数据永不过期
    对于某些热点数据,可以设置其永不过期,或者设置一个较长的过期时间。

import java.util.concurrent.locks.ReentrantLock;  public class CacheWithLock {  private static final ReentrantLock lock = new ReentrantLock();  public static String getDataWithLock(String key) {  String data = jedis.get(key);  if (data == null) {  lock.lock();  try {  // 再次检查,防止其他线程已经加载了数据  data = jedis.get(key);  if (data == null) {  // 模拟从数据库查询数据  data = "Data from DB with lock";  // 将数据存入Redis缓存  jedis.set(key, data);  // 设置过期时间  jedis.expire(key, 60);  }  } finally {  lock.unlock();  }  }  return data;  }  
}
http://www.lryc.cn/news/370491.html

相关文章:

  • 业务架构的位置及关系
  • CMS与AI的融合:构建万能表单小程序系统
  • 机器学习常见知识点 2:决策树
  • 海洋CMS admin_notify.php 远程代码执行漏洞复现(CVE-2024-30565)
  • Spring、Spring MVC、MyBatis和Spring Boot对比
  • 【Linux高级IO】select、poll、epoll
  • Etcd Raft架构设计和源码剖析2:数据流
  • 深入理解Qt多线程编程(QThreadPool)
  • Prisma数据库ORM框架学习
  • Flutter-使用MethodChannel 实现与iOS交互
  • 【星海随笔】云解决方案学习日志篇(一) ELK,kibana,Logstash安装
  • 【leetcode】hot100 哈希表
  • 每日5题Day22 - LeetCode 106 - 110
  • 【Python】读取文件夹中所有excel文件拼接成一个excel表格 的方法
  • 7. 通配符和正则表达式
  • ROS2底层机制源码分析
  • 超越 Transformer开启高效开放语言模型的新篇章
  • 快速排序-Hoare 递归版 C语言
  • C语言经典指针运算笔试题图文解析
  • 使用 KubeKey v3.1.1 离线部署原生 Kubernetes v1.28.8 实战
  • DOS 命令
  • 如何用Java程序实现一个简单的消息队列?
  • OpenAI 宕机事件:GPT 停摆的影响与应对
  • linux常用的基础命令
  • 618家用智能投影仪推荐:这个高性价比品牌不容错过
  • 自愿离婚协议书
  • WPS JSA 宏脚本入门和样例
  • Printing and Exporting
  • c++【入门】正多边形每个内角的度数
  • spring boot3登录开发-邮箱登录/注册接口实现