您好,登录后才能下订单哦!
密码登录
            
            
            
            
        登录注册
            
            
            
        点击 登录注册 即表示同意《亿速云用户服务条款》
        # SpringBoot如何使用Redis缓存
## 1. Redis缓存概述
### 1.1 什么是Redis
Redis(Remote Dictionary Server)是一个开源的、基于内存的数据结构存储系统,可以用作数据库、缓存和消息中间件。它支持多种数据结构,包括字符串(strings)、哈希(hashes)、列表(lists)、集合(sets)、有序集合(sorted sets)等。
Redis的主要特点:
- 高性能:基于内存操作,读写速度极快
- 持久化:支持RDB和AOF两种持久化方式
- 丰富的数据结构:支持多种高级数据结构
- 原子性:所有操作都是原子性的
- 发布/订阅:支持消息发布订阅模式
### 1.2 为什么选择Redis作为缓存
在SpringBoot应用中使用Redis作为缓存有诸多优势:
1. **性能提升**:相比直接访问数据库,Redis的响应时间通常在微秒级别
2. **减轻数据库压力**:高频访问数据可以从缓存获取,减少数据库查询
3. **分布式支持**:Redis天然支持分布式,适合微服务架构
4. **丰富的数据结构**:可以灵活处理各种缓存场景
5. **过期策略**:支持设置缓存过期时间,自动清理旧数据
### 1.3 Spring缓存抽象
Spring框架提供了缓存抽象,通过注解的方式简化缓存操作。主要注解包括:
- `@EnableCaching`:启用缓存支持
- `@Cacheable`:在方法执行前检查缓存
- `@CachePut`:将方法返回值放入缓存
- `@CacheEvict`:清除缓存
- `@Caching`:组合多个缓存操作
- `@CacheConfig`:类级别的共享缓存配置
## 2. 环境准备与配置
### 2.1 安装Redis
#### Windows安装
1. 下载Redis for Windows:https://github.com/microsoftarchive/redis/releases
2. 解压zip包
3. 运行redis-server.exe启动服务
4. 使用redis-cli.exe连接测试
#### Linux安装
```bash
# Ubuntu/Debian
sudo apt update
sudo apt install redis-server
# CentOS/RHEL
sudo yum install epel-release
sudo yum install redis
sudo systemctl start redis
sudo systemctl enable redis
docker run --name my-redis -p 6379:6379 -d redis
使用Spring Initializr创建项目,添加以下依赖:
或手动添加Maven依赖:
<dependencies>
    <!-- Spring Boot Starter Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <!-- Spring Data Redis -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    
    <!-- Lombok -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    
    <!-- 测试依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>
在application.properties或application.yml中配置Redis连接:
# application.properties
spring.redis.host=localhost
spring.redis.port=6379
spring.redis.password=
spring.redis.database=0
# 连接池配置
spring.redis.lettuce.pool.max-active=8
spring.redis.lettuce.pool.max-idle=8
spring.redis.lettuce.pool.min-idle=0
spring.redis.lettuce.pool.max-wait=-1ms
或YAML格式:
# application.yml
spring:
  redis:
    host: localhost
    port: 6379
    password: 
    database: 0
    lettuce:
      pool:
        max-active: 8
        max-idle: 8
        min-idle: 0
        max-wait: -1ms
在主应用类上添加@EnableCaching注解:
@SpringBootApplication
@EnableCaching
public class RedisCacheApplication {
    public static void main(String[] args) {
        SpringApplication.run(RedisCacheApplication.class, args);
    }
}
@Service
public class ProductService {
    
    @Cacheable(value = "products", key = "#id")
    public Product getProductById(Long id) {
        // 模拟数据库查询
        System.out.println("Fetching product from database...");
        return new Product(id, "Product " + id, 100.00);
    }
}
@CachePut(value = "products", key = "#product.id")
public Product updateProduct(Product product) {
    // 更新数据库逻辑
    System.out.println("Updating product in database...");
    return product;
}
@CacheEvict(value = "products", key = "#id")
public void deleteProduct(Long id) {
    // 删除数据库记录
    System.out.println("Deleting product from database...");
}
// 清除所有缓存
@CacheEvict(value = "products", allEntries = true)
public void clearAllCache() {
    System.out.println("Clearing all products cache...");
}
@Caching(
    cacheable = {
        @Cacheable(value = "products", key = "#name")
    },
    put = {
        @CachePut(value = "products", key = "#result.id")
    }
)
public Product getProductByName(String name) {
    // 查询逻辑
    return productRepository.findByName(name);
}
@Configuration
public class RedisConfig {
    
    @Bean
    public KeyGenerator customKeyGenerator() {
        return (target, method, params) -> {
            StringBuilder sb = new StringBuilder();
            sb.append(target.getClass().getSimpleName());
            sb.append(":");
            sb.append(method.getName());
            sb.append(":");
            for (Object obj : params) {
                sb.append(obj.toString());
            }
            return sb.toString();
        };
    }
}
// 使用自定义Key生成器
@Cacheable(value = "products", keyGenerator = "customKeyGenerator")
public Product getProduct(Long id) {
    // ...
}
@Configuration
public class RedisConfig {
    
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        
        // 使用Jackson2JsonRedisSerializer序列化value
        Jackson2JsonRedisSerializer<Object> serializer = 
            new Jackson2JsonRedisSerializer<>(Object.class);
        
        ObjectMapper mapper = new ObjectMapper();
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        mapper.activateDefaultTyping(
            mapper.getPolymorphicTypeValidator(), 
            ObjectMapper.DefaultTyping.NON_FINAL
        );
        serializer.setObjectMapper(mapper);
        
        // 设置key和value的序列化规则
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(serializer);
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(serializer);
        
        template.afterPropertiesSet();
        return template;
    }
}
@Configuration
@EnableCaching
public class RedisCacheConfig {
    
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
            .entryTtl(Duration.ofMinutes(30))  // 默认过期时间30分钟
            .disableCachingNullValues()        // 不缓存null值
            .serializeKeysWith(RedisSerializationContext.SerializationPair
                .fromSerializer(new StringRedisSerializer()))
            .serializeValuesWith(RedisSerializationContext.SerializationPair
                .fromSerializer(new GenericJackson2JsonRedisSerializer()));
        
        // 针对不同cacheName设置不同的过期时间
        Map<String, RedisCacheConfiguration> cacheConfigurations = new HashMap<>();
        cacheConfigurations.put("products", 
            config.entryTtl(Duration.ofHours(1)));
        cacheConfigurations.put("users", 
            config.entryTtl(Duration.ofMinutes(10)));
        
        return RedisCacheManager.builder(factory)
            .cacheDefaults(config)
            .withInitialCacheConfigurations(cacheConfigurations)
            .transactionAware()
            .build();
    }
}
实现本地缓存(Caffeine)与Redis的多级缓存:
@Configuration
@EnableCaching
public class MultiLevelCacheConfig {
    
    @Bean
    @Primary
    public CacheManager cacheManager() {
        CaffeineCacheManager caffeineCacheManager = new CaffeineCacheManager();
        caffeineCacheManager.setCaffeine(Caffeine.newBuilder()
            .expireAfterWrite(Duration.ofMinutes(10))
            .maximumSize(1000));
        return caffeineCacheManager;
    }
    
    @Bean
    public CacheManager redisCacheManager(RedisConnectionFactory factory) {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
            .entryTtl(Duration.ofHours(1));
        return RedisCacheManager.builder(factory)
            .cacheDefaults(config)
            .build();
    }
    
    @Bean
    public CacheManager multiLevelCacheManager(
            @Qualifier("cacheManager") CacheManager localCacheManager,
            @Qualifier("redisCacheManager") CacheManager redisCacheManager) {
        return new MultiLevelCacheManager(localCacheManager, redisCacheManager);
    }
}
public class MultiLevelCacheManager implements CacheManager {
    
    private final CacheManager localCacheManager;
    private final CacheManager redisCacheManager;
    
    public MultiLevelCacheManager(CacheManager localCacheManager, 
                                 CacheManager redisCacheManager) {
        this.localCacheManager = localCacheManager;
        this.redisCacheManager = redisCacheManager;
    }
    
    @Override
    public Cache getCache(String name) {
        return new MultiLevelCache(
            localCacheManager.getCache(name),
            redisCacheManager.getCache(name)
        );
    }
    
    @Override
    public Collection<String> getCacheNames() {
        return redisCacheManager.getCacheNames();
    }
}
public class MultiLevelCache implements Cache {
    
    private final Cache localCache;
    private final Cache redisCache;
    
    public MultiLevelCache(Cache localCache, Cache redisCache) {
        this.localCache = localCache;
        this.redisCache = redisCache;
    }
    
    @Override
    public String getName() {
        return redisCache.getName();
    }
    
    @Override
    public Object getNativeCache() {
        return redisCache.getNativeCache();
    }
    
    @Override
    public ValueWrapper get(Object key) {
        // 先从本地缓存获取
        ValueWrapper value = localCache.get(key);
        if (value != null) {
            return value;
        }
        
        // 本地缓存没有,从Redis获取
        value = redisCache.get(key);
        if (value != null) {
            // 将Redis中的数据放入本地缓存
            localCache.put(key, value.get());
        }
        return value;
    }
    
    // 实现其他Cache接口方法...
}
@Service
public class ProductServiceImpl implements ProductService {
    
    @Autowired
    private ProductRepository productRepository;
    
    @Override
    @Cacheable(value = "products", key = "#id", unless = "#result == null")
    public Product getProductById(Long id) {
        return productRepository.findById(id).orElse(null);
    }
    
    @Override
    @CachePut(value = "products", key = "#product.id")
    public Product updateProduct(Product product) {
        return productRepository.save(product);
    }
    
    @Override
    @CacheEvict(value = "products", key = "#id")
    public void deleteProduct(Long id) {
        productRepository.deleteById(id);
    }
    
    @Override
    @Cacheable(value = "products", key = "'list:' + #pageable.pageNumber")
    public Page<Product> listProducts(Pageable pageable) {
        return productRepository.findAll(pageable);
    }
}
@Service
public class SessionService {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    private static final String SESSION_PREFIX = "session:";
    
    public void createSession(User user, String sessionId) {
        String key = SESSION_PREFIX + sessionId;
        redisTemplate.opsForValue().set(key, user, Duration.ofHours(2));
    }
    
    public User getSession(String sessionId) {
        String key = SESSION_PREFIX + sessionId;
        return (User) redisTemplate.opsForValue().get(key);
    }
    
    public void refreshSession(String sessionId) {
        String key = SESSION_PREFIX + sessionId;
        redisTemplate.expire(key, Duration.ofHours(2));
    }
    
    public void deleteSession(String sessionId) {
        String key = SESSION_PREFIX + sessionId;
        redisTemplate.delete(key);
    }
}
@Service
public class HotDataService {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    private static final String HOT_PRODUCTS_KEY = "hot:products";
    private static final String TOP_SEARCHES_KEY = "top:searches";
    
    public void incrementProductView(Long productId) {
        String key = "product:views:" + productId;
        redisTemplate.opsForValue().increment(key);
        
        // 添加到热门商品ZSET
        redisTemplate.opsForZSet().incrementScore(HOT_PRODUCTS_KEY, productId.toString(), 1);
    }
    
    public List<Long> getHotProducts(int limit) {
        Set<Object> productIds = redisTemplate.opsForZSet()
            .reverseRange(HOT_PRODUCTS_KEY, 0, limit - 1);
        
        return productIds.stream()
            .map(id -> Long.parseLong(id.toString()))
            .collect(Collectors.toList());
    }
    
    public void recordSearch(String keyword) {
        redisTemplate.opsForZSet().incrementScore(TOP_SEARCHES_KEY, keyword, 1);
    }
    
    public List<String> getTopSearches(int limit) {
        Set<Object> keywords = redisTemplate.opsForZSet()
            .reverseRange(TOP_SEARCHES_KEY, 0, limit - 1);
        
        return keywords.stream()
            .map(Object::toString)
            .collect(Collectors.toList());
    }
}
问题:大量请求查询不存在的key,导致请求直接打到数据库
解决方案:
@Cacheable(value = "products", key = "#id", unless = "#result == null")
public Product getProductById(Long id) {
    Product product = productRepository.findById(id).orElse(null);
    if (product == null) {
        // 缓存一个空对象,设置较短过期时间
        return new Product(); // 空对象
    }
    return product;
}
@Service
public class BloomFilterService {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    private static final String BLOOM_FILTER_KEY = "product:bloom:filter";
    
    public void initBloomFilter(List<Long> productIds) {
        for (Long id : productIds) {
            addToBloomFilter(id);
        }
    }
    
    public void addToBloomFilter(Long productId) {
        // 使用多个hash函数计算位位置
        int[] positions = getHashPositions(productId.toString());
        for (int pos : positions) {
            redisTemplate.opsForValue().setBit(BLOOM_FILTER_KEY, pos, true);
        }
    }
    
    public boolean mightContain(Long productId) {
        int[] positions = getHashPositions(productId.toString());
        for (int pos : positions) {
            if (!redisTemplate.opsForValue().getBit(BLOOM_FILTER_KEY, pos)) {
                return false;
            }
        }
        return true;
    }
    
    private int[] getHashPositions(String value) {
        // 使用多个hash函数计算位置
        // 实际实现可以使用Guava的BloomFilter或Redisson的RBloomFilter
        return new int[]{/* 计算出的位置数组 */};
    }
}
问题:大量缓存同时失效,导致所有请求直接访问数据库
解决方案:
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
    RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
        .entryTtl(Duration.ofMinutes(30 + new Random().nextInt(15))) // 30-45分钟随机
        .disableCachingNullValues();
    
    // ...
}
@Service
public class ProductService {
    
    @Cacheable(value = "products", key = "#id")
    public Product getProductById(Long id) {
        return loadFromDb(id);
    }
    
    // 后台定时任务更新缓存
    @Scheduled(fixedRate = 3600000) // 每小时更新一次
    public void refreshCache() {
        List<Product> products = productRepository.findAll();
        products.forEach(p -> updateProduct(p));
    }
}
问题:数据库更新后,缓存数据不一致
解决方案:
”`java @Transactional public Product updateProduct(Product product) { // 先更新数据库 Product updated = productRepository.save(product); // 再更新缓存 redisTemplate.ops
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。