当前位置:网站首页>Redis+caffeine two-level cache enables smooth access speed
Redis+caffeine two-level cache enables smooth access speed
2022-06-24 21:56:00 【InfoQ】
Redis
MemCache
Redis
Guava cache
Caffeine

Advantages and problems
- Local cache is based on the memory of the local environment , Very fast access , For some changes, the frequency is low 、 Data with low real-time requirements , Can be put in the local cache , Improve access speed
- Using local caching can reduce and
Redis
Data interaction between remote caches of classes , Reduce network I/O expenses , Reduce the time-consuming of network communication in this process
preparation
Caffeine
Redis
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.8.1</version>
</dependency>
application.yml
Redis
spring:
redis:
host: 127.0.0.1
port: 6379
database: 0
timeout: 10000ms
lettuce:
pool:
max-active: 8
max-wait: -1ms
max-idle: 8
min-idle: 0
RedisTemplate
redis
RedisTemplate
ConnectionFactory
V1.0 edition
Caffeine
Cache
Map
key
value
Cache
@Configuration
public class CaffeineConfig {
@Bean
public Cache<String,Object> caffeineCache(){
return Caffeine.newBuilder()
.initialCapacity(128)// Initial size
.maximumSize(1024)// The largest number
.expireAfterWrite(60, TimeUnit.SECONDS)// Expiration time
.build();
}
}
Cache
initialCapacity
: Initial cache empty size
maximumSize
: The maximum number of caches , Setting this value can avoid memory overflow
expireAfterWrite
: Specify the expiration time of the cache , Is the time after the last write operation , here
expireAfterAccess
refreshAfterWrite
Cache
Service
@Service
@AllArgsConstructor
public class OrderServiceImpl implements OrderService {
private final OrderMapper orderMapper;
@Override
public Order getOrderById(Long id) {
Order order = orderMapper.selectOne(new LambdaQueryWrapper<Order>()
.eq(Order::getId, id));
return order;
}
@Override
public void updateOrder(Order order) {
orderMapper.updateById(order);
}
@Override
public void deleteOrder(Long id) {
orderMapper.deleteById(id);
}
}
OrderService
public Order getOrderById(Long id) {
String key = CacheConstant.ORDER + id;
Order order = (Order) cache.get(key,
k -> {
// First query Redis
Object obj = redisTemplate.opsForValue().get(k);
if (Objects.nonNull(obj)) {
log.info("get data from redis");
return obj;
}
// Redis If not, query DB
log.info("get data from database");
Order myOrder = orderMapper.selectOne(new LambdaQueryWrapper<Order>()
.eq(Order::getId, id));
redisTemplate.opsForValue().set(k, myOrder, 120, TimeUnit.SECONDS);
return myOrder;
});
return order;
}
Cache
get
Caffeine
Redis
Redis
Redis
Caffeine
get
Caffeine
Redis

Redis
Caffeine

Redis
Caffeine
Redis
Redis
Caffeine
public void updateOrder(Order order) {
log.info("update order data");
String key=CacheConstant.ORDER + order.getId();
orderMapper.updateById(order);
// modify Redis
redisTemplate.opsForValue().set(key,order,120, TimeUnit.SECONDS);
// Modify local cache
cache.put(key,order);
}

Reids
Caffeine
public void deleteOrder(Long id) {
log.info("delete order");
orderMapper.deleteById(id);
String key= CacheConstant.ORDER + id;
redisTemplate.delete(key);
cache.invalidate(key);
}

