Redis缓存介绍

二级缓存(Secondary Cache)是指在系统中使用两个缓存层次的架构,通常用于优化数据读取效率。最常见的二级缓存结构包括 本地缓存(一级缓存)远程缓存(Redis作为二级缓存)

在很多应用中,为了提高性能,使用 Redis 作为第二层缓存(即二级缓存)。一级缓存通常是应用程序的内存缓存(例如 Java 中的 ConcurrentHashMap 或使用框架如 CaffeineEhcache 等),而 Redis 作为二级缓存,提供分布式的缓存层,能够支持多个应用实例共享缓存数据。

在这种模式下,一级缓存负责快速获取和存储数据,二级缓存(Redis)则是一个备份缓存,它的数据通常比一级缓存的存储数据更大,且存储时间较长,能够缓存大量的共享数据。

一级缓存与二级缓存的工作原理

  1. 一级缓存(本地缓存):一级缓存通常存在于应用的内存中,每次访问时都会首先检查本地缓存。本地缓存的优势是访问速度非常快,但缺点是只能在单一应用实例中使用,且缓存数据的量受到应用内存的限制。
  2. 二级缓存(Redis):Redis 作为二级缓存通常用来存储大量数据。它是分布式的,可以跨多个应用实例共享缓存数据,适合存储相对持久且访问频繁的数据。Redis 的访问速度也非常快,但相较于本地缓存稍慢。

Redis 二级缓存的工作流程

  1. 查询流程:首先检查一级缓存:当应用程序需要获取数据时,首先会检查本地缓存(一级缓存)。如果一级缓存中没有数据:然后查询 Redis 二级缓存,如果 Redis 中有数据,则从 Redis 中获取并将数据缓存到一级缓存中。如果 Redis 中也没有数据,则从数据库中查询,并将数据先写入 Redis,然后再缓存到一级缓存。
  2. 写入流程:当数据被更新时,先更新数据库,再更新 Redis(二级缓存),最后将更新的数据写入一级缓存。通常使用 缓存失效策略,如 TTL(过期时间)来保证缓存中的数据保持一致性。
  3. 一致性问题:由于本地缓存和 Redis 缓存存在不同步的问题,因此需要采取一些策略来保持缓存数据的一致性。例如:更新缓存策略:每次更新数据时,不仅更新数据库,也更新缓存。过期策略:通过定期过期或手动失效缓存,确保缓存中的数据不会过时。

Redis 二级缓存的优势

  • 提高性能:通过将数据存储在内存中,无需每次都访问数据库,减少数据库的压力,提高响应速度。
  • 减少数据库负载:只要数据存在缓存中,就无需查询数据库,减少数据库的压力。
  • 扩展性:使用 Redis 作为二级缓存时,能够有效处理大规模的数据缓存,支持分布式环境下的多实例共享。

Redis 二级缓存的常见问题与解决方案

  1. 缓存穿透:即查询的数据在缓存和数据库中都不存在,解决方案是将空值缓存(空对象缓存),避免每次请求都查询数据库。
  2. 缓存击穿:某个热点数据的缓存过期,导致多个请求同时查询数据库,解决方案是使用互斥锁(SETNX 命令)来保证只有一个请求查询数据库,其他请求等待。
  3. 缓存雪崩:大量缓存同时过期,导致所有请求同时访问数据库,解决方案是设置不同的缓存过期时间,避免缓存集中失效。
  4. 缓存一致性:本地缓存和 Redis 中的数据可能不一致,解决方案是使用双写机制,即更新数据库时,同时更新 Redis 和本地缓存。

Redis二级缓存

为了实现 Redis 作为二级缓存,可以使用 JedisSpring Cache 等工具来管理缓存。以下是一个简单的 JedisConcurrentHashMap 本地缓存实现二级缓存的示例。

1. 使用 Jedis 和本地缓存实现 Redis 二级缓存

Maven 依赖
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>4.3.0</version>
</dependency>
代码实现
import redis.clients.jedis.Jedis;
import java.util.concurrent.ConcurrentHashMap;

public class RedisSecondaryCacheExample {
    
    // 一级缓存:本地缓存,使用 ConcurrentHashMap
    private static final ConcurrentHashMap<String, String> localCache = new ConcurrentHashMap<>();

    // Redis 客户端实例(假设 Redis 服务运行在本地)
    private static final Jedis jedis = new Jedis("localhost", 6379);

    public static void main(String[] args) {
        String key = "user:1001";

        // 1. 尝试从一级缓存获取数据
        String value = getFromLocalCache(key);
        if (value == null) {
            // 2. 如果一级缓存没有,从 Redis 获取数据
            value = getFromRedis(key);
            if (value == null) {
                // 3. 如果 Redis 没有,从数据库获取数据
                value = getFromDatabase(key);

                // 4. 更新 Redis 和一级缓存
                setToRedis(key, value);
                setToLocalCache(key, value);
            }
        }
        
        System.out.println("Fetched value: " + value);
    }

    // 从本地缓存获取数据
    public static String getFromLocalCache(String key) {
        return localCache.get(key);
    }

    // 设置数据到本地缓存
    public static void setToLocalCache(String key, String value) {
        localCache.put(key, value);
    }

    // 从 Redis 获取数据
    public static String getFromRedis(String key) {
        return jedis.get(key);
    }

    // 设置数据到 Redis
    public static void setToRedis(String key, String value) {
        jedis.setex(key, 3600, value); // 设置 1 小时过期时间
    }

    // 模拟从数据库获取数据
    public static String getFromDatabase(String key) {
        // 模拟从数据库查询数据
        return "Database value for " + key;
    }
}

  1. 本地缓存(一级缓存):使用 ConcurrentHashMap 作为本地缓存。通过 localCache.get(key) 方法检查是否有缓存数据。
  2. Redis 二级缓存:使用 Jedis 客户端连接 Redis。通过 jedis.get(key) 获取 Redis 中的缓存数据,若 Redis 中没有数据,则从数据库查询并更新 Redis 和本地缓存。
  3. 缓存操作:使用 setex 方法将数据写入 Redis,并设置过期时间(1 小时)。
  4. 模拟数据库查询:getFromDatabase 方法模拟从数据库获取数据的操作。实际生产中,应该替换为实际的数据库查询操作。

2. 使用 Spring Cache 配置 Redis 作为二级缓存

Spring 提供了 Spring Cache 抽象,可以非常方便地实现二级缓存,结合 Redis 和本地缓存。以下是 Spring Cache 配置示例。

Maven 依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
Spring 配置示例
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.annotation.CacheEvict;
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.cache.CacheManager;

@Configuration
@EnableCaching
public class CacheConfig {

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
        RedisCacheConfiguration cacheConfig = RedisCacheConfiguration.defaultCacheConfig();
        return RedisCacheManager.builder(connectionFactory)
                .cacheDefaults(cacheConfig)
                .build();
    }
}
使用 Spring Cache
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    // Redis 作为二级缓存,使用 @Cacheable 注解来标记缓存操作
    @Cacheable(value = "userCache", key = "#userId")
    public String getUserById(String userId) {
        // 模拟从数据库获取数据
        return "Database value for user " + userId;
    }
}

  1. @Cacheable 注解:使用 @Cacheable 注解标记需要缓存的方法,Spring 会自动将方法的返回值缓存到 Redis 中。
  2. Redis 配置:通过 RedisCacheManager 配置 Redis 作为缓存提供者。
  3. 本地缓存与 Redis 缓存:Spring 会先查找本地缓存(如 ConcurrentHashMap),如果没有,则从 Redis 中获取缓存。如果 Redis 中没有,则执行方法并缓存返回值。
Redis的碎碎念 文章被收录于专栏

Redis面试中的碎碎念

全部评论

相关推荐

评论
4
4
分享

创作者周榜

更多
牛客网
牛客企业服务