Redis——某马点评day02——商铺缓存
什么是缓存


添加Redis缓存
添加商铺缓存

Controller层中
/*** 根据id查询商铺信息* @param id 商铺id* @return 商铺详情数据*/@GetMapping("/{id}")public Result queryShopById(@PathVariable("id") Long id) {return shopService.queryById(id);}
Service层中
*/
@Service
public class ShopServiceImpl extends ServiceImpl<ShopMapper, Shop> implements IShopService {@Resourceprivate StringRedisTemplate stringRedisTemplate;@Overridepublic Result queryById(Long id) {String key="cache:shop:" + id;//1.从Redis查询缓存String shopJson = stringRedisTemplate.opsForValue().get(key);//2.判断是否存在if (StrUtil.isNotBlank(shopJson)) {//3.存在,直接返回Shop shop = JSONUtil.toBean(shopJson, Shop.class);return Result.ok(shop);}//4.不存在,根据id查询数据库Shop shop = getById(id);if (shop==null) {//5.不存在,返回错误return Result.fail("店铺不存在");}//6.存在,写入RedisstringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(shop));//7.返回return Result.ok(shop);}
}
练习添加店铺类型缓存
Controller层中
@RestController
@RequestMapping("/shop-type")
public class ShopTypeController {@Resourceprivate IShopTypeService typeService;@GetMapping("list")public Result queryTypeList() {return typeService.queryTypeList();}
}
Service层中
@Overridepublic Result queryTypeList() {String key="cache:shopType";//1.从Redis查询缓存String shopType = stringRedisTemplate.opsForValue().get(key);//2.判断是否存在if (StrUtil.isNotBlank(shopType)) {//3.存在,直接返回List<ShopType> typeList = JSONUtil.toList(shopType, ShopType.class);return Result.ok(typeList);}//4.不存在,查询数据库List<ShopType> typeList = query().orderByAsc("sort").list();//5.存在,写入RedisstringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(typeList));//7.返回return Result.ok(typeList);}
缓存更新策略

通常选择的方案都是第一种
单体系统可以通过@Transactional注解完成事务。

通常是先操作数据库,再删除缓存,出现问题的几率极小。

实现商铺缓存和数据库的双写一致

第一个地方,写入Redis时加上超时时间。
//6.存在,写入RedisstringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(shop),CACHE_SHOP_TTL, TimeUnit.MINUTES);
第二个地方
controller中
/*** 更新商铺信息* @param shop 商铺数据* @return 无*/@PutMappingpublic Result updateShop(@RequestBody Shop shop) {return shopService.update(shop);}
service中
@Overridepublic Result update(Shop shop) {Long id = shop.getId();if(id==null){return Result.fail("店铺id不能为空");}//1.更新数据库updateById(shop);//2.删除缓存stringRedisTemplate.delete(CACHE_SHOP_KEY+id);return null;}
缓存穿透
布隆过滤器的实现不是真的存储数据,而是用某种Hash算法计算之后用二进制压缩之类的方法保存是否存在。但是,也有可能多个数据hash值相同导致错误结果。

编码解决商铺查询的缓存穿透(缓存空对象做法)
代码修改
@Overridepublic Result queryById(Long id) {String key=CACHE_SHOP_KEY+ id;//1.从Redis查询缓存String shopJson = stringRedisTemplate.opsForValue().get(key);//2.判断是否存在if (StrUtil.isNotBlank(shopJson)) {//3.存在,直接返回Shop shop = JSONUtil.toBean(shopJson, Shop.class);return Result.ok(shop);}//判断命中的是否是空值if(shopJson!=null){//返回一个错误信息return Result.fail("店铺不存在");}//4.不存在,根据id查询数据库Shop shop = getById(id);if (shop==null) {//将空值写入RedisstringRedisTemplate.opsForValue().set(key,"",CACHE_NULL_TTL, TimeUnit.MINUTES);//5.不存在,返回错误return Result.fail("店铺不存在");}//6.存在,写入RedisstringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(shop),CACHE_SHOP_TTL, TimeUnit.MINUTES);//7.返回return Result.ok(shop);}
限流可以用sentinel实现.
缓存雪崩
宕机时降级限流也是用sentinel实现。
nginx缓存也是一级缓存.
tmd,一直在说springcloud里面有讲。

缓存击穿
常见解决方案

这里可以参考一下redisson的源码设计思路,设计一个监听通知机制!
逻辑过期解决方案不会设置ttl过期时间,而是新增一个exprie字段,从redis里面查询发现是过期数据时就需要加锁开启一个新线程去更新缓存,然后直接返回旧数据。有别的线程来获取锁失败时说明已经有线程在进行更新,所以就直接返回过期数据,避免了过多线程等待锁。


利用互斥锁解决缓存击穿问题(重点)

这里的锁不能用lock和synchronized进行互斥实现,这两个会一直等待.这里用到Redis的一个命令setnx, 这个是一旦设置之后就不能修改,只能删除,但是如果因为意外原因导致迟迟不能删除会有大问题,所以这里会给锁设置一个有效期.

代码修改
@Service
public class ShopServiceImpl extends ServiceImpl<ShopMapper, Shop> implements IShopService {@Resourceprivate StringRedisTemplate stringRedisTemplate;@Overridepublic Result queryById(Long id) {//缓存穿透//Shop shop=queryWithPassThrouh(id);//互斥锁解决缓存击穿Shop shop = queryWithMutex(id);if(shop==null){return Result.fail("店铺不存在!");}//7.返回return Result.ok(shop);}public Shop queryWithMutex(Long id){String key=CACHE_SHOP_KEY+ id;//1.从Redis查询缓存String shopJson = stringRedisTemplate.opsForValue().get(key);//2.判断是否存在if (StrUtil.isNotBlank(shopJson)) {//3.存在,直接返回return JSONUtil.toBean(shopJson, Shop.class);}//判断命中的是否是空值if(shopJson!=null){//返回一个错误信息return null;}//4.实现缓存重建//4.1获取互斥锁String lockkey="lock:shop:"+id;Shop shop = null;try {boolean isLock = tryLock(lockkey);//4.2判断是否获取成功if(!isLock){//4.3失败,休眠并重试Thread.sleep(50);return queryWithMutex(id); //这里有可能会出现栈溢出的情况。}//获取成功之后应该再次检查缓存是否存在,有可能别的线程已经重建完了缓存,所以这里就无需再重建缓存shopJson = stringRedisTemplate.opsForValue().get(key);//再次判断是否存在if (StrUtil.isNotBlank(shopJson)) {//存在,直接返回return JSONUtil.toBean(shopJson, Shop.class);}//4.4根据id查询数据库shop = getById(id);//模拟重建的延时Thread.sleep(200);if (shop==null) {//将空值写入RedisstringRedisTemplate.opsForValue().set(key,"",CACHE_NULL_TTL, TimeUnit.MINUTES);//5.不存在,返回错误return shop;}//6.存在,写入RedisstringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(shop),CACHE_SHOP_TTL, TimeUnit.MINUTES);} catch (InterruptedException e) {throw new RuntimeException(e);} finally {//7.释放互斥锁unlock(lockkey);}//8.返回return shop;}public Shop queryWithPassThrouh(Long id){String key=CACHE_SHOP_KEY+ id;//1.从Redis查询缓存String shopJson = stringRedisTemplate.opsForValue().get(key);//2.判断是否存在if (StrUtil.isNotBlank(shopJson)) {//3.存在,直接返回Shop shop = JSONUtil.toBean(shopJson, Shop.class);return shop;}//判断命中的是否是空值if(shopJson!=null){//返回一个错误信息return null;}//4.不存在,根据id查询数据库Shop shop = getById(id);if (shop==null) {//将空值写入RedisstringRedisTemplate.opsForValue().set(key,"",CACHE_NULL_TTL, TimeUnit.MINUTES);//5.不存在,返回错误return shop;}//6.存在,写入RedisstringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(shop),CACHE_SHOP_TTL, TimeUnit.MINUTES);//7.返回return shop;}private boolean tryLock(String key){Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.SECONDS);return BooleanUtil.isTrue(flag);}private void unlock(String key){stringRedisTemplate.delete(key);}@Overridepublic Result update(Shop shop) {Long id = shop.getId();if(id==null){return Result.fail("店铺id不能为空");}//1.更新数据库updateById(shop);//2.删除缓存stringRedisTemplate.delete(CACHE_SHOP_KEY+id);return null;}
}
这里可以上Jmeter进行压测,上100个线程进行测试

但是最终实际只查询了一次数据库.

利用逻辑过期解决缓存击穿问题(重点)

为了能增加一个逻辑过期时间的字段,新建一个对象
@Data
public class RedisData {private LocalDateTime expireTime;private Object data;
}
代码修改
@Service
public class ShopServiceImpl extends ServiceImpl<ShopMapper, Shop> implements IShopService {@Resourceprivate StringRedisTemplate stringRedisTemplate;@Overridepublic Result queryById(Long id) {//缓存穿透//Shop shop=queryWithPassThrouh(id);//互斥锁解决缓存击穿
// Shop shop = queryWithMutex(id);//逻辑过期解决缓存击穿问题Shop shop = queryWithLogicalExpire(id);if(shop==null){return Result.fail("店铺不存在!");}//7.返回return Result.ok(shop);}private static final ExecutorService CACHE_REBUILD_EXECUTOR= Executors.newFixedThreadPool(10);public Shop queryWithLogicalExpire(Long id){String key=CACHE_SHOP_KEY+ id;//1.从Redis查询缓存String shopJson = stringRedisTemplate.opsForValue().get(key);//2.判断是否存在if (StrUtil.isBlank(shopJson)) {//3.存在,直接返回nullreturn null;}//4.命中,需要先把json反序列化为对象RedisData redisData = JSONUtil.toBean(shopJson, RedisData.class);Shop shop = JSONUtil.toBean((JSONObject)redisData.getData(), Shop.class);LocalDateTime expireTime = redisData.getExpireTime();//5.判断是否过期if(expireTime.isAfter(LocalDateTime.now())){//5.1未过期,直接返回店铺信息return shop;}//5.2已过期,需要缓存重建//6.缓存重建//6.1获取互斥锁String lockKey=LOCK_SHOP_KEY+id;boolean isLock = tryLock(lockKey);//6.2判断是否获取锁成功if(isLock){//这里应该再次检测缓存是否过期,做双重判断,如果没过期就不需重建了,因为可能别的线程已经重建了shopJson = stringRedisTemplate.opsForValue().get(key);redisData = JSONUtil.toBean(shopJson, RedisData.class);expireTime = redisData.getExpireTime();if(expireTime.isAfter(LocalDateTime.now())){//返回前先释放锁unlock(lockKey);//5.1未过期,直接返回店铺信息return shop;}//6.3成功,开启独立线程,实现缓存重建CACHE_REBUILD_EXECUTOR.submit(()->{try {//重建缓存this.saveShop2Redis(id,20L);} catch (Exception e) {throw new RuntimeException(e);} finally {//释放锁unlock(lockKey);}});}//6.4失败,返回过期商铺信息。return shop;}public Shop queryWithMutex(Long id){String key=CACHE_SHOP_KEY+ id;//1.从Redis查询缓存String shopJson = stringRedisTemplate.opsForValue().get(key);//2.判断是否存在if (StrUtil.isNotBlank(shopJson)) {//3.存在,直接返回return JSONUtil.toBean(shopJson, Shop.class);}//判断命中的是否是空值if(shopJson!=null){//返回一个错误信息return null;}//4.实现缓存重建//4.1获取互斥锁String lockkey="lock:shop:"+id;Shop shop = null;try {boolean isLock = tryLock(lockkey);//4.2判断是否获取成功if(!isLock){//4.3失败,休眠并重试Thread.sleep(50);return queryWithMutex(id); //这里有可能会出现栈溢出的情况。}//获取成功之后应该再次检查缓存是否存在,有可能别的线程已经重建完了缓存,所以这里就无需再重建缓存shopJson = stringRedisTemplate.opsForValue().get(key);//再次判断是否存在if (StrUtil.isNotBlank(shopJson)) {//存在,直接返回return JSONUtil.toBean(shopJson, Shop.class);}//4.4根据id查询数据库shop = getById(id);//模拟重建的延时//Thread.sleep(200);if (shop==null) {//将空值写入RedisstringRedisTemplate.opsForValue().set(key,"",CACHE_NULL_TTL, TimeUnit.MINUTES);//5.不存在,返回错误return shop;}//6.存在,写入RedisstringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(shop),CACHE_SHOP_TTL, TimeUnit.MINUTES);} catch (InterruptedException e) {throw new RuntimeException(e);} finally {//7.释放互斥锁unlock(lockkey);}//8.返回return shop;}public Shop queryWithPassThrouh(Long id){String key=CACHE_SHOP_KEY+ id;//1.从Redis查询缓存String shopJson = stringRedisTemplate.opsForValue().get(key);//2.判断是否存在if (StrUtil.isNotBlank(shopJson)) {//3.存在,直接返回Shop shop = JSONUtil.toBean(shopJson, Shop.class);return shop;}//判断命中的是否是空值if(shopJson!=null){//返回一个错误信息return null;}//4.不存在,根据id查询数据库Shop shop = getById(id);if (shop==null) {//将空值写入RedisstringRedisTemplate.opsForValue().set(key,"",CACHE_NULL_TTL, TimeUnit.MINUTES);//5.不存在,返回错误return shop;}//6.存在,写入RedisstringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(shop),CACHE_SHOP_TTL, TimeUnit.MINUTES);//7.返回return shop;}private boolean tryLock(String key){Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.SECONDS);return BooleanUtil.isTrue(flag);}private void unlock(String key){stringRedisTemplate.delete(key);}public void saveShop2Redis(Long id,Long expireSeconds) throws InterruptedException {//1.查询店铺数据Shop shop = getById(id);//模拟延时
// Thread.sleep(200);//2.封装逻辑过期时间RedisData redisData = new RedisData();redisData.setData(shop);redisData.setExpireTime(LocalDateTime.now().plusSeconds(expireSeconds));//3.写入RedisstringRedisTemplate.opsForValue().set(CACHE_SHOP_KEY+id,JSONUtil.toJsonStr(redisData));}@Overridepublic Result update(Shop shop) {Long id = shop.getId();if(id==null){return Result.fail("店铺id不能为空");}//1.更新数据库updateById(shop);//2.删除缓存stringRedisTemplate.delete(CACHE_SHOP_KEY+id);return null;}
}
缓存工具封装(重点)

封装工具类里用到的实体
@Data
public class RedisData {private LocalDateTime expireTime;private Object data;
}
工具类代码
@Slf4j
@Component
public class CacheClient {@Resourceprivate StringRedisTemplate stringRedisTemplate;String LOCK_SHOP_KEY="lock:shop:";public CacheClient(StringRedisTemplate stringRedisTemplate) {this.stringRedisTemplate = stringRedisTemplate;}public void set(String key, Object value, Long time, TimeUnit timeUnit){stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(value),time,timeUnit);}public void setWithLogicalExpire(String key, Object value, Long time, TimeUnit timeUnit){//设置逻辑过期RedisData redisData = new RedisData();redisData.setData(value);redisData.setExpireTime(LocalDateTime.now().plusSeconds(timeUnit.toSeconds(time)));//写入RedisstringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(redisData));}public <R,ID> R queryWithPassThrough(String keyPrefix, ID id, Class<R> type, Function<ID,R>dbFallback, Long time, TimeUnit timeUnit){String key=keyPrefix+id;//1.从Redis查询缓存String json = stringRedisTemplate.opsForValue().get(key);//2.判断是否存在if (StrUtil.isNotBlank(json)) {//3.存在,直接返回return JSONUtil.toBean(json, type);}//判断命中的是否是空值if(json!=null){//返回一个错误信息return null;}//4.不存在,根据id查询数据库R r=dbFallback.apply(id);//5.不存在,返回错误if (r==null) {//将空值写入RedisstringRedisTemplate.opsForValue().set(key,"",2L, TimeUnit.MINUTES);return null;}//6.存在,写入Redisthis.set(key,r,time,timeUnit);//7.返回return r;}private static final ExecutorService CACHE_REBUILD_EXECUTOR= Executors.newFixedThreadPool(10);public <R,ID> R queryWithLogicalExpire(String keyPrefix, ID id, Class<R> type, Function<ID,R>dbFallback, Long time, TimeUnit timeUnit){String key=keyPrefix+ id;//1.从Redis查询缓存String json = stringRedisTemplate.opsForValue().get(key);//2.判断是否存在if (StrUtil.isBlank(json)) {//3.存在,直接返回nullreturn null;}//4.命中,需要先把json反序列化为对象RedisData redisData = JSONUtil.toBean(json, RedisData.class);R r = JSONUtil.toBean((JSONObject)redisData.getData(), type);LocalDateTime expireTime = redisData.getExpireTime();//5.判断是否过期if(expireTime.isAfter(LocalDateTime.now())){//5.1未过期,直接返回店铺信息return r;}//5.2已过期,需要缓存重建//6.缓存重建//6.1获取互斥锁String lockKey=LOCK_SHOP_KEY+id;boolean isLock = tryLock(lockKey);//6.2判断是否获取锁成功if(isLock){//这里应该再次检测缓存是否过期,做双重判断,如果没过期就不需重建了,因为可能别的线程已经重建了json = stringRedisTemplate.opsForValue().get(key);redisData = JSONUtil.toBean(json, RedisData.class);r = JSONUtil.toBean((JSONObject)redisData.getData(), type);expireTime = redisData.getExpireTime();if(expireTime.isAfter(LocalDateTime.now())){//返回前先释放锁unlock(lockKey);//5.1未过期,直接返回店铺信息return r;}//6.3成功,开启独立线程,实现缓存重建CACHE_REBUILD_EXECUTOR.submit(()->{try {//重建缓存//查询数据库R r1 = dbFallback.apply(id);//写入Redisthis.setWithLogicalExpire(key,r1,time,timeUnit);} catch (Exception e) {throw new RuntimeException(e);} finally {//释放锁unlock(lockKey);}});}//6.4失败,返回过期信息。return r;}private boolean tryLock(String key){Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.SECONDS);return BooleanUtil.isTrue(flag);}private void unlock(String key){stringRedisTemplate.delete(key);}
}
Service层修改后代码
里面有缓存穿透的调用,也有缓存击穿的调用.
@Service
public class ShopServiceImpl extends ServiceImpl<ShopMapper, Shop> implements IShopService {@Resourceprivate StringRedisTemplate stringRedisTemplate;@Resourceprivate CacheClient cacheClient;@Overridepublic Result queryById(Long id) {//缓存穿透Shop shop=cacheClient.queryWithPassThrough(CACHE_SHOP_KEY,id, Shop.class,id2->getById(id2),CACHE_SHOP_TTL,TimeUnit.MINUTES);//Shop shop=cacheClient.queryWithPassThrough(CACHE_SHOP_KEY,id, Shop.class,this::getById,,CACHE_SHOP_TTL,TimeUnit.MINUTES);//互斥锁解决缓存击穿//Shop shop = queryWithMutex(id);//逻辑过期解决缓存击穿问题
// Shop shop = cacheClient
// .queryWithLogicalExpire(CACHE_SHOP_KEY,id,Shop.class,this::getById,CACHE_SHOP_TTL,TimeUnit.SECONDS);if(shop==null){return Result.fail("店铺不存在!");}//7.返回return Result.ok(shop);}@Overridepublic Result update(Shop shop) {Long id = shop.getId();if(id==null){return Result.fail("店铺id不能为空");}//1.更新数据库updateById(shop);//2.删除缓存stringRedisTemplate.delete(CACHE_SHOP_KEY+id);return null;}
}
内容总结:
去看文档资料里面xmind文档,那个里面总结的很好。
相关文章:
Redis——某马点评day02——商铺缓存
什么是缓存 添加Redis缓存 添加商铺缓存 Controller层中 /*** 根据id查询商铺信息* param id 商铺id* return 商铺详情数据*/GetMapping("/{id}")public Result queryShopById(PathVariable("id") Long id) {return shopService.queryById(id);} Service…...
prometheus|云原生|轻型日志收集系统loki+promtail的部署说明
一, 日志聚合的概念说明 日志------ 每一个程序,服务都应该有保留日志,日志的作用第一是记录程序运行的情况,在出错的时候能够记录错误情况,简单来说就是审计工作,例如nginx服务的日志,kuber…...
MySQL 临时数据空间不足导致SQL被killed 的问题与扩展
开头还是介绍一下群,如果感兴趣PolarDB ,MongoDB ,MySQL ,PostgreSQL ,Redis, Oceanbase, Sql Server等有问题,有需求都可以加群群内,可以解决你的问题。加群请联系 liuaustin3 ,(共1730人左右 1 2 3 4 5࿰…...
文心一言大模型应用开发入门
本文重点介绍百度智能云平台、文心一言、千帆大模型平台的基本使用与接入流程及其详细步骤。 注册文心一言 请登录文心一言官方网站 https://yiyan.baidu.com/welcome 点击登录;图示如下: 请注册文心一言账号并点击登录,图示如下࿱…...
C++新经典模板与泛型编程:SFINAE替换失败并不是一个错误
替换失败并不是一个错误(SFINAE) SFINAE是一个英文简称,全称为Substitution Failure is not an Error,翻译成中文就是“替换失败并不是一个错误”。 SFINAE可以看作C语言的一种特性或模板设计中要遵循的一个重要原则,…...
基于若依的ruoyi-nbcio流程管理系统支持支持定时边界事件和定时捕获事件
更多ruoyi-nbcio功能请看演示系统 gitee源代码地址 前后端代码: https://gitee.com/nbacheng/ruoyi-nbcio 演示地址:RuoYi-Nbcio后台管理系统 1、定时边界事件 <template><div class"panel-tab__content"><!--目前只处理定…...
递归-极其优雅的问题解决方法(Java)
递归的定义 大名鼎鼎的递归,相信你即使没接触过也或多或少听过,例如汉诺塔问题就是运用了递归的思想,对于一些学过c语言的同学来说,它可能就是噩梦,因为我当时就是这么认为的(不接受反驳doge) …...
VSCode搭建STM32开发环境
1、下载安装文件 链接:https://pan.baidu.com/s/1WnpDTgYBobiZaXh80pn5FQ 2、安装VSCodeUserSetup-x64-1.78.2.exe软件 3、 在VSCode中安装必要的插件 3、配置Keil Assistant插件 4、在环境变量中部署mingw64编译环境...
解决CentOS下PHP system命令unoconv转PDF提示“Unable to connect or start own listener“
centos系统下,用php的system命令unoconv把word转pdf时提示Unable to connect or start own listene的解决办法 unoconv -o /foo/bar/public_html/upload/ -f pdf /foo/bar/public_html/upload/test.docx 2>&1 上面这个命令在shell 终端能执行成功,…...
软件测试外包干了2个月,技术进步2年。。。
先说一下自己的情况,本科生,18年通过校招进入北京某软件公司,干了接近2年的功能测试,今年国庆,感觉自己不能够在这样下去了,长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了2年的功能测试&…...
Linux-网络服务和端口
域名:便于人们记忆和使用的标识符 www.baidu.com域名解析:将域名转换为与之对应的 IP 地址的过程 nameserver 8.8.8.8ip地址:网络设备的唯一数字标识符 域名ip地址localhost127.0.0.1 网络服务和端口 网络服务端口ftp21ssh22http80https…...
Kubernetes权威指南:从Docker到Kubernetes实践全接触(第5版)读书笔记 目录
完结状态:未完结 文章目录 前言第1章 Kubernetes入门 11.1 了解Kubernetes 2 附录A Kubernetes核心服务配置详解 915总结 前言 提示:这里可以添加本文要记录的大概内容: Kubernetes权威指南:从Docker到Kubernetes实践全接触&…...
阿里云Arthas使用——通过watch命令查看类的返回值 捞数据出来
前言 Arthas 是一款线上监控诊断产品,通过全局视角实时查看应用 load、内存、gc、线程的状态信息,并能在不修改应用代码的情况下,对业务问题进行诊断,包括查看方法调用的出入参、异常,监测方法执行耗时,类…...
Redis中持久化策略RDB与AOF优缺点对比
Redis持久化策略对比 RDBAOF持久化方式定时对整个内存做快照记录每一次执行的命令数据完整性不完整,两次备份之间存在丢失相对完整,取决于刷盘策略文件大小会有压缩,文件体积小记录命令,文件体积较大宕机恢复速度很快慢数据恢复优先级低,数据完整性不如AOF高,记录了执行命令数据…...
通用plantuml 时序图(Sequence Diagram)模板头
通用plantuml文件 startuml participant Admin order 0 #87CEFA // 参与者、顺序、颜色 participant Student order 1 #87CEFA participant Teacher order 2 #87CEFA participant TestPlayer order 3 #87CEFA participant Class order 4 #87CEFA participant Subject order …...
Domino多Web站点托管
大家好,才是真的好。 看到一篇文档,大概讲述的是他在家里架了一台Domino服务器,上面跑了好几个Internet的Web网站(使用Internet站点)。再租了一台云服务器,上面安装Nginx做了反向代理,代理访问…...
防火墙补充NAT
目录 1.iptables保存规则 2.自定义链 3.NAT NAT的实现分为下面类型: SNAT实验操作 DNAT实验操作 1.iptables保存规则 永久保存方法一: iptables -save > /data/iptables_rule //输出重定向备份 iptables -restore < /data/iptables_r…...
配置和管理VLAN
VLAN技术是交换技术的重要组成部分,也是交换机配置的基础。用于把物理上直接相连的网络从逻辑上划分为多个子网。 每一个VLAN 对应一个广播域,处于不同VLAN 上的主机不能通信。 不同VLAN 之间通信需要引入三层交换技术。 对性能局域网的配置和管理主要…...
dtaidistance笔记:dtw_ndim (高维时间序列之间的DTW)
1 数据 第一个维度是sequence的index,每一行是多个元素(表示这一时刻的record) from dtaidistance.dtw_ndim import *s1 np.array([[0, 0],[0, 1],[2, 1],[0, 1],[0, 0]], dtypenp.double) s2 np.array([[0, 0],[2, 1],[0, 1],[0, .5],[0…...
2 文本分类入门:TextCNN
论文链接:https://arxiv.org/pdf/1408.5882.pdf TextCNN 是一种用于文本分类的卷积神经网络模型。它在卷积神经网络的基础上进行了一些修改,以适应文本数据的特点。 TextCNN 的主要思想是使用一维卷积层来提取文本中的局部特征,并通过池化操…...
idea大量爆红问题解决
问题描述 在学习和工作中,idea是程序员不可缺少的一个工具,但是突然在有些时候就会出现大量爆红的问题,发现无法跳转,无论是关机重启或者是替换root都无法解决 就是如上所展示的问题,但是程序依然可以启动。 问题解决…...
【HarmonyOS 5.0】DevEco Testing:鸿蒙应用质量保障的终极武器
——全方位测试解决方案与代码实战 一、工具定位与核心能力 DevEco Testing是HarmonyOS官方推出的一体化测试平台,覆盖应用全生命周期测试需求,主要提供五大核心能力: 测试类型检测目标关键指标功能体验基…...
高频面试之3Zookeeper
高频面试之3Zookeeper 文章目录 高频面试之3Zookeeper3.1 常用命令3.2 选举机制3.3 Zookeeper符合法则中哪两个?3.4 Zookeeper脑裂3.5 Zookeeper用来干嘛了 3.1 常用命令 ls、get、create、delete、deleteall3.2 选举机制 半数机制(过半机制࿰…...
MVC 数据库
MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...
PL0语法,分析器实现!
简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...
鸿蒙DevEco Studio HarmonyOS 5跑酷小游戏实现指南
1. 项目概述 本跑酷小游戏基于鸿蒙HarmonyOS 5开发,使用DevEco Studio作为开发工具,采用Java语言实现,包含角色控制、障碍物生成和分数计算系统。 2. 项目结构 /src/main/java/com/example/runner/├── MainAbilitySlice.java // 主界…...
【C++特殊工具与技术】优化内存分配(一):C++中的内存分配
目录 一、C 内存的基本概念 1.1 内存的物理与逻辑结构 1.2 C 程序的内存区域划分 二、栈内存分配 2.1 栈内存的特点 2.2 栈内存分配示例 三、堆内存分配 3.1 new和delete操作符 4.2 内存泄漏与悬空指针问题 4.3 new和delete的重载 四、智能指针…...
[免费]微信小程序问卷调查系统(SpringBoot后端+Vue管理端)【论文+源码+SQL脚本】
大家好,我是java1234_小锋老师,看到一个不错的微信小程序问卷调查系统(SpringBoot后端Vue管理端)【论文源码SQL脚本】,分享下哈。 项目视频演示 【免费】微信小程序问卷调查系统(SpringBoot后端Vue管理端) Java毕业设计_哔哩哔哩_bilibili 项…...
Linux nano命令的基本使用
参考资料 GNU nanoを使いこなすnano基础 目录 一. 简介二. 文件打开2.1 普通方式打开文件2.2 只读方式打开文件 三. 文件查看3.1 打开文件时,显示行号3.2 翻页查看 四. 文件编辑4.1 Ctrl K 复制 和 Ctrl U 粘贴4.2 Alt/Esc U 撤回 五. 文件保存与退出5.1 Ctrl …...
elementUI点击浏览table所选行数据查看文档
项目场景: table按照要求特定的数据变成按钮可以点击 解决方案: <el-table-columnprop"mlname"label"名称"align"center"width"180"><template slot-scope"scope"><el-buttonv-if&qu…...

