springboot本地缓存caffeine

概述

Guava、Ehcache、Caffeine都是基于JVM堆内存的本地缓存方案, 从功能和性能角度的对比这里不再多做赘述。在实际生产环境中, 为了避免缓存雪崩, 一般会采用多级缓存策略。

Caffeine基本使用

pom.xml

1
2
3
4
5
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>2.9.0</version>
</dependency>

缓存配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* @author cuishiying
* @date 2021-01-22
*/
@Configuration
public class CacheConfig {

@Bean
public Cache<String, Object> caffeineCache() {
return Caffeine.newBuilder()
// 固定过期时间
.expireAfterWrite(60, TimeUnit.MINUTES)
// 初始缓存空间大小
.initialCapacity(100)
// 缓存最大条数
.maximumSize(1000)
.build();
}
}

应用

1
2
3
4
5
6
7
8
9
10
11
12
/**
* @author cuishiying
* @date 2021-01-22
*/
@Data
@ToString
public class User {

private Long id;
private String name;

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* @author cuishiying
* @date 2021-01-22
*/
public interface UserService {

void addUser(User user);

User getUser(Long id);

User updateUser(User user);

void deleteUser(Long id);

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
/**
* @author cuishiying
* @date 2021-01-22
*/
@Slf4j
@Service
public class UserServiceImpl implements UserService {

/**
* 缓存
* 注意: key存取需要保证类型一致, 如全部使用String.valueOf(id)处理key, 否则操作失败
*/
@Resource
private Cache<String, Object> cache;

/**
* 模拟数据库
*/
private final Map<Long, User> db = new HashMap<>();

@Override
public void addUser(User user) {
db.put(user.getId(), user);
log.info("saved to db, user={}", user);
}

@Override
public User getUser(Long id) {
// 先从缓存中读取
User user = (User) cache.getIfPresent(String.valueOf(id));
if (Objects.nonNull(user)) {
log.info("get from cache, user={}", user);
return user;
}
// 如果缓存中不存在, 则从库中查找
user = db.get(id);
log.info("get from db, user={}", user);
// 如果数据库存在, 则加入缓存
if (Objects.nonNull(user)) {
log.info("saved to cache, user={}", user);
cache.put(String.valueOf(id), user);
}
return user;
}

@Override
public User updateUser(User user) {
if (!db.containsKey(user.getId())) {
return null;
}
// 更新数据库
User oldUser = db.get(user.getId());
oldUser.setName(user.getName());
db.put(user.getId(), oldUser);

// 清除缓存
cache.invalidate(String.valueOf(user.getId()));
log.info("invalidate cache, user={}", user);

return oldUser;
}

@Override
public void deleteUser(Long id) {
// 删除数据库
db.remove(id);
// 删除缓存
cache.invalidate(String.valueOf(id));
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/**
* @author cuishiying
* @date 2021-01-22
*/
@RestController
public class UserController {

@Resource
private UserService userService;

@PostMapping("/user")
public Object createUser(@RequestBody User user) {
userService.addUser(user);
return user;
}

@GetMapping("/user/{id}")
public Object getUser(@PathVariable Long id) {
return userService.getUser(id);
}

@PutMapping("/user")
public Object updateUser(@RequestBody User user) {
return userService.updateUser(user);
}

@DeleteMapping("/user/{id}")
public Object deleteUser(@PathVariable Long id) {
userService.deleteUser(id);
return id;
}
}

配合spring-cahce使用

pom.xml

1
2
3
4
5
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
<version>2.4.2</version>
</dependency>

修改配置文件

将缓存交给spring接管

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package com.example.caffeine;

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.cache.CacheManager;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.concurrent.TimeUnit;

/**
* @author cuishiying
* @date 2021-01-22
*/
@Configuration
public class CacheConfig {

@Bean
public Cache<String, Object> caffeineCache() {
return Caffeine.newBuilder()
// 固定过期时间
.expireAfterWrite(60, TimeUnit.MINUTES)
// 初始缓存空间大小
.initialCapacity(100)
// 缓存最大条数
.maximumSize(1000)
.build();
}

@Bean("caffeineCacheManage")
public CacheManager caffeineCacheManage() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
cacheManager.setCaffeine(Caffeine.newBuilder()
// 固定过期时间
.expireAfterWrite(60, TimeUnit.MINUTES)
// 初始缓存空间大小
.initialCapacity(100)
// 缓存最大条数
.maximumSize(1000));
return cacheManager;
}
}

重写实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/**
* @author cuishiying
* @date 2021-01-22
*/
@Slf4j
@Service("caffeineCacheService")
@CacheConfig(cacheNames = "caffeineCacheManage")
public class UserServiceImpl implements UserService {

/**
* 模拟数据库
*/
private final Map<Long, User> db = new HashMap<>();

@Override
@CachePut(key = "#user.id")
public void addUser(User user) {
db.put(user.getId(), user);
log.info("saved to db, user={}", user);
}

@Override
@Cacheable(key = "#id")
public User getUser(Long id) {
return db.get(id);
}

@Override
@CacheEvict(key = "#user.id")
public User updateUser(User user) {
if (!db.containsKey(user.getId())) {
return null;
}
// 更新数据库
User oldUser = db.get(user.getId());
oldUser.setName(user.getName());
db.put(user.getId(), oldUser);

// 清除缓存
log.info("invalidate cache, user={}", user);

return oldUser;
}

@Override
@CacheEvict(key = "#id")
public void deleteUser(Long id) {
// 删除数据库
db.remove(id);
}
}

测试

1
2
@Resource(name = "caffeineCacheService")
private UserService userService;

最后

本文到此结束,感谢阅读。如果您觉得不错,请关注公众号【当我遇上你】,您的支持是我写作的最大动力。