-
Notifications
You must be signed in to change notification settings - Fork 0
Caching Strategies
Caching refers to the process of storing frequently used data so that those data can be served much faster for any future requests. So we take the most frequently used data and copy it into temporary storage so that it can be accessed much faster in future calls from the client. Caching significantly improves the performance of an application, reducing the complexity to generate content.
- Cache Aside
- Write-Through Cache
- Read-Through Cache
- Write-Back
In this caching strategy, the cache is logically placed at the side and the application directly communicates with the cache and the database to know if the requested information is present or not.
The followings are the characteristics of the pattern.
- When an application needs data, first it looks in the cache.
- If the data available in the cache, then application will use the data from the cache, otherwise data is retrieved from the database and the cache entry will be updated.
we have to add a dependency to the cache starter
implementation 'org.springframework.boot:spring-boot-starter-cache'
-
@EnableCaching
We can enable the caching feature simply by adding the @EnableCaching annotation to any of the configuration classes:
@EnableCaching public class CachingConfig { @Bean public CacheManager cacheManager() { return new ConcurrentMapCacheManager("addresses"); } }
-
@Cacheable
we can provide a cache name by using the value or cacheNames attribute.
@Cacheable(value = "cacheAside",key = "#id") public Optional<CacheAsideEntity> findById(Long id) { return cacheAsideEntityRepository.findById(id); }
-
@CachePut
It is used when we want to update the cache without interfering the method execution. It means the method will always execute, and its result will be placed into the cache. It supports the attributes of @Cacheable annotation.
@CachePut(value = "cacheAside",key = "#cacheAside.id") public CacheAsideEntity save(CacheAsideDTO sampleEntityDTO) { CacheAsideEntity cacheAsideEntity = cacheAsideEntityMapper.dtoToEntity(sampleEntityDTO); return cacheAsideEntityRepository.save(sampleEntity); }
-
@CacheEvict
We can use the @CacheEvict annotation to indicate the removal of one or more/all values so that fresh values can be loaded into the cache again:
@CacheEvict(value = "sampleEntity",allEntries = false,key = "#id") public boolean deleteById(Long id) { sampleEntityRepository.deleteById(id); return true; }
CacheAside.mp4
Write-through caching is a caching strategy in which the cache and database are updated almost simultaneously. When we want to update the information in the cache, we first update the cache itself, and then propagate the same update to the underlying database.
Redisson includes functionality for write-through caching in Redis by using the RMap interface.
@Bean
public RMapCache<Long, SampleEntity> userRMapCache() {
final RMapCache<Long, SampleEntity> userRMapCache = redissonClient.getMapCache(CACHE_NAME,MapOptions.<Long, SampleEntity>defaults()
.writer(getMapWriter()).loader(getMapLoader())
.writeMode(MapOptions.WriteMode.WRITE_THROUGH));
return userRMapCache;
}
When the Map entry is being updated method won't return until Redisson update it in an external storage using MapWriter object. Code example:
private MapWriter<Long, SampleEntity> getMapWriter() {
return new MapWriter<Long, SampleEntity>() {
@Override
public void write(final Map<Long, SampleEntity> map) {
logger.info("*********************** write");
map.forEach( (k, v) -> {
WriteThroughCacheEntityDTO sampleEntityDTO=new WriteThroughCacheEntityDTO();
sampleEntityDTO.setId(v.getId());
sampleEntityDTO.setAge(v.getAge());
sampleEntityDTO.setName(v.getName());
sampleEntityDTO.setPassword(v.getPassword());
sampleEntityDTO.setPhone(v.getPhone());
userJPAAdapter.save(sampleEntityDTO);
});
}
@Override
public void delete(Collection<Long> keys) {
logger.info("*********************** delete");
keys.stream().forEach(e -> {
userJPAAdapter.deleteById(e);
});
}
};
}
writethrough.mp4
Read-through cache sits in-line with the database. When there is a cache miss, it loads missing data from database, populates the cache and returns it to the application. Read-through caches work best for read-heavy workloads when the same data is requested many times.
If requested entry doesn't exist in the Redisson Map object when it will be loaded using provided MapLoader object. Code example:
private MapLoader<Long, ReadThroughEntity> getMapLoader(){
return new MapLoader<Long, ReadThroughEntity>() {
@Override
public ReadThroughEntity load(Long key) {
logger.info("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ReadThroughEntity load(Long key)");
return sampleEntity2JpaAdapter.findById(key).get();
}
@Override
public Iterable<Long> loadAllKeys() {
logger.info("############################## Itrerable<Long> loadAllKeys()");
List<Long> list = new ArrayList<Long>() {
/**
*
*/
private static final long serialVersionUID = 1L;};
List<ReadThroughEntity> list1 = sampleEntity2JpaAdapter.findAll();
for(ReadThroughEntity sample : list1) {
list.add(sample.getId());
}
return list;
}
};
}