多级缓存设计和实战应用 多层次缓存设计和实际应用(7)
多级缓存设计和实战应用

5、编写监听器
通过实现EntryHandler<T>接口编写监听器,监听Canal消息。注意两点:
- 实现类通过
@CanalTable("tb_item")指定监听的表信息 - EntryHandler的泛型是与表对应的实体类
@CanalTable("tb_item")
@Component
public class ItemHandler implements EntryHandler<Item> {
@Autowired
private RedisHandler redisHandler;
@Autowired
private Cache<Long, Item> itemCache;
@Override
public void insert(Item item) {
// 写数据到JVM进程缓存
itemCache.put(item.getId(), item);
// 写数据到redis
redisHandler.saveItem(item);
}
@Override
public void update(Item before, Item after) {
// 写数据到JVM进程缓存
itemCache.put(after.getId(), after);
// 写数据到redis
redisHandler.saveItem(after);
}
@Override
public void delete(Item item) {
// 删除数据到JVM进程缓存
itemCache.invalidate(item.getId());
// 删除数据到redis
redisHandler.deleteItemById(item.getId());
}
}
6、演示效果 比如:我们将id = 10002 脱脂牛奶 价格(price)变为998.
修改前:
mysql> select t.id,t.name,t.price from mydb.tb_item t where t.id = '10002' ;
+-------+--------------+-------+
| id | name | price |
+-------+--------------+-------+
| 10002 | 脱脂牛奶 | 68600 |
+-------+--------------+-------+
1 row in set (0.00 sec)
执行修改: 访问:http://localhost:8081/item 更新id=10002 对应价格998. 请求参数:
{
"id":10002,
"name":"脱脂牛奶",
"title":"安佳脱脂牛奶 新西兰进口轻欣脱脂250ml*24整箱装*2",
"price":998,
"image":"https://m.360buyimg.com/mobilecms/s720x720_jfs/t25552/261/1180671662/383855/33da8faa/5b8cf792Neda8550c.jpg!q70.jpg.webp",
"category":"牛奶",
"brand":"安佳",
"spec":"{\"数量\": 30}",
"status":1,
"createTime":"2024-01-25T16:00:00.000+00:00",
"updateTime":"2024-01-25T16:00:00.000+00:00",
"stock":99999,
"sold":54981
}
修改后:
查看数据库:
mysql> select t.id,t.name,t.price from mydb.tb_item t where t.id = '10002' ;
+-------+--------------+-------+
| id | name | price |
+-------+--------------+-------+
| 10002 | 脱脂牛奶 | 998 |
+-------+--------------+-------+
1 row in set (0.00 sec)
查看redis:

