分布式缓存
# 分布式缓存
NGINX本地缓存——>redis缓存——>Tomcat进程缓存——>数据库
# 1.本地缓存-caffeine
JVM进程缓存
- 优点:读取本地(Tomcat服务器)内存,没有网络开销,速度快
- 缺点:本地进程缓存容量上限为JVM设定的堆内存最大值,无法共享。
Cache<String, String> cache = Caffeine.newBuilder().build();
// 存数据
cache.put("name", "张三");
// 取数据,如果不存在返回空
String name = cache.getIfPresent("name");
// 取数据,包含两个参数:参数一:缓存的key,参数二:Lambda表达式,表达式参数就是缓存的key,方法体是查询数据库的逻辑
// 优先根据key查询JVM缓存,如果未命中,则执行参数二的Lambda表达式去数据库数据库查数据,并加入到缓存。
String defaultGF = cache.get("defaultGF", key -> {
// 根据key去数据库查询数据
return "李四";
});
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
caffeine初始化创建时,可以设置缓存的容量上限和缓存的有效时间
Cache<String, String> cache = Caffeine.newBuilder().maximumSize(1).build();
Cache<String, String> cache = Caffeine.newBuilder().expireAfterWrite(Duration.ofSeconds(10)).build();
1
2
3
2
3
# 2.多级缓存(OpenResty)
分布式下每台服务器保存的本地缓存不会共享,因此需要在负载均衡配置中设置 hash+请求uri,保证相同地址的请求访问的是同一台机器的缓存。
冷启动:服务刚刚启动时,redis并没有缓存,如果所有商品数据都在第一次查询时添加缓存,可能会给数据库带来较大压力。
缓存预热:项目启动时将热点数据提前查询并保存到redis中(实现接口中的afterPropertiesSet方法,会在项目启动创建Bean的成员变量后执行)
ObjectMapper的writeValueAsString方法将对象转化成String
@Component
public class RedisHandler implements InitializingBean{
@Autowired
private StringRedisTemplate redisTemplate;
//spring默认的json处理工具
private static final ObjectMapper MAPPER = new ObjectMapper();
@Override
public void afterPropertiesSet throws Exception{//初始化缓存}
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
整个查询逻辑:本地缓存—>redis—>Tomcat服务器内存cache—>DB
function read_data(key, expire, path, params)
-- 查询本地缓存
local val = item_cache:get(key)
if not val then
ngx.log(ngx.ERR, "本地缓存查询失败,尝试查询Redis, key: ", key)
-- 查询redis
val = read_redis("127.0.0.1", 6379, key)
-- 判断查询结果
if not val then
ngx.log(ngx.ERR, "redis查询失败,尝试查询http, key: ", key)
-- redis查询失败,去查询http
val = read_http(path, params)
end
end
-- 查询成功,把数据写入本地缓存
item_cache:set(key, val, expire)
-- 返回数据
return val
end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 3.Canal
解决缓存和数据库同步的三种方法:
- 给缓存设置有效期,到期后自动删除。再次查询时会搜索数据库进行更新
- 同步双写,更新数据库的同时,直接修改缓存。两者绑定一个事务,但是代码耦合度高
- 异步通知,修改数据库时发送事件通知,修改缓存。
# 原理
Canal会把自己伪装成MySQL的一个slave节点,监听master的binary log变化,然后再把变化消息通知给Canal客户端。和MQ类似,区别在于Canal不需要推送消息,采用的是监听机制。
- 添加maven依赖
- 配置canal监听的数据库名称,canal服务端地址
- 实现数据更新后JAVA逻辑,@CanalTable注解监听的表
@CanalTable("tb_item")
@Component
public class ItemHandler implements EntryHandler<Item> {
@Autowired
private Cache<Long, Item> itemCache;
@Override
public void insert(Item item) {
itemCache.put(item.getId(), item);
}
@Override
public void update(Item before, Item after) {
itemCache.put(after.getId(), after);
}
@Override
public void delete(Item item) {
itemCache.invalidate(item.getId());
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- 另外通过注解告诉Canal封装的Item类,@Id标记表中的id,@Transient标记不属于表中的字段。
编辑 (opens new window)
上次更新: 2023/12/15, 15:49:57