当前位置:网站首页>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】
RedisMemCacheRedisGuava cacheCaffeine
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
RedisData interaction between remote caches of classes , Reduce network I/O expenses , Reduce the time-consuming of network communication in this process
preparation
CaffeineRedis<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.ymlRedisspring:
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
RedisTemplateredisRedisTemplateConnectionFactoryV1.0 edition
CaffeineCacheMapkeyvalueCache@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();
}
}
CacheinitialCapacity: 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
expireAfterAccessrefreshAfterWriteCacheService@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);
}
}
OrderServicepublic 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;
}
CachegetCaffeineRedisRedisRedisCaffeinegetCaffeineRedis
RedisCaffeine
RedisCaffeineRedisRedisCaffeinepublic 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);
}

ReidsCaffeinepublic 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
springCacheManager@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 .
CacheBeanspringCacheManager@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;
}
}
@EnableCachingCaffeineService@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;
}
@CacheablevaluecacheNamesCachecacheNameCachecacheNameCachevaluecacheNamesCachekeykeySpringELkey# Parameter name
# Parameter object . Property name
#p The parameter corresponds to the subscript
@CacheableRedisCaffeine@CachePutCache@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;
}
voidkeyRedis@CacheEvictRedis@CacheEvict(cacheNames = "order",key = "#id")
public void deleteOrder(Long id) {
log.info("delete order");
orderMapper.deleteById(id);
redisTemplate.delete(CacheConstant.ORDER + id);
}
springCacheManagerCachespringRedisV3.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 + keykeyCacheCacheNamel2TimeOutRedistypepublic enum CacheType {
FULL, // access
PUT, // Only exist
DELETE // Delete
}
keyspringElpublic 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;
}
elStringkeymappublic 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
CacheCacheCaffeineRedisTemplateRedis@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
keyOfspringElexpression , 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);
}
Servicesummary
Manongshen 边栏推荐
- PyCharm 中出现Cannot find reference ‘imread‘ in ‘__init__.py‘
- 拖动拖动拖动
- leetcode_191_2021-10-15
- 降低pip到指定版本(通过PyCharm升级pip,在降低到原来版本)
- Datakit 代理实现局域网数据统一汇聚
- EasyBypass
- [product design and R & D collaboration tool] Shanghai daoning provides you with blue lake introduction, download, trial and tutorial
- SAP接口debug设置外部断点
- 性能测试工具wrk安装使用详解
- TKKC round#3
猜你喜欢

【OpenCV 例程200篇】209. HSV 颜色空间的彩色图像分割

应用实践 | 海量数据,秒级分析!Flink+Doris 构建实时数仓方案
![在每个树行中找最大值[分层遍历之一的扩展]](/img/5b/81ff20b61c0719ceb6873e44878859.png)
在每个树行中找最大值[分层遍历之一的扩展]

好想送对象一束花呀

The collection of zero code enterprise application cases in various industries was officially released

Blender FAQs
![[theory] deep learning in the covid-19 epic: a deep model for urban traffic revitalization index](/img/d9/f5461a81a343f3c3ca01876e423fa2.png)
[theory] deep learning in the covid-19 epic: a deep model for urban traffic revitalization index

Li Kou daily question - day 26 -496 Next larger element I

心楼:华为运动健康的七年筑造之旅

Introduce the overall process of bootloader, PM, kernel and system startup
随机推荐
Direct attack on "three summers" production: good harvest news spreads frequently and summer broadcasting is in full swing
cv2导包时报Could not find a version that satisfies the requirement cv2 (from versions: none)
Understanding openstack network
LeetCode-513. 找树左下角的值
How to resolve the 35 year old crisis? Sharing of 20 years' technical experience of chief architect of Huawei cloud database
openGauss内核:简单查询的执行
Installing Oracle without graphical interface in virtual machine centos7 (nanny level installation)
在每个树行中找最大值[分层遍历之一的扩展]
suspense组件和异步组件
Graduation summary of phase 6 of the construction practice camp
基于kruskal的最小生成树
03---增反膜
多线程收尾
leetcode_ 191_ 2021-10-15
You are using pip version 21.1.2; however, version 22.1.2 is available
02--- impossible phenomenon of longitudinal wave
CV2 package guide times could not find a version that satisfies the requirement CV2 (from versions: none)
Network layer & IP
leetcode:1504. 统计全 1 子矩形的个数
leetcode1720_2021-10-14