How to use key of Spring boot redis cache

  • 2021-07-26 07:35:38
  • OfStack

In database query, we often use adding cache to improve the performance of programs. @ Cacheable can easily add cache to database query methods. This article mainly explores key used in cache under 1.

Build a project

Database


mysql> select * from t_student;
+----+--------+-------------+
| id | name  | grade_class |
+----+--------+-------------+
| 1 | Simone | 3-2     |
+----+--------+-------------+
1 row in set (0.01 sec)

spring boot Configuration


#jpa
spring.jpa.hibernate.ddl-auto=none
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/pratice
spring.datasource.username=root
spring.datasource.password=123456
#redis
spring.redis.host=localhost
spring.redis.lettuce.pool.maxActive=5
spring.redis.lettuce.pool.maxIdle=5
#cache
spring.cache.cache-names=Cache
spring.cache.redis.time-to-live=300000

Data access layer


public interface StudentDao extends CrudRepository<Student, Long> {

  @Cacheable(value = "Cache")
  @Override
  Optional<Student> findById(Long aLong);

  @Cacheable(value = "Cache")
  Optional<Student> findByName(String name);
}

Observe the method of starting and calling data access layer


@Override
public void run(ApplicationArguments args) throws Exception {
 Optional<Student> optional = studentDao.findById(1L);
 System.out.println(optional);
}

View key cached in redis when @ Cacheable (value = "Cache") is used by default


127.0.0.1:6379> keys *
1) "Cache::1"

It can be known that key is generated by cache name and parameter value, and key is generated regardless of method name. If the parameters of two methods are the same, it will hit the same cache, which is obviously impossible. Call findById and findByName with the same parameters to observe the query results


@Override
public void run(ApplicationArguments args) throws Exception {
 Optional<Student> optional = studentDao.findById(1L);
 System.out.println(optional);

 Optional<Student> optionalByName = studentDao.findByName("1");
 System.out.println(optionalByName);
}
// Output result 
//Optional[Student(id=1, name=Simone, gradeClass=3-2)]
//Optional[Student(id=1, name=Simone, gradeClass=3-2)]

From the data in the database, studentDao. findByName ("1") should be queried empty, but the fetch hit the cache, so we need to add the name of the method to the cached key.


@Cacheable(value = "Cache", key = "{#root.methodName, #aLong}")
@Override
Optional<Student> findById(Long aLong);

@Cacheable(value = "Cache", key = "{#root.methodName, #name}")
Optional<Student> findByName(String name);

//Optional[Student(id=1, name=Simone, gradeClass=3-2)] 
//Optional.empty

Key in Redis also has the name of the method


127.0.0.1:6379> keys *
1) "Cache::findById,1"
2) "Cache::findByName,1"

In a real project, we definitely don't have only one table. If other tables use the cached name Cache, it is very likely to produce the same key. For example, I also have one dao as follows


public interface TeacherDao extends CrudRepository<Teacher, Long> {

  @Cacheable(value = "Cache", key = "{#root.methodName, #aLong}")
  @Override
  Optional<Teacher> findById(Long aLong);

  @Cacheable(value = "Cache", key = "{#root.methodName, #name}")
  Optional<Teacher> findByName(String name);
}

If a method in TeacherDao hits a method in StudentDao, ClassCastException will be generated. Here are two ways to solve this problem. The first method is to use different cache names in each dao. The second is to add the class name to key.

I did google for 1 time, but I didn't find a way to use Spel or get the class name (maybe there is), so it won't work by configuring the key parameter of @ Cacheable here. Then you can only implement custom generators.


@Bean("customKeyGenerator")
public KeyGenerator keyGenerator() {
 return new KeyGenerator() {
  @Override
  public Object generate(Object o, Method method, Object... objects) {
   return method.getDeclaringClass().getName() + "_"
    + method.getName() + "_"
    + StringUtils.arrayToDelimitedString(objects, "_");
  }
 };
}

Setting the keyGenerator parameter for @ Cacheable


#jpa
spring.jpa.hibernate.ddl-auto=none
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/pratice
spring.datasource.username=root
spring.datasource.password=123456
#redis
spring.redis.host=localhost
spring.redis.lettuce.pool.maxActive=5
spring.redis.lettuce.pool.maxIdle=5
#cache
spring.cache.cache-names=Cache
spring.cache.redis.time-to-live=300000
0

View key in Redis


#jpa
spring.jpa.hibernate.ddl-auto=none
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/pratice
spring.datasource.username=root
spring.datasource.password=123456
#redis
spring.redis.host=localhost
spring.redis.lettuce.pool.maxActive=5
spring.redis.lettuce.pool.maxIdle=5
#cache
spring.cache.cache-names=Cache
spring.cache.redis.time-to-live=300000
1

Key consists of cache name, class name, method name and parameters, which is safe enough. In actual development, key can be constructed according to the actual situation to meet the needs.


Related articles: