How to use Redis caching less gracefully
We all know to use redis
to cache our data collection, as shown in the figure below.
Usually cache the data by yourself. The advantage of this is that the logic is clear, and redis
the key
sum value
will be more standardized. But there will be more redundant codes, and you need to judge whether the data is out of date.
In order to simplify the business code, the redis
second-level cache is now integrated with annotations , but his key
sum value
will be less in compliance with the specification. His key
altogether contains 5 parts, the most important is the parameter sql
with this sql
. His value
is the result set of this query.
Ready to work
Introduce dependencies,mybatis
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
<exclusions>
<!-- 排除 tomcat-jdbc 以使用 HikariCP -->
<exclusion>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jdbc</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.0.6</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-extension</artifactId>
<version>3.0.6</version>
</dependency>
redis
rely
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
Configuration file
spring:
datasource:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
# 这里使用的是 ip:3336/db_order 的数据库(一个服务一个数据库)
url: jdbc:mysql://localhost:3306/sys-common?useUnicode=true&characterEncoding=utf-8&serverTimezone=Hongkong&useSSL=false
username: root
password: 123456
hikari:
minimum-idle: 5
idle-timeout: 600000
maximum-pool-size: 10
auto-commit: true
pool-name: MyHikariCP
max-lifetime: 1800000
connection-timeout: 30000
connection-test-query: SELECT 1
# redis
redis:
host: xxx.xxx.xxx.xxx
port: 6379
lettuce:
pool:
max-active: 8
max-idle: 8
max-wait: -1ms
min-idle: 0
database: 4
ConfigurationredisTemplate
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* ProjectName: hello-redis-cache
* Package: com.laoshiren.hello.redis.cache.mybatis.configure
* ClassName: RedisConfiguration
* Author: laoshiren
* Description:
* Date: 2020/9/13 15:49
* Version: 1.0
*/
@Configuration
public class RedisConfiguration {
/**
* 设置redisTemplate
*/
@Bean(name = "redisTemplate")
public RedisTemplate<Object, Object> redisTemplate(LettuceConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
// 使用String 序列化
// redisTemplate.setDefaultSerializer(new StringRedisSerializer());
// redisTemplate.setKeySerializer(new StringRedisSerializer());
// redisTemplate.setValueSerializer(new StringRedisSerializer());
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
Note that String
the serialization method I do not use here is deserialization Key
andValue
achieve
Implement Cache
interface
package com.laoshiren.hello.redis.cache.mybatis.cache;
import com.laoshiren.hello.redis.cache.mybatis.configure.ApplicationContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.cache.Cache;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* ProjectName: hello-redis-cache
* Package: com.laoshiren.hello.redis.cache.mybatis.cache
* ClassName: RedisCache
* Author: laoshiren
* Description:
* Date: 2020/9/13 15:34
* Version: 1.0
*/
@Slf4j
public class RedisCache implements Cache {
private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private final String id; // cache instance id
private RedisTemplate redisTemplate;
private static final long EXPIRE_TIME_IN_MINUTES = 30; // redis过期时间
public RedisCache(String id) {
if (id == null) {
throw new IllegalArgumentException("Cache instances require an ID");
}
this.id = id;
}
@Override
public String getId() {
return id;
}
/**
* Put query result to redis
*
* @param key
* @param value
*/
@Override
public void putObject(Object key, Object value) {
try {
RedisTemplate redisTemplate = getRedisTemplate();
redisTemplate.opsForValue().set(key, value, EXPIRE_TIME_IN_MINUTES, TimeUnit.MINUTES);
log.debug("Put query result to redis");
} catch (Throwable t) {
log.error("Redis put failed", t);
}
}
/**
* Get cached query result from redis
*
* @param key
* @return
*/
@Override
public Object getObject(Object key) {
try {
RedisTemplate redisTemplate = getRedisTemplate();
log.info("Get cached query result from redis");
// System.out.println("****" + opsForValue.get(key).toString());
return redisTemplate.opsForValue().get(key);
} catch (Throwable t) {
log.error("Redis get failed, fail over to db", t);
return null;
}
}
/**
* Remove cached query result from redis
*
* @param key
* @return
*/
@Override
@SuppressWarnings("unchecked")
public Object removeObject(Object key) {
try {
RedisTemplate redisTemplate = getRedisTemplate();
redisTemplate.delete( key.toString());
log.debug("Remove cached query result from redis");
} catch (Throwable t) {
log.error("Redis remove failed", t);
}
return null;
}
/**
* Clears this cache instance
*/
@Override
public void clear() {
RedisTemplate redisTemplate = getRedisTemplate();
redisTemplate.execute((RedisCallback) connection -> {
connection.flushDb();
return null;
});
log.debug("Clear all the cached query result from redis");
}
/**
* This method is not used
*
* @return
*/
@Override
public int getSize() {
return 0;
}
@Override
public ReadWriteLock getReadWriteLock() {
return readWriteLock;
}
private RedisTemplate getRedisTemplate() {
if (redisTemplate == null) {
redisTemplate = ApplicationContextHolder.getBean("redisTemplate");
}
return redisTemplate;
}
}
mapper
Cache the specified configuration
@CacheNamespace(implementation = RedisCache.class)
public interface TbPostMapper extends BaseMapper<TbPost> {
}
test
Request a database once, use the Debug
mode, it key
is one CacheKey
, and cannot use StringRedisSerializer
deserialization, so you have redisTemplate
to use the default serialization, namely JdkSerializationRedisSerializer
Open it RDM
and take a look at library number 4.
Finding the key
sum value
is not very beautiful, but it does not affect the use. Of course, you can use it StringRedisSerializer
to achieve it, but sql
there will be a little problem when I get the parameters in the process of trying . Hope someone can point it out.
Sql with parameters
Pay special attention ! In the paging cache, the Page
object total
must be manually queried once, otherwise the object returned to the front end will have the total number of pages for the first time, and the second time the cache will not carry this total
, so it must be queried manually .
@GetMapping("page/{pageNo}/{pageSize}")
public ResponseResult<IPage<Area>> page(@PathVariable(name = "pageNo")Integer pageNo,
@PathVariable(name = "pageSize") Integer pageSize,
HttpServletRequest request){
IPage<Area> wherePage = new Page<>(pageNo, pageSize);
String word = request.getParameter("wd");
LambdaQueryWrapper<Area> queryWrapper = new LambdaQueryWrapper<>();
if (StringUtils.isNotBlank(word)) {
queryWrapper.like(Area::getAreaName,word);
}
int count = areaService.count(queryWrapper);
IPage<Area> page = areaService.page(wherePage,queryWrapper);
page.setTotal((long)count);
return new ResponseResult<>(CodeStatus.OK,"操作成功",page);
}
I will continue to update this blog in the future. Well, in the end, I would like to borrow a saying from the big man: "Without a bit of cold, how can you know that the plum blossoms are so sweet".