温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

SpringBoot如何使用Redis缓存

发布时间:2021-07-06 10:44:14 来源:亿速云 阅读:263 作者:chen 栏目:大数据
# 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运行Redis

docker run --name my-redis -p 6379:6379 -d redis 

2.2 SpringBoot项目创建

使用Spring Initializr创建项目,添加以下依赖:

  • Spring Web
  • Spring Data Redis
  • Lombok(可选)

或手动添加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> 

2.3 基本配置

在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 

3. 基本缓存操作

3.1 启用缓存支持

在主应用类上添加@EnableCaching注解:

@SpringBootApplication @EnableCaching public class RedisCacheApplication { public static void main(String[] args) { SpringApplication.run(RedisCacheApplication.class, args); } } 

3.2 缓存注解使用

@Cacheable

@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

@CachePut(value = "products", key = "#product.id") public Product updateProduct(Product product) { // 更新数据库逻辑 System.out.println("Updating product in database..."); return product; } 

@CacheEvict

@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

@Caching( cacheable = { @Cacheable(value = "products", key = "#name") }, put = { @CachePut(value = "products", key = "#result.id") } ) public Product getProductByName(String name) { // 查询逻辑 return productRepository.findByName(name); } 

3.3 自定义Key生成器

@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) { // ... } 

4. 高级缓存配置

4.1 自定义RedisTemplate

@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; } } 

4.2 自定义缓存管理器

@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(); } } 

4.3 多级缓存策略

实现本地缓存(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接口方法... } 

5. 实战应用场景

5.1 商品信息缓存

@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); } } 

5.2 用户会话管理

@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); } } 

5.3 热点数据缓存

@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()); } } 

6. 性能优化与最佳实践

6.1 缓存穿透解决方案

问题:大量请求查询不存在的key,导致请求直接打到数据库

解决方案

  1. 缓存空对象
@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; } 
  1. 布隆过滤器
@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[]{/* 计算出的位置数组 */}; } } 

6.2 缓存雪崩解决方案

问题:大量缓存同时失效,导致所有请求直接访问数据库

解决方案

  1. 设置不同的过期时间
@Bean public CacheManager cacheManager(RedisConnectionFactory factory) { RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig() .entryTtl(Duration.ofMinutes(30 + new Random().nextInt(15))) // 30-45分钟随机 .disableCachingNullValues(); // ... } 
  1. 永不过期+后台更新
@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)); } } 

6.3 缓存一致性保障

问题:数据库更新后,缓存数据不一致

解决方案

  1. 双写模式

”`java @Transactional public Product updateProduct(Product product) { // 先更新数据库 Product updated = productRepository.save(product); // 再更新缓存 redisTemplate.ops

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI