redis常见使用场景与实例
问题(需求):
项目中一些常见问题
1.多节点用户会话保存
2.订单大规模定时取消
3.产品服务访问慢
4.分布式任务重复执行
5.高并发提交:设备状态上报接口
接口高并发
/*** 接到上报* 1* @param params* @return*/
@Override
public int save(CarAccessQueryParams params){CarAccess carAccessExit = getOneCarAccessByCarnum(params.getCarnum());CarAccess carAccessParam = CarAccessQueryParams.convertToEntity(params);String key = String.format("app:%s",params.getCarnum());Long count= redisCache.increment(key);if(count == 1) {redisCache.expire(key, 60);if(carAccessExit==null){carAccessDao.insert(carAccessParam);}else {carAccessDao.updateByCarnum(carAccessParam);}}else if((count>1)&&(carAccessParam.getPicUrl()!=null)){//<1分钟if(carAccessExit!=null){carAccessDao.updateByCarnum(carAccessParam);}else{try {TimeUnit.SECONDS.sleep(1);}catch (Exception e){logger.error(String.format("carAccess save延时失败==%s"),e.getMessage());}carAccessDao.updateByCarnum(carAccessParam);}}return 1;
}
分布式会话
@Configuration
@EnableRedisHttpSession(redisNamespace = "{app}",maxInactiveIntervalInSeconds=1800)
public class RedisConfig {}
延时队列
public abstract class AbstractRedisDelayQueue {protected Logger logger = LoggerFactory.getLogger(AbstractRedisDelayQueue.class);@Autowiredprotected RedisTemplate redisTemplate;/*** 插入任务id* @param taskId 任务id(队列内唯一)* @param time 延时时间(单位 :秒)* @return 是否插入成功*/public boolean addTaskId(String taskId, Integer time) {Calendar instance = Calendar.getInstance();instance.add(Calendar.SECOND, time);long delaySeconds = instance.getTimeInMillis() / 1000;try {String key = setDelayQueueName();boolean zadd = redisTemplate.opsForZSet().add(key, taskId, delaySeconds);return zadd;} catch (Exception e) {logger.warn("AbstractRedisDelayQueue addTaskId异常===",e);e.printStackTrace();return false;}}private void startDelayQueue() {logger.info("AbstractRedisDelayQueue---init----QueueName==> {}", setDelayQueueName());while (true) {try {long now = System.currentTimeMillis() / 1000;// 获取当前时间前的任务列表Set<Object> taskIds = redisTemplate.opsForZSet().rangeByScoreWithScores(setDelayQueueName(), 0, now);// 如果不为空则遍历判断其是否满足取消要求if (!CollectionUtils.isEmpty(taskIds)) {for (Object obj : taskIds) {JSONObject jsonObject = JSONObject.parseObject(JSON.toJSONString(obj));String taskId = jsonObject.get("value").toString();Long num = redisTemplate.opsForZSet().remove(setDelayQueueName(), taskId);// 如果移除成功, 则取消订单if (null != num && num > 0) {invoke(taskId);}}}} catch (Exception e) {logger.error("处理延时任务发生异常,异常原因为 {}", e);} finally {// 间隔一分钟执行一次try {TimeUnit.SECONDS.sleep(60L);} catch (InterruptedException e) {e.printStackTrace();}}}}/*** 最终执行的任务方法* @param taskId 任务id*/public abstract void invoke(String taskId);/*** 要实现延时队列的名字* @return*/public abstract String setDelayQueueName();/*** 项目启动时执行初始化*/@PostConstructpublic void init(){ExecutorService singleThreadExecutor = new ThreadPoolExecutor(1, 1, 300L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(16),new BasicThreadFactory.Builder().namingPattern("start-delay-queue-thread-pool-%d").daemon(true).build(), new ThreadPoolExecutor.AbortPolicy());singleThreadExecutor.execute(()-> {logger.info("startDelayQueue--- {}", Thread.currentThread().getName());startDelayQueue();});singleThreadExecutor.shutdown();}public class CancelOrderDelayQueue extends AbstractRedisDelayQueue {@Autowiredprivate OrderServiceFacade orderServiceFacade;@Overridepublic void invoke(String taskId) {log.info("超时未支付自动取消======================" + taskId);OrderResponse orderResponse = orderServiceFacade.queryOrderByOrderNo(taskId);if(Objects.nonNull(orderResponse)){Optional.ofNullable(orderResponse.getMain()).ifPresent(main -> {if(TradeStatusEnum.CREATE.equals(main.getOrderStatus())){orderServiceFacade.cancleOrderByOrderNo(OrderConstant.ORDER_TYPE_MAIN,main.getOrderNo(),"超时自动关闭");}});}}@Overridepublic String setDelayQueueName() {return RedisKeyConstant.CANCEL_ORDER_DELAY_QUEUE_NAME;}
}
分布式锁
@Scheduled(cron = "0 0 10 * * ?")
public void remindFollowCustomerNumber(){try{log.info("RemindTask remindUnFollow {}",new Date());if (!redisCache.setNX(DsopConstants.REMIND_FLAG_FOLLOW_CUSTOMER_NUMBER,DsopConstants.REMIND_FLAG_VALUE,DsopConstants.REMIND_FLAG_TIMEOUT, TimeUnit.SECONDS)){log.info("任务已执行");return;}List<FollowRemindNumberDTO> customerInfos=customerFollowService.queryRemindCustomerNumber();customerInfos.forEach(c->{wxPushService.pushRemindCustomerNumber(c.getManager(),c.getManagerPhone(),c.getCustomeraNumber(),c.getCustomerbNumber());});}catch (Exception e){log.error("RemindTask remind异常:{}",e.getMessage());}finally {redisCache.del(DsopConstants.REMIND_FLAG_FOLLOW_CUSTOMER_NUMBER);}
}
reids提效工具springcache
@Override@Cacheable(value = "newProductDetailList",key = "#root.target.getFormatKey(#p0)",cacheManager = "cacheManager5Minute")public Page<ItemResponse> quertNewProductDetailListCache(ItemOnsaleRequest itemOnsaleRequest) {CommResponse<Page<ItemResponse>> onsale = productFeign.getOnsale(itemOnsaleRequest);if(onsale.getCode()!=0){throw new BusinessException(onsale.getDesc());}return onsale.getData();
}
5.常见场景
5.1.分布式锁
5.2.缓存
5.3.统一会话
5.4.token认证
6.问题的解决方案代码示例
分布式缓存之缓存击穿+解决⽅案
简介:分布式缓存必考题之缓存击穿+解决⽅案
缓存击穿 (某个热点key缓存失效了)
缓存中没有但数据库中有的数据,假如是热点数据,那key在缓存过期的⼀刻,同时有⼤量的请求,这些请求都会击穿到DB,造成瞬时DB请求量⼤、压⼒增⼤。和缓存雪崩的区别在于这⾥针对某⼀key缓存,后者则是很多key。
预防
设置热点数据不过期
定时任务定时更新缓存
设置互斥锁
SpringCache解决⽅案
缓存的同步 sync
sync 可以指示底层将缓存锁住,使只有⼀个线程可以进⼊计算,⽽其他线程堵塞,直到返回结果更新到缓存中
@Cacheable(value = {“product”},key ="#root.args[0]", cacheManager =
“customCacheManagr”,sync=true)
分布式缓存缓存雪崩+解决⽅案
简介:分布式缓存之缓存雪崩+解决⽅案
缓存雪崩 (多个热点key都过期)
⼤量的key设置了相同的过期时间,导致在缓存在同⼀时刻全部失效,造成瞬时DB请求量⼤、压⼒骤增,引起雪崩
预防
存数据的过期时间设置随机,防⽌同⼀时间⼤量数据过期现象发⽣
设置热点数据永远不过期,定时任务定时更新
SpringCache解决⽅案
设置差别的过时时间
⽐如CacheManager配置多个过期时间维度
配置⽂件 time-to-live
cache:
#使⽤的缓存类型
type: redis
#过时时间
redis:
time-to-live: 3600000
开启前缀,默以为true
use-key-prefix: true
键的前缀,默认就是缓存名cacheNames
key-prefix: MIDDLE_CACHE
是否缓存空结果,防⽌缓存穿透,默以为true
cache-null-values: true
cache-null-values: true
分布式缓存之缓存穿透
+解决⽅案
简介:分布式缓存之缓存穿透+解决⽅案
缓存穿透(查询不存在数据)查询⼀个不存在的数据,由于缓存是不命中的,并且出于容错考虑,如发起为id为“-1”不存在的数据
如果从存储层查不到数据则不写⼊缓存这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义。存在⼤量查询不存在的数据,可能DB就挂掉了,这也是⿊客利⽤不存在的key频繁攻击应⽤的⼀种⽅式。
预防
接⼝层增加校验,数据合理性校验
缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,设置短点的过期时间,防⽌同个key被⼀直攻击
SpringCache解决⽅案
空结果也缓存,默认不配置condition或者unless就⾏
cache:
#使⽤的缓存类型
type: redis
#过时时间
redis:
time-to-live: 3600000
开启前缀,默以为true
use-key-prefix: true
键的前缀,默认就是缓存名cacheNames
key-prefix: MIDDLE_CACHE
是否缓存空结果,防⽌缓存穿透,默以为true
@Override@Cacheable(value = "mallPlantList", key = "0", condition = "#root.target.getFormatKey(#p0) != null", cacheManager = "cacheManager5Minute",unless="#result.size()==0")public List<PlantDTO> getListAll() {PlantQueryForm plantQueryForm = new PlantQueryForm();plantQueryForm.setSize(50);CommResponse<Page<PlantDTO>> search = plantFeign.search(plantQueryForm);if(search.getCode()!=0){throw new BusinessException(search.getDesc());}Page<PlantDTO> data = search.getData();if(data != null){List<PlantDTO> records = data.getRecords();return records;}return new ArrayList<>();}
常用工具:
1.RedisDesktopManager
2.ApacheJMeter
redis常见使用场景与实例相关推荐
- Redis项目应用场景与实例(三):队列(List)
文章目录 一.背景 二.项目需求 三.环境配置 四.项目代码 4.1 Redis工具类增加队列操作方法 4.2 图片上传服务增加Redis队列 五.测试与验证 六.源码 一.背景 在前两篇文章 < ...
- redis常见应用场景
什么是Redis Redis是由意大利人Salvatore Sanfilippo(网名:antirez)开发的一款内存高速缓存数据库.Redis全称为:Remote Dictionary Server ...
- 16 个 Redis 常见使用场景
作者 | 菜鸟编程98K 来源 | https://blog.csdn.net/qq_39938758/article/details/105577370 1.缓存 String类型 例如:热点数据缓 ...
- Redis学习总结(8)——Redis常见使用场景总结
1.缓存 在目前的互联网网站中,缓存几乎是网站都在用的,合理的使用缓存不但可以提升网站访问速度,还可以大大降低数据库的压力.Redis不仅提供了键过期功能,也提供了灵活的键淘汰策略,而且拥有相比mem ...
- 16个 Redis 常见使用场景
目录 缓存 数据共享分布式 分布式锁 全局ID 计数器 限流 位统计 购物车 用户消息时间线timeline 消息队列 抽奖 点赞.签到.打卡 商品标签 商品筛选 用户关注.推荐模型 排行榜 1.缓存 ...
- Redis 常见使用场景
常用命令说明 1.String 适合简单的key,value存储结构,类似于之前使用过的cache,memcached的存储结构.应用场景:短信验证码,配置信息等. 常用命令: 1.1 incr:自增 ...
- 16个Redis常见使用场景总结
五年从程序员到架构师!这是我见过史上最好的程序员职业规划 (下一篇) 16 条 yyds 的代码规范 40 个 SpringBoot 常用注解 别慌,在Java面试的时候,面试官会这样问关于框架的问题 ...
- redis常见使用场景下PHP实现
基于redis字符串string类型的简单缓存实现 <?php //简单字符串缓存$redis = new \Redis(); $redis->connect('127.0.0.1',63 ...
- redis应用场景及实例
redis应用场景及实例 前言 Redis是一个开源的使用ANSI C语言编写.支持网络.可基于内存亦可持久化的日志型.Key-Value数据库,并提供多种语言的API.在这篇文章中,我们将阐述 Re ...
最新文章
- Fast-RCNN解析:训练阶段代码导读
- python的22个基本语法
- CCS中给工程加入C66x CSL库和头文件
- linux daemon守护线程,线程8--守护线程Daemon
- 二项式反演(非详细)
- 红黑树与平衡二叉树_百图详解红黑树,想不理解都难
- 各大厂面试高频的面试题新鲜出炉,你能答上几道?
- SharePoint Framework 企业向导(三)
- 7 行为型模式之 - 状态模式
- jQuery EasyUI 使用笔记
- 转:ffmpeg编码h264
- C++ 循环for 引用 for(string : )
- python 下载股票数据_「Python量化资料」用Python抓取Yahoo、investing平台股票数据
- 给c++程序员的一份礼物——常用工具集
- api与密度转换公式_API、比重换算表
- MongoDBCompass使用教程
- 共享计算机用户帐户限制怎么办,系统之家Win7系统无法共享提示用户账户限制怎么办...
- 用c语言输入年份 月份 显示天数,java输入月份,年份,显示对应月份的天数,...
- python爬取某网站视频
- 邮件营销EDM(Email Direct Marketing) 运营笔记