b站讲缓存的视频
一些讲解缓存的书籍
开源项目
再结合实际。比如想想大佬开发的软件所展示出来的效果,想想那些公司啊。
学习就是不断迭代的过程,既然如此,那我就不需要害怕犯错。坚持总能出结果的。
击穿:一个高并发访问的时候,针对一个不存在的缓存数据的查询,由于缓存中没有相关的数据,导致这个查询直接落在数据库上,造成数据库的压力过大,导致数据库性能下降甚至宕机。比如我就故意用null或者其它缓存不存在的值去访问。
穿透:一个请求查询一个非常多缓存和数据库中都没有的数据时,这种情况下就会导致请求穿透到后端数据库,造成数据库的压力过大。讲人话,就是某热搜新闻,假设这里发生了缓存穿透,你说数据库会不会崩溃呢?
雪崩:某个时间段,缓存集中失效,而导致大量的请求直接落到数据库上,造成数据库的 压力过大,甚至引起数据库崩溃。
整个流程
类设计
整个缓存查询流程,只有查询数据库那里会发生变化,我就简单的封装下:
/**
* 执行搜索
*
* @param callback 回调
* @param key 关键key
* @param maxTime 最长时间
* @param timeUnit 时间单位
* @return {@link T}
*/
@Override
public <T> T executeSearch(ReadDataFromDbCallback<T> callback,String key,long maxTime, TimeUnit timeUnit) {
// 无效数据的过滤,减少击穿。
if(!codeGenerateCacheFilter.filter(key)){
return null;
}
// 从远程redis缓存找,这里会有穿透,想办法识别并延长热点数据
T result = codeGenerateCache.getCacheData(key);
if(result != null){
return result;
}
// 缓存找不到则从mybatis一二级缓存找,最后才会去数据库找。
result = callback.readDataFromDb();
if(result != null){
// 缓存放入数据,可能有雪崩的隐患,里边已解决,maxTime+ThreadLocalRandom产生的随机数
codeGenerateCache.putCacheData(key,result,maxTime,timeUnit);
return result;
}
// 没有找到
return null;
}
使用例子
DeveloperInfo developerInfo = codeGenerateCacheOperations.executeSearch(() -> {
DeveloperInfo result = developerInfoService.getById(id);
return result;
}, "1001", 60*5, TimeUnit.SECONDS);
Assert.assertNotNull(developerInfo);
缓存更新
有修改/删除,直接删除。
先更新数据库,然后删除,再延时删除
/**
* 执行更新
*
* @param callback 回调
* @param key 关键key
* @return {@link T}
*/
@Override
public <T> T executeUpdate(WriteDataToDbCallback<T> callback,String key) {
T result;
try{
result = callback.writeDataToDb();
}catch (Exception e){
log.info("数据库更新执行异常",e);
return null;
}
if(result != null){
codeGenerateCache.deleteCacheData(key);
}
return result;
}
使用例子
DeveloperInfo developerInfo = codeGenerateCacheOperations.executeSearch(() -> {
DeveloperInfo result = developerInfoService.getById(id);
return result;
}, "1001", 60*5, TimeUnit.SECONDS);
Assert.assertNotNull(developerInfo);
DeveloperInfo data = codeGenerateCacheOperations.executeSearch(() -> {
// 这里已经有缓存,若走到这里则表示有问题
Assert.fail();
DeveloperInfo result = developerInfoService.getById(developerInfo.getUserId());
return result;
}, "1001", 60*5, TimeUnit.SECONDS);
Assert.assertNotNull(data);
developerInfo.setUserName("lch_001");
codeGenerateCacheOperations.executeUpdate(() -> {
boolean result = developerInfoService.updateById(developerInfo);
if(!result){
return null;
}
return developerInfo;
},developerInfo.getUserId());
List<DeveloperInfo> list = new ArrayList<>();
data = codeGenerateCacheOperations.executeSearch(() -> {
DeveloperInfo result = developerInfoService.getById(developerInfo.getUserId());
// 缓存已经被删除,应该要走这里
list.add(result);
return result;
}, "1001", 60*5, TimeUnit.SECONDS);
Assert.assertNotNull(data);
Assert.assertTrue(list.size()>0);