开发辅助三(缓存Redisson分布式锁+分页插件)

缓存

缓存穿透:查询一个不存在的数据,由于缓存不命中,将大量查询数据库,但是数据库也没有此记录。
没有将这次查询的null写入缓存,导致了这个不存在的数据每次请求都要到存储层查询,失去了缓存的意义。
解决:null结果缓存,并加入短暂的过期事件

缓存雪崩:设置缓存时采用了相同的过期时间,导致缓存存在某一时刻同时失效,请求全部转发到DB,DB瞬时压力过重
解决:原有的失效时间基础上增加一个随机值,这样缓存的过期时间重复率就会降低。

缓存击穿:对于一些设置了过期时间的key,如果这些key在某些时间点高并发地访问,同时正好失效,那么所有的压力都会到数据库上。
解决:大量并发只让一个去查,其他人等待,查到以后释放锁,其他人获取到锁,先查缓存,就有数据,不用去数据库。
将redis作为缓存工具
在这里插入图片描述

①、模块中导入依赖,并且配置文件application.yml添加reids连接信息IP和端口

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

②、Service业务类中使用

@Autowired
private StringRedisTemplate redisTemplate;@Override
public Map<String,List<Categlo2Vo>> getCatelogJson(){String catelogJSON = redisTemplate.opsForValue().get("catelogJSON");if(!StringUtils.isEmpty(catalogJSON)){//如果redis不存在,则从数据库中查找,然后将数据库查找的结果放入缓存redis中Map<String,List<Catelog2Vo>> catalogJsonFromDb = getCatalogJsonFromDb();return catalogJsonFromDb;}//如果缓存中存在json,则需要转为对象Map<String,List<Catelog2Vo>> result = JSON.parseObject(catalogJSON,new TypeReference<Map<String,List<Catelog2Vo>>>(){});return result;
}//查询数据库
public Map<String,List<Catelog2Vo>> getCatalogJsonFromDb(){synchronized(this){//得到锁以后,应该再去缓存确定一次,如果没有才需要继续查询String catalogJSON = redisTemplate.opsForValue().get("catalogJSON");if(!StringUtils.isEmpty(catalogJSON)){Map<String,List<Catelog2Vo>> result = JSON.parseObject(catalogJSON,new TypeReference<Map<String,List<Catelog2Vo>>>(){});return result;}//查询数据库的业务逻辑……//...//缓存中以json类型存储可以跨平台使用,所以将对象转为json,并放到缓存中String s = JSON.toJSONString(catalogJsonFromDb);redisTemplate.opsForValue().set("catalogJSON",s,1,TimeUnit.DAY);return parent_cid;}}

产生堆外异常OutOfDirectMemoryError
SpringBoot2.0以后默认使用lettuce作为操作redis的客户端,使用netty进行网络通信
lettuce的bug导致netty堆外内存溢出,-Xmx300m
解决方案:①、升级lettuce客户端 ②、切换jedis

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId><exclusions><exclusion><groupId>io.lettuce</groupId><artifactId>lettuce-core<artifactId></exclusion></exclusions>
</dependency>
<dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId>
</denpendency>

分布式锁

redis命令

set locl 1111 NXset lock 1111 EX 300 NX #占坑的同时添加了过期时间

①、若查询数据库业务中出现异常,导致delete锁无法执行,出现死锁情况,则通过添加expire过期时间来释放锁

public Map<String,List<Catelog2Vo>> getCatalogJsonFromDbWithRedisLock(){//占分布式锁(如果不存在,即设置)Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock","111");if(lock){//加锁成功,执行业务redisTemplate.expire("lock",30,TimeUnit.SECONDS);//防止查询数据库业务中出现异常导致的死锁Map<String,List<Catelog2Vo>> dataFromDb = getDataFromDb();//即使业务出现异常,还会删除锁redisTemplate.delete("lock");return dataFromDb}else{ //加锁失败//重试return getCatalogJsonFromDbWithRedisLock();}}

在这里插入图片描述
②、若在执行过期时间的时候出现异常,造成无法释放锁

public Map<String,List<Catelog2Vo>> getCatalogJsonFromDbWithRedisLock(){Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock","1111",300,Time.SECONDS);//占锁的时候同时设置过期时间if(lock){Map<String,List<Catelog2Vo>> dataFromDb = getDataFromDb();//即使业务出现异常,还会删除锁redisTemplate.delete("lock");return dataFromDb}else{return getCatalogJsonFromDbWithRedisLock();}
}

在这里插入图片描述

③、删除锁导致将其他线程锁也删除,通过判断先获取自己的锁值,然后在删除自己的锁

public Map<String,List<Catelog2Vo>> getCatalogJsonFromDbWithRedisLock(){String uuid = UUID.randomUUID().toString();Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock",uuid,300,Time.SECONDS);//占锁的时候同时设置过期时间if(lock){Map<String,List<Catelog2Vo>> dataFromDb = getDataFromDb();String lockValue = redisTemplate.opsForValue().get("lock");if(uuid.equals(lockValue)){redisTemplate.delete("lock");}return dataFromDb;}else{return getCatalogJsonFromDbWithRedisLock();}
}

在这里插入图片描述

④、如果判断之后出现锁过期,而其他线程设置了锁,那么删除的依然是其他线程的锁(加锁、解锁都保证原子性)

public Map<String,List<Catelog2Vo>> getCatalogJsonFromDbWithRedisLock(){String uuid = UUID.randomUUID().toString();Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock",uuid,300,Time.SECONDS);//占锁的时候同时设置过期时间if(lock){Map<String,List<Catelog2Vo>> dataFromDb;try{dataFromDb = getDataFromDb();}finally{//通过lua脚本解锁String script = "if redis.call('get',KEYS[1] == ARGV[1] then return redis.call('del',KEYS[1])) else return 0 end";Long lock1 = redisTemplate.execute(new DefaultRedisScript<Long>(script,Integer.class),Arrays.asList("lock"),uuid);}return dataFromDb;}else{return getCatalogJsonFromDbWithRedisLock();}
}

在这里插入图片描述

分布式锁Redisson

①、导入依赖

<dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.12.0</version>
</dependency>

②、配置类

@Configuration
public class MyRedissonConfig{@Bean(destroyMethod="shutdown")public RedissonClient redisson() throws IOException{Config config = new Config();/**Redis集群模式config.useClusterServers().addNodeAddress("127.0.0.1:7004","127.0.0.1:7001");*///Redis单节点模式config.useSingleServer().setAddress("redis://192.168.56.10:6379");RedissonClient redissonClient = Redisson.create(config);return redissonClient;}
}

③、使用

@Autowired
private RedissonClient redisson;public Map<String,List<Catelog2Vo>> getCatalogJsonFromDbWithRedissonLock(){//获取锁,锁的粒度越小越好RLock lock = redisson.getLock("catalogJson-lock");lock.lock();Map<String,List<Catelog2Vo>> dataFromDb;try{dataFromDb = getDataFromDb();}finally{lock.unlock();}return dataFromDb;
}

缓存一致性

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

整合SpringCache

①、引入依赖

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

②、配置application.properties,配置使用redis作为缓存

spring.cache.type=redis
spring.cache.redis.time-to-live=1000 #设置过期时间,以毫秒为单位
spring.cache.redis.key-prefix=CACHE_
spring.cache.redis.use-key-prefix=true#是否缓存空值(防止缓存穿透)
spring.cache.redis.cache-null-value=true

③、使用

先在启动类开启缓存注解 @EnableCaching

Service实现的业务类中:

@Cacheable(value={"category"},key="'level1Categorys'") //当前方法的结果需要缓存,如果缓存中有,方法不用调用
@Override
public List<CategoryEntity> getLevel1Categorys(){Long l = System.currentTimeMllis();List<CategoryEntity> categoryEntities = baseMapper.selectList();return categoryEntities;
}@CacheEvict(value="category",key="getLevel1Categorys")//修改时,删除缓存
@Transactional
@Override
public void updateCascade(CategoryEntity category){}

自定义缓存配置类

@EnableConfigurationProperties(CacheProperties.class) //开启和配置文件的绑定功能,使得配置文件的内容生效
@Configuration
@EnableCaching
public class MyCacheConfig{/**第一种方式:直接从容器的配置文件获取第二种方式:将其作为方法参数(如下方法的参数)*/
//	@Autowired
//	CacheProperties cacheProperties;//从容器中获取配置@Beanpublic RedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties){RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();//对key和value进行序列化config = config.serializeKeysWith(RedisSerializetionContext.SerializationPair.fromSerializer(new StringRedisSerializer()));config = config.serializeValuesWith(RedisSerializetionContext.SerializationPair.fromSerializer(new StringRedisSerializer(new GenericJackson2JsoRedisSerializer()));//将配置文件中的所有配置生效CacheProperties.Redis redisProperties = cacheProperties.getRedis();if(redisProperties.getTimeToLive()!=null){config = config.entityTtl(redisProperties.getTimeToLive());}if(redisProperties.getKeyPrefix()!=null){config = config.prefixKeysWith(redis.getKeyPrefix());}if(!redisProperties.isCacheNullValues()){config = config.disableCachingNullValues();}if(!redisProperties.isUserKeyPrefix){config = config.disableKeyPrefix();}return config;}
}

分页插件

查询用户分页显示功能

①、myatis分页插件依赖

<dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper</artifactId>
</dependency>

②、配置文件spring-persist-mybtis.xml

<bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactory"><property name="configLocation" value="classpath:mybatis-config.xml"/><property name="mapperLocations" value="classpath:mybatis/mapper/*Mapper.xml"/><property name="dataSource" ref="dataSource"/><!--配置插件--><property name="plugins"><array><!--分页插件--><bean class="com.github.pagehelper.PageHelper"><property name="properties"><props><!--数据库方言--><prop key="dialect">mysql</prop><!--配置页码的合理化修正,在1~总页数之间修正页码--><prop key="reasonable">true</prop></props></property></bean></array></property>
</bean>

③、Mapper文件和Mapper接口

<select id="selectAdminByKeyword" resultMap="BaseResultMap">select id,login_acct,user_pswd,user_name,email,create_timefrom t_adminwhere login_acct like concat("%",#{keyword},"%") or user_name like concat("%",#{keyword},"%") oremail like concat("%",#{keyword},"%")
</select>
List<Admin> selectAdminByKeyword(String keyword);

④、service实现

@Override
public PageInfo<Admin> getPageInfo(String keyword,Integer pageNum,Integer pageSize){//调用PageHelper静态方法开启分页功能PageHelper.startPage(pageNum,pageSize);//执行查询List<Admin> list = adminMapper.selectAdminByKeyword(keyword);//封装到PageInfo对象中return new PageInfo<>(list);
}

⑤、Controller

@RequestMapping("/admin/get/page.html")
public String getPageInfo(@RequestParam(value="keyword",defaultValue="")String keyword,@RequestParam(value="pageNum",defaultValue="1")Integer pageNum,@RequestParam(value="pageSize",defaultValue="5")Integr pageSize,ModelMap modelMap){PageInfo<Admin> pageInfo = adminService.getPageInfo(keyword,pageNum,pageSize);modelMap.addAttribute(CrowdConstant.ATTR_NAME_PAGE_INFO,pageInfo);return "admin-page"
}

⑥、页面中循环遍历

<c:if test="${empty requestScope.pageInfo.list}"><tr><td colspan="6" align="center">没有查询到你要的数据</td></tr>
</c:if>
<c:if test="${!empty requestScope.pageInfo.list}"><c:forEach items="${requestScope.pageInfo.list}" var="admin"><c:forEach>
</c:if>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/315496.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

机器学习(二) -- 数据预处理(1)

系列文章目录 机器学习&#xff08;一&#xff09; -- 概述 机器学习&#xff08;二&#xff09; -- 数据预处理&#xff08;1-3&#xff09; 机器学习&#xff08;三&#xff09; -- 特征工程&#xff08;1-2&#xff09; 未完待续…… 目录 系列文章目录 前言 一、概述…

普中STM32-PZ6806L开发板(HAL库函数实现-PWM呼吸灯)

简介 实现PWM呼吸灯。 主芯片 STM32F103ZET6呼吸灯引脚 : PC7电路原理图 LED8 电路图 LED8 与 主芯片连接图 其他知识 公式 PWM周期公式: Tpwm ( (ARR 1) * (PSC 1) ) / Tclk Tclk为定时器的输入时钟频率 Tout则为定时器溢出时间 ARR为计数周期 PSC为预分频器的值…

数据结构OJ实验14-哈希查找

A. DS哈希查找—线性探测再散列 题目描述 定义哈希函数为H(key) key%11&#xff0c;输入表长&#xff08;大于、等于11&#xff09;。输入关键字集合&#xff0c;用线性探测再散列构建哈希表&#xff0c;并查找给定关键字。 输入 测试次数t 每组测试数据为&#xff1a; …

Python序列之字典

系列文章目录 Python序列之列表Python序列之元组Python序列之字典&#xff08;本篇文章&#xff09;Python序列之集合 Python序列之字典 系列文章目录前言一、字典是什么&#xff1f;二、字典的操作1.创建&#xff08;1&#xff09;通过{}、dict()创建&#xff08;2&#xff0…

Django HttpResponse 响应对象

目录 一、概述二、测试三、属性和方法四、解读 request 参数 一、概述 所谓 HttpRequest 响应就是服务器返回给客户端的数据&#xff0c;HttpRequest 由程序员自己创建&#xff0c;一般他们通过两种方式来创建。 不使用模板&#xff0c;直接调用 HttpResponse()&#xff0c;返…

安装 Node.js、npm

安装 nodejs 安装Node.js的最简单的方法是通过软件包管理器。 Node.js官网&#xff1a;https://nodejs.org/en/download/ cd /usr/local/src/wget -c https://nodejs.org/dist/v18.16.0/node-v18.16.0-linux-x64.tar.xz xz -d node-v18.16.0-linux-x64.tar.xz tar -xf node…

【ArcGIS微课1000例】0085:甘肃省白银市平川区4.9级地震震中位置图件制作

据中国地震台网正式测定,12月31日22时27分在甘肃白银市平川区发生4.9级地震,震源深度10公里,震中位于北纬36.74度,东经105.00度。 文章目录 一、白银市行政区划图1. 县级行政区2. 乡镇行政区二、4.9级地震图件制作1. 震中位置2. 影像图3. 震中三维地形一、白银市行政区划图…

Redis 与 Spring: 解决序列化异常的探索之旅

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

Java 19的未来:新特性、性能优化和更多

目录 前言 新特性的引入 1. 模式匹配的扩展 2. 增强的模式匹配异常处理 3. 基于记录的反射 4. 引入静态方法的接口 性能优化 1. 垃圾收集器的改进 2. 即时编译器的增强 3. 并行处理的改进 Java编程的前景展望 1. 更多的应用场景 2. 更强的生态系统 3.…

Groovy面向对象的使用及元编程方法的调用和拦截

文章目录 面向对象1 类的定义和对象的定义2 对象的属性值3 方法的定义和调用4 接口5 trait的使用6 元编程方法的调用和拦截 面向对象 1 类的定义和对象的定义 ​ 在groovy中新建一个Student的类&#xff0c;选择Groovy class。 ​ Student类的代码内容如下&#xff0c;在这个…

Spring见解 1

1.Spring概述 1.1.Spring介绍 ​ Spring是轻量级Java EE应用开源框架&#xff08;官网&#xff1a; http://spring.io/ &#xff09;&#xff0c;它由Rod Johnson创为了解决企业级编程开发的复杂性而创建 1.2.简化应用开发体现在哪些方面&#xff1f; IOC 解决传统Web开发中…

qt图像绘制QPainter

QPainter 以下是一些常用的 Qt::PenStyle 枚举值&#xff1a; Qt::NoPen&#xff1a;无线条。Qt::SolidLine&#xff1a;实线。Qt::DashLine&#xff1a;虚线&#xff0c;由短划线组成。Qt::DotLine&#xff1a;点线&#xff0c;由点组成。Qt::DashDotLine&#xff1a;点划线&…