Technology Sharing

SpringBoot implements Read Through mode

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

Introduction

Read Through mode generally refers to a caching strategy in which when an application attempts to read data, the cache system is first checked to see if the data already exists in the cache. If the data exists in the cache (i.e., a cache hit), the data is read directly from the cache and returned to the application. If the data does not exist in the cache (i.e., a cache miss), the data is read from the underlying data store (such as a database), then loaded into the cache, and finally returned to the application.

The main advantages of this model include:

  1. Improve performance: By reducing the number of direct accesses to the underlying storage, the performance of data retrieval can be significantly improved.
  2. Reduce latency: Cache is usually located in memory and can be accessed much faster than disk storage, thus reducing latency in data retrieval.
  3. Reduce database load: By storing frequently accessed data in the cache, the query pressure on the database can be reduced, thereby improving the throughput of the entire system.

The Read Through pattern is often contrasted with strategies such as Lazy Loading and Eager Loading:

  • Lazy Loading: Data is loaded only when needed, which can reduce unnecessary data loading but may increase the latency of the first access.
  • Eager Loading: Preload data, which can reduce latency on first access but may increase your app's memory usage and startup time.

When implementing the Read Through mode, you may need to consider the following aspects:

  • Cache Invalidation Strategy: Determines when to remove data from the cache, such as based on time (TTL) or based on space (when the cache reaches a certain size).
  • Concurrency Control: Handles situations where multiple application instances access and modify the cache simultaneously.
  • Data consistency: Ensure that the data in the cache is consistent with the data in the underlying storage, especially when the data is updated.

accomplish

Implementing the Read Through pattern in Spring Boot can usually be done through the Spring Cache abstraction. Spring Cache provides a unified API across different cache implementations and supports multiple cache solutions such as EhCache, Hazelcast, Infinispan, Redis, etc.

  1. Add dependencies: First, you need to add Spring Boot's cache dependency and the selected cache implementation library (such as Redis)

    1. <!-- Spring Boot Starter Cache -->
    2. <dependency>
    3. <groupId>org.springframework.boot</groupId>
    4. <artifactId>spring-boot-starter-cache</artifactId>
    5. </dependency>
    6. <!-- 以Redis为例,添加Redis的Spring Boot Starter -->
    7. <dependency>
    8. <groupId>org.springframework.boot</groupId>
    9. <artifactId>spring-boot-starter-data-redis</artifactId>
    10. </dependency>
  2. Enable caching annotations: Add to Spring Boot's configuration class@EnableCachingAnnotation to enable cache annotation support.

  3. Configuring the Cache Manager: Configure one or moreCacheManager, Spring Boot will automatically configure a simpleCacheManager, but you can configure more complex caching strategies as needed.

    1. import org.springframework.context.annotation.Bean;
    2. import org.springframework.context.annotation.Configuration;
    3. import org.springframework.data.redis.cache.RedisCacheConfiguration;
    4. import org.springframework.data.redis.cache.RedisCacheManager;
    5. import org.springframework.data.redis.connection.RedisConnectionFactory;
    6. @Configuration
    7. public class RedisCacheConfig {
    8. @Bean
    9. public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
    10. RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
    11. .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.string()))
    12. .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(GenericJackson2JsonRedisSerializer.json())))
    13. Map<String, RedisCacheConfiguration> customCacheConfigs = new HashMap<>();
    14. customCacheConfigs.put("mySpecialCache",
    15. config.entryTtl(Duration.ofMinutes(15))); // 为特定缓存设置不同的过期时间
    16. .disableCachingNullValues();
    17. return RedisCacheManager.builder(connectionFactory)
    18. .cacheDefaults(config)
    19. // 在这里可以自定义添加缓存配置
    20. .withInitialCacheConfigurations(customCacheConfigs)
    21. .build();
    22. }
    23. }
  4. Using Cache Annotations: Used on methods that require caching@CacheableAnnotation to implement Read Through mode. If there is no data in the cache, the method will be called and the result will be cached.
    1. import org.springframework.cache.annotation.Cacheable;
    2. import org.springframework.stereotype.Service;
    3. @Service
    4. public class MyService {
    5. @Cacheable(value = "myCache", key = "#id")
    6. public MyData getDataById(String id) {
    7. // 从数据库加载数据
    8. return myDataRepository.findById(id);
    9. }
    10. }
  5. Cache key value:exist@CacheableThe cache key is specified in the annotation, which is usually based on the value of the method parameter.

  6. Cache Name: Specifies the cache name, which will be used to distinguish different cache domains.

  7. Configuring cache parameters: You can configure the cache timeout, conditions, unless conditions, etc. as needed

    1. value or cacheNames: Specify cache name. You can specify one or more cache names, which will be used to store the cache.

      @Cacheable(value = "myCacheName", key = "#id")
    2. key: Defines the generation strategy of cache key values. Usually SpEL expressions (Spring Expression Language) are used to specify method parameters as cache keys.

      @Cacheable(cacheNames = "myCache", key = "#id")
    3. condition: Define the conditions for caching, and cache will only be performed when the conditions are met.

      @Cacheable(cacheNames = "myCache", key = "#id", condition = "#id.length() > 3")
    4. unless: Defines the conditions for not caching, andconditionInstead, it is used to exclude certain situations.

      @Cacheable(cacheNames = "myCache", key = "#id", unless = "#result == null")
    5. keyGenerator: Specify a custom cache key generation strategy. If you need more complex key generation logic, you can specify aKeyGeneratorThe bean name.

      @Cacheable(cacheNames = "myCache", keyGenerator = "myKeyGenerator")
    6. cacheManager: Specify whichCacheManager, if there are multipleCacheManagerUse when.

      @Cacheable(cacheNames = "myCache", cacheManager = "myCacheManager")
    7. expireAfterWrite: Set the expiration time after the cache item is written (in milliseconds). This is a common configuration used to define the lifetime of cached data.

      @Cacheable(cacheNames = "myCache", key = "#id", expireAfterWrite = 3600000) // 1小时后过期
    8. expireAfterAccess: Set the expiration time of the cache item after the last access, which is applicable to how long after the cached data is last accessed to expire.

    9. refreshAfterWrite: Set how long to refresh the cache after writing. It is suitable for scenarios where the cache is refreshed dynamically.

    10. sync: Set whether to create cache items synchronously to prevent race conditions in concurrent environments.

  8. Exception handling: Make sure to handle exceptions that may be thrown in cache methods to avoid affecting the stability of the application.