V2.0 edition
spring
CacheManager
@Cacheable
: Take value from cache according to key , If the cache exists , After the cache is successfully obtained , Directly return the cached results . If the cache does not exist , Then the execution method , And put the results in the cache .
@CachePut
: Regardless of whether the cache corresponding to the previous key exists , All execution methods , And force the results into the cache
@CacheEvict
: After executing the method , Will remove the data from the cache .
Cache
Bean
spring
CacheManager
@Configuration
public class CacheManagerConfig {
@Bean
public CacheManager cacheManager(){
CaffeineCacheManager cacheManager=new CaffeineCacheManager();
cacheManager.setCaffeine(Caffeine.newBuilder()
.initialCapacity(128)
.maximumSize(1024)
.expireAfterWrite(60, TimeUnit.SECONDS));
return cacheManager;
}
}
@EnableCaching
Caffeine
Service
@Cacheable
@Cacheable(value = "order",key = "#id")
//@Cacheable(cacheNames = "order",key = "#p0")
public Order getOrderById(Long id) {
String key= CacheConstant.ORDER + id;
// First query Redis
Object obj = redisTemplate.opsForValue().get(key);
if (Objects.nonNull(obj)){
log.info("get data from redis");
return (Order) obj;
}
// Redis If not, query DB
log.info("get data from database");
Order myOrder = orderMapper.selectOne(new LambdaQueryWrapper<Order>()
.eq(Order::getId, id));
redisTemplate.opsForValue().set(key,myOrder,120, TimeUnit.SECONDS);
return myOrder;
}
@Cacheable
value
cacheNames
Cache
cacheName
Cache
cacheName
Cache
value
cacheNames
Cache
key
key
SpringEL
key
# Parameter name
# Parameter object . Property name
#p The parameter corresponds to the subscript
@Cacheable
Redis
Caffeine
@CachePut
Cache
@CachePut(cacheNames = "order",key = "#order.id")
public Order updateOrder(Order order) {
log.info("update order data");
orderMapper.updateById(order);
// modify Redis
redisTemplate.opsForValue().set(CacheConstant.ORDER + order.getId(),
order, 120, TimeUnit.SECONDS);
return order;
}
void
key
Redis
@CacheEvict
Redis
@CacheEvict(cacheNames = "order",key = "#id")
public void deleteOrder(Long id) {
log.info("delete order");
orderMapper.deleteById(id);
redisTemplate.delete(CacheConstant.ORDER + id);
}
spring
CacheManager
Cache
spring
Redis
V3.0 edition
spring
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DoubleCache {
String cacheName();
String key(); // Support springEl expression
long l2TimeOut() default 120;
CacheType type() default CacheType.FULL;
}
cacheName + key
key
Cache
CacheName
l2TimeOut
Redis
type
public enum CacheType {
FULL, // access
PUT, // Only exist
DELETE // Delete
}
key
springEl
public static String parse(String elString, TreeMap<String,Object> map){
elString=String.format("#{%s}",elString);
// Create an expression parser
ExpressionParser parser = new SpelExpressionParser();
// adopt evaluationContext.setVariable Variables can be set in context .
EvaluationContext context = new StandardEvaluationContext();
map.entrySet().forEach(entry->
context.setVariable(entry.getKey(),entry.getValue())
);
// Analytic expression
Expression expression = parser.parseExpression(elString, new TemplateParserContext());
// Use Expression.getValue() Get the value of the expression , Here comes Evaluation Context
String value = expression.getValue(context, String.class);
return value;
}
elString
key
map
public void test() {
String elString="#order.money";
String elString2="#user";
String elString3="#p0";
TreeMap<String,Object> map=new TreeMap<>();
Order order = new Order();
order.setId(111L);
order.setMoney(123D);
map.put("order",order);
map.put("user","Hydra");
String val = parse(elString, map);
String val2 = parse(elString2, map);
String val3 = parse(elString3, map);
System.out.println(val);
System.out.println(val2);
System.out.println(val3);
}
123.0
Hydra
null
Cache
Cache
Caffeine
RedisTemplate
Redis
@Slf4j @Component @Aspect
@AllArgsConstructor
public class CacheAspect {
private final Cache cache;
private final RedisTemplate redisTemplate;
@Pointcut("@annotation(com.cn.dc.annotation.DoubleCache)")
public void cacheAspect() {
}
@Around("cacheAspect()")
public Object doAround(ProceedingJoinPoint point) throws Throwable {
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
// Splicing analysis springEl Of expression map
String[] paramNames = signature.getParameterNames();
Object[] args = point.getArgs();
TreeMap<String, Object> treeMap = new TreeMap<>();
for (int i = 0; i < paramNames.length; i++) {
treeMap.put(paramNames[i],args[i]);
}
DoubleCache annotation = method.getAnnotation(DoubleCache.class);
String elResult = ElParser.parse(annotation.key(), treeMap);
String realKey = annotation.cacheName() + CacheConstant.COLON + elResult;
// Force update
if (annotation.type()== CacheType.PUT){
Object object = point.proceed();
redisTemplate.opsForValue().set(realKey, object,annotation.l2TimeOut(), TimeUnit.SECONDS);
cache.put(realKey, object);
return object;
}
// Delete
else if (annotation.type()== CacheType.DELETE){
redisTemplate.delete(realKey);
cache.invalidate(realKey);
return point.proceed();
}
// Reading and writing , Inquire about Caffeine
Object caffeineCache = cache.getIfPresent(realKey);
if (Objects.nonNull(caffeineCache)) {
log.info("get data from caffeine");
return caffeineCache;
}
// Inquire about Redis
Object redisCache = redisTemplate.opsForValue().get(realKey);
if (Objects.nonNull(redisCache)) {
log.info("get data from redis");
cache.put(realKey, redisCache);
return redisCache;
}
log.info("get data from database");
Object object = point.proceed();
if (Objects.nonNull(object)){
// write in Redis
redisTemplate.opsForValue().set(realKey, object,annotation.l2TimeOut(), TimeUnit.SECONDS);
// write in Caffeine
cache.put(realKey, object);
}
return object;
}
}
- Through the parameters of the method , Parsing comments
key
OfspringEl
expression , Assemble the real cachekey
- Depending on the type of operation cache , Handle access separately 、 Only exist 、 Delete cache operation
- Delete and force update of cache , All need to execute the original method , And perform corresponding cache deletion or update operations
- Before access , First check if there is data in the cache , Direct return if any , If not, execute the original method , And cache the results
Service
@DoubleCache(cacheName = "order", key = "#id",
type = CacheType.FULL)
public Order getOrderById(Long id) {
Order myOrder = orderMapper.selectOne(new LambdaQueryWrapper<Order>()
.eq(Order::getId, id));
return myOrder;
}
@DoubleCache(cacheName = "order",key = "#order.id",
type = CacheType.PUT)
public Order updateOrder(Order order) {
orderMapper.updateById(order);
return order;
}
@DoubleCache(cacheName = "order",key = "#id",
type = CacheType.DELETE)
public void deleteOrder(Long id) {
orderMapper.deleteById(id);
}
Service
summary
Manongshen
边栏推荐
- (待补充)GAMES101作业7提高-实现微表面模型你需要了解的知识
- How to resolve the 35 year old crisis? Sharing of 20 years' technical experience of chief architect of Huawei cloud database
- 心楼:华为运动健康的七年筑造之旅
- Slider controls the playback progress of animator animation
- 机器学习:线性回归
- 基于kruskal的最小生成树
- 面试官:你说你精通Redis,你看过持久化的配置吗?
- 使用region折叠代码
- openGauss内核:简单查询的执行
- Why are life science enterprises on the cloud in succession?
猜你喜欢
[camera Foundation (I)] working principle and overall structure of camera
[featured] how do you design unified login with multiple accounts?
Antdb database online training has started! More flexible, professional and rich
Réduire le PIP à la version spécifiée (mettre à jour le PIP avec pycharm avant de le réduire à la version originale)
Multi task model of recommended model: esmm, MMOE
A deep learning model for urban traffic flow prediction with traffic events mined from twitter
[notes of Wu Enda] multivariable linear regression
Graduation design of phase 6 of the construction practice camp
Datakit agent realizes unified data aggregation in LAN
虚拟机CentOS7中无图形界面安装Oracle(保姆级安装)
随机推荐
TypeScript快速入门
【论】Deep learning in the COVID-19 epidemic: A deep model for urban traffic revitalization index
Application practice | massive data, second level analysis! Flink+doris build a real-time data warehouse scheme
leetcode_1365
建木持续集成平台v2.5.0发布
Mysql 通过表明获取字段以及注释
WMI and PowerShell get TCP connection list
面试官:你说你精通Redis,你看过持久化的配置吗?
[notes of Wu Enda] convolutional neural network
[camera Foundation (I)] working principle and overall structure of camera
The most important thing at present
好想送对象一束花呀
[notes of wuenda] fundamentals of machine learning
Transport layer UDP & TCP
将二维数组方阵顺时针旋转90°
PyCharm 中出现Cannot find reference ‘imread‘ in ‘__init__.py‘
2022 international women engineers' Day: Dyson design award shows women's design strength
Data link layer & some other protocols or technologies
You are using pip version 21.1.2; however, version 22.1.2 is available
Multithreaded finalization