查看控制台:
11:14:28:980 INFO 19916 --- [l-client-thread] t.j.c.client.client.AbstractCanalClient : 获取消息 Message[id=2,entries=[header {
version: 1
logfileName: "mysql-bin.000005"
logfileOffset: 978
serverId: 1000
serverenCode: "UTF-8"
executeTime: 1706325266000
sourceType: MYSQL
schemaName: ""
tableName: ""
eventLength: 31
}
说明Canal同步缓存数据生效,验证通过。
基于OpenResty的Nginx本地缓存
OpenResty为Nginx提供了shard dict的功能,可以在nginx的多个worker之间共享数据,实现缓存功能。
这里粗略给出OpenResty安装后的目录结构:

从目录结构可以看到:OpenResty 内置Nginx + Lua模块
1)定义共享字典,在nginx.conf的http下添加配置:
# 共享字典,也就是本地缓存,名称叫做:item_cache,大小150m
lua_shared_dict item_cache 150m;
2)使用共享字典:
-- 获取本地缓存对象
local item_cache = ngx.shared.item_cache
-- 存储, 指定key、value、过期时间,单位s,默认为0代表永不过期
item_cache:set('key', 'value', 1000)
-- 读取
local val = item_cache:get('key')
3)实现本地缓存查询
修改/usr/local/openresty/lua/item.lua文件,添加本地缓存逻辑:
设置了缓存过期时间,过期后nginx缓存会自动删除,下次访问即可更新缓存。
这里给商品基本信息设置超时时间为30分钟。因为库存更新频率较高,如果缓存时间过长,可能与数据库差异较大。
完整的item.lua文件:
-- 导入common函数库
local common = require('common')
local read_http = common.read_http
local read_redis = common.read_redis
-- 导入cjson库
local cjson = require('cjson')
-- 导入共享词典,本地缓存
local item_cache = ngx.shared.item_cache
-- 封装查询函数
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("1.XXX.XXX.47", 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
-- 获取路径参数
local id = ngx.var[1]
-- 查询商品信息
local itemJSON = read_data("item:id:" .. id, 1800 , "/item/" .. id, nil)
-- JSON转化为lua的table
local item = cjson.decode(itemJSON)
-- 把item序列化为json 返回结果
ngx.say(cjson.encode(item))
关于OpenResty 的使用和语法结构,读者请自行查阅。由于笔者属于后端领域,这里暂时简单介绍。
感兴趣的可以深究。
官网地址:https://openresty.org/cn/
总结
当考虑不同的缓存设计方案时,需要根据具体的应用场景和需求来选择。
这里给出项目链路上关于缓存设计的应用场景和建议:
- 浏览器缓存:
- 应用场景:适用于静态资源如图片、CSS和JavaScript等的缓存,以减少服务器负载和加快页面加载速度。
- 建议:使用HTTP标头控制缓存策略,如Cache-Control和Expires,确保资源在客户端浏览器上被有效地缓存,减少对服务器的请求次数。
- 基于OpenResty的Nginx本地缓存:
- 应用场景:适用于对频繁请求的动态数据进行本地缓存,如API响应数据、网页片段等。
- 建议:使用OpenResty结合Nginx的本地缓存功能,通过Lua脚本实现对特定请求响应的本地缓存,可以有效减少后端服务器的负载和提高响应速度。
- Redis缓存:
- 应用场景:适用于需要分布式缓存、高并发读写、数据结构丰富(如哈希、列表、集合等)的场景,如会话管理、页面片段缓存、数据缓存等。
- 建议:使用Redis作为分布式缓存,结合其丰富的数据结构和高性能特性,可以实现高效的缓存管理和数据处理。
- JVM进程缓存:
- 应用场景:适用于对应用程序内部数据进行缓存,如对象缓存、方法结果缓存等。
- 建议:使用基于内存的缓存库,如Caffeine、ConcurrentHashMap、Guava Cache等,将需要频繁访问的数据缓存在JVM进程中,以加快数据访问速度,并减少对外部存储的依赖。
综上,缓存设计方案能极大提高应用程序的性能和可扩展性。
需要说明的是:后台系统最好注意对缓存的管理、监控和更新。
此外,我们知道任何的设计都是根据需要的,因为引入新的技术就会带来新的问题,所以也要避免过度设计。
这里讲的是生产实践中的一些思路,可以说多一种选择、多一种可能。
上述就是多级缓存设计和实战应用的相关内容,希望对您有所帮助!
相关阅读
-
编写一个简单的网页结构 网页设计作业html成品
小编为网友们解答编写一个简单的网页结构和网页设计作业html成品方面的介绍,具体详情如下: HTML(HyperText Markup Language)是一种简单、通用的超文本标记语言,Web服务器返回给客户端的最常
-
access数据库的对象是什么 了解access数据库的结构层次
下面为网友们详细介绍access数据库的对象是什么和了解access数据库的结构层次方面的内容,接下来分享详细内容。 说到数据处理,大家首先想到的是Excel,其实,Office中的数据处理软件除了E
-
web服务器怎么配置 免费个人web服务器推荐
对于许多网友来说web服务器怎么配置和免费个人web服务器推荐方面的内容,具体详情如下: 回想一下一个http请求的过程,你在浏览器输入xxx.com,经过域名解析 发起tcp的3次握手 建立tcp连接后
-
信息加密、解密与常用算法的基本概念和相关知识
本文为您带来的是信息加密、解密与常用算法的基本概念和相关知识的话题,请看下面详细的介绍。 一、信息加密概念 信息加密技术是对信息进行伪装,使得信息非法窃取者无法理解信息的真


