当前位置: 首页 > news >正文

关于Redis(热点数据缓存,分布式锁,缓存安全(穿透,击穿,雪崩));

热点数据缓存:

为了把一些经常访问的数据,放入缓存中以减少对数据库的访问频率。从而减少数据库的压力,提高程序的性能。【内存中存储】成为缓存;

缓存适合存放的数据:
  1. 查询频率高且修改频率低

  2. 数据安全性低

作为缓存的组件:
  1. redis组件

  2. memory组件

  3. ehcache组件

Redis实现缓存功能
涉及的内容:

spring缓存组件 Redis数据库 AOP面向切面编程

实现:
Spirng缓存组件
//开启缓存注解
@EnableCaching
public class CalassNameApplication {public static void main (String[] arge) {SpringApplication.run(CalssNameApplication.class,args);}
}
添加注解
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {RedisSerializer<String> redisSerializer = new StringRedisSerializer();Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);//解决查询缓存转换异常的问题ObjectMapper om = new ObjectMapper();om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);jackson2JsonRedisSerializer.setObjectMapper(om);// 配置序列化(解决乱码的问题),过期时间600秒RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofSeconds(600)) //缓存过期10分钟 ---- 业务需求。.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))//设置key的序列化方式.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer)) //设置value的序列化.disableCachingNullValues();RedisCacheManager cacheManager = RedisCacheManager.builder(factory).cacheDefaults(config).build();return cacheManager;
}
实现缓存功能
//所在的类和注入
@Service
public class ClazzServiceImpl implements ClazzService {
​@Autowiredprivate ClazzDao clazzDao;@Autowiredprivate RedisTemplate<String, Object> redisTemplate;
查询
  1. 查询缓存中是否存在名称为cacheNames::key的值

  2. 如果存在则方法不会执行

  3. 如果不存在则执行方法体并把方法的返回结果放入缓存cacheNames::key

//Cacheable:表示查询时使用的注解。
//cacheNames:缓存的名称
//key:缓存的唯一表示值
@Cacheable(cacheNames ={ "clazz"}, key = "#id")
@Override
public Clazz getById(Integer id) {//查询数据库Clazz clazz = clazzDao.selectById(id);return clazz;
}
修改
  1. 先执行方法体

  2. 把方法的返回结果放入缓存中

//CachePut:表示修改时使用的注解.
//cacheNames:缓存的名称
//key:缓存的唯一表示值
@CachePut(cacheNames = "clazz", key = "#clazz.cid")
public Clazz update(Clazz clazz) {//修改数据库int i = clazzDao.updateById(clazz);return clazz;
}
删除
  1. 先执行方法体

  2. 把缓存中名称为cacheNames::key的值删除

//CacheEvict:表示删除时使用的注解
//cacheNames:缓存的名称
//key:缓存的唯一表示值
@CacheEvict(cacheNames = "clazz", key = "#cid")
@Override
public int delete(Integer cid) {int i = clazzDao.deleteById(cid);return i;
}
原理:
@Service
public class ClazzServiceImpl implements ClazzService {
​@Autowiredprivate ClazzDao clazzDao;@Autowiredprivate RedisTemplate<String, Object> redisTemplate;@Overridepublic Clazz getById(Integer id) {//1.查询redis缓存是否命中ValueOperations<String, Object> forValue = redisTemplate.opsForValue();Object o = forValue.get("clazz::" + id);//表示缓存命中if(o!=null){return (Clazz) o;}//查询数据库Clazz clazz = clazzDao.selectById(id);if(clazz!=null){forValue.set("clazz::" + id,clazz);}return clazz;}
​@Overridepublic Clazz save(Clazz clazz) {int insert = clazzDao.insert(clazz);return clazz;}
​@Overridepublic Clazz update(Clazz clazz) {//修改数据库int i = clazzDao.updateById(clazz);if(i>0){//修改缓存redisTemplate.opsForValue().set("clazz::"+clazz.getCid(),clazz);}return clazz;}
​@Overridepublic int delete(Integer cid) {int i = clazzDao.deleteById(cid);if(i>0){//删除缓存redisTemplate.delete("clazz::"+cid);}return i;}
}

分布式锁:

自理解:

分布式:一个网站部署多台服务器

锁:保证线程安全(将并行改为串行)只允许同时只有一个可以访问;

分布式锁:通过共享的锁保证每台服务器都能共享到同一个实时数据(进行操作);

涉及内容:

syn和lock锁 nginx代理群 Redis数据库 看门狗redisson

使用工具:

Jmeter压测工具 nginx代理

Jmeter:

模拟高并发工具

使用:
分布式锁实现:
@Service
public class StockService {
​@Autowiredprivate StockDao stockDao;@Autowiredprivate RedissonClient redisson;
​//public String decrement(Integer productid) {RLock lock = redisson.getLock("product::" + productid);lock.lock();try {//根据id查询商品的库存: 提前预热到redis缓存中int num = stockDao.findById(productid);if (num > 0) {//修改库存---incr---定时器[redis  数据库同步]stockDao.update(productid);System.out.println("商品编号为:" + productid + "的商品库存剩余:" + (num - 1) + "个");return "商品编号为:" + productid + "的商品库存剩余:" + (num - 1) + "个";} else {System.out.println("商品编号为:" + productid + "的商品库存不足。");return "商品编号为:" + productid + "的商品库存不足。";}}finally {lock.unlock();}}
}
原理:
  1. 加入syn和lock锁;

    syn和lock虽然解决了并发问题,但是项目部署时可能要部署集群模式。

     public String decrement(Integer productid) {//根据id查询商品的库存int num = stockDao.findById(productid);synchronized (this) {
    ​if (num > 0) {//修改库存stockDao.update(productid);System.out.println("商品编号为:" + productid + "的商品库存剩余:" + (num - 1) + "个");return "商品编号为:" + productid + "的商品库存剩余:" + (num - 1) + "个";} else {System.out.println("商品编号为:" + productid + "的商品库存不足。");return "商品编号为:" + productid + "的商品库存不足。";}}}

  1. nginx集群部署并使用redis解决分布式锁文件;

    打开并使用nginx代理集群exe文件;

    nginx(详细操作见linux笔记内):
    代理集群使用
    配置文件:nginx/conf/nginx.conf
    @Service
    public class StockService {
    ​@Autowiredprivate StockDao stockDao;@Autowiredprivate StringRedisTemplate redisTemplate;//public String decrement(Integer productid) {ValueOperations<String, String> opsForValue = redisTemplate.opsForValue();//1.获取共享锁资源Boolean flag = opsForValue.setIfAbsent("product::" + productid, "1111", 30, TimeUnit.SECONDS);//表示获取锁成功if(flag) {try {//根据id查询商品的库存int num = stockDao.findById(productid);if (num > 0) {//修改库存stockDao.update(productid);System.out.println("商品编号为:" + productid + "的商品库存剩余:" + (num - 1) + "个");return "商品编号为:" + productid + "的商品库存剩余:" + (num - 1) + "个";} else {System.out.println("商品编号为:" + productid + "的商品库存不足。");return "商品编号为:" + productid + "的商品库存不足。";}}finally {//释放锁资源redisTemplate.delete("product::"+productid);}}else{//休眠100毫秒 在继续抢锁try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}return decrement(productid);}}
    }

  1. redis超时问题[业务代码执行时间超过了上锁时间]

    使用第三方redisson插件
    <!--引入redisson依赖,看门狗-->
    <dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.24.3</version>
    </dependency>
    //配置文件
    @Configuration
    public class RedissonConfig {
    ​@Beanpublic RedissonClient redisson(){Config config = new Config();
    //        //连接的为redis集群
    //        config.useClusterServers()
    //                // use "rediss://" for SSL connection
    //                .addNodeAddress("redis://127.0.0.1:7181","","","")
    //        ;//连接单机config.useSingleServer().setAddress("redis://172.16.7.18:6379");RedissonClient redisson = Redisson.create(config);return redisson;}
    }
    代码实现:
    @Service
    public class StockService {
    ​@Autowiredprivate StockDao stockDao;@Autowiredprivate RedissonClient redisson;
    ​//public String decrement(Integer productid) {RLock lock = redisson.getLock("product::" + productid);lock.lock();try {//根据id查询商品的库存: 提前预热到redis缓存中int num = stockDao.findById(productid);if (num > 0) {//修改库存---incr---定时器[redis  数据库同步]stockDao.update(productid);System.out.println("商品编号为:" + productid + "的商品库存剩余:" + (num - 1) + "个");return "商品编号为:" + productid + "的商品库存剩余:" + (num - 1) + "个";} else {System.out.println("商品编号为:" + productid + "的商品库存不足。");return "商品编号为:" + productid + "的商品库存不足。";}}finally {lock.unlock();}}
    }

问题集:

使用StringRedisTemplate调用get方法获取map数据,使用String接出现

org.springframework.data.redis.RedisSystemException:Error in execution; nested exception is io.lettuce.core.RedisCommandExecutionException: WRONGTYPE Operation against a key holding the wrong kind of value

DefaultSerializer需要一个可序列化的有效负载,但接收到一个类型的对象

java.lang.IllegalArgumentException: DefaultSerializer requires a Serializable payload but received an object of type [com.day51.entity.MyEntity]

缓存安全:

什么是缓存穿透以及如何解决?

缓存穿透: 查询的数据在数据库中不存在缓存中也不存在,这时有人恶意访问这种数据,请求到达数据库。

解决方案 :

第一步:在controller层校验数据。对一些不合法的数据过滤掉.

第二步: 使用bloom布隆过滤器。

第三步: 存放一个空对象,并且设置过期时间不能超过5分钟。

什么是缓存击穿以及如何解决?

缓存击穿: 数据库中存在,但是缓存中该数据过期了。这是有大量的请求访问该过期的数据。压力顶到数据库。

解决方案: [1]使用互斥锁 [2]设置永不过期。

什么是缓存雪崩以及如何解决?

缓存雪崩: 当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,也会给后端系统(比如DB)带来很大压力。

解决方案: [1] 设置散列的过期时间。 [2]预热数据 [3]搭建redis集群

http://www.lryc.cn/news/408130.html

相关文章:

  • 【北京迅为】《i.MX8MM嵌入式Linux开发指南》-第三篇 嵌入式Linux驱动开发篇-第四十七章 字符设备和杂项设备总结回顾
  • C#初级——枚举
  • Linux 动静态库
  • 微信小游戏之 三消(一)
  • 软件测试---Linux
  • 数据库之数据表基本操作
  • 利用OSMnx求路网最短路径并可视化(二)
  • 双向门控循环神经网络(BiGRU)及其Python和MATLAB实现
  • 【BUG】已解决:ERROR: Failed building wheel for jupyter-nbextensions-configurator
  • Unity UGUI 之 自动布局组件
  • 网络基础之(11)优秀学习资料
  • QT自定义无边框窗口(可移动控制和窗口大小调整)
  • Typora 【最新1.8.6】版本安装下载教程 (轻量级 Markdown 编辑器),图文步骤详解,免费领取(软件可激活使用)
  • RxJava 面试题及其答案
  • 【Rust】所有权OwnerShip
  • qt总结--翻金币案例
  • 最清楚的 BIO、NIO、AIO 详解!
  • 八股文学习第二天| HTTP请求报文和响应报文是怎样的,有哪些常见的字段?,HTTP有哪些请求方式?,GET请求和POST请求的区别?
  • C++初阶学习第四弹——类与对象(中)
  • 【计算机网络】期末实验答辩
  • 一步步教你学会如何安装VMare虚拟机(流程参考图)
  • WebGoC题解(14) 151.(2017dloi小乙)第5题 巧克力甜度(sweet)
  • 深入探索PHP框架:Symfony框架全面解析
  • 内卷的利与弊
  • 用Java手写jvm之实现查找class
  • 【React】组件:全面解析现代前端开发的基石
  • java学习--包装类
  • Python Django功能强大的扩展库之channels使用详解
  • 推荐3款将相片变为动漫风格的免费AI工具推荐
  • 【职业学习】高效工作法