点击上方蓝色“程序猿DD”,选择“设为星标”

回复“资源”获取独家整理的学习资料!

作者:三国梦回

来源:cnblogs.com/grey-wolf/p/9179113.html

一、经历概要

程序里有个跑数据的job,这个job的主要功能是往数据库写假数据。

既需要跑历史数据(传给job的日期是过去的时间),也需要能够上线后,实时跑(十秒钟触发一次,传入触发时的当前时间)。

其中一个job比较奇葩点,要写入的数据比较难以随机生成,是产品的同事从互联网上找的数据,比如当前网络上的热门话题,然后导入到数据库中。所以,

我这边随机的时候,不能乱造。因此我的策略是,从数据库将已经存在的那几条真实数据查询出来,然后job中根据随机数,选择其中一条来仿造一条新的,

随机生成新记录的其他字段,再写入数据库中。

我单元测试一直这么跑的,没有任何问题,直到,将定时触发器打开,然后上线运行。悲剧来了。

二、程序大体逻辑

1、job接口定义:

/*** desc:* 造数据的job,可按表来划分。一个表一个job* @author :* creat_date: 2018/6/11 0011* creat_time: 14:46**/
public interface DataProduceJob {
/*** job的初始化* @param date*/
void jobInit(Date date);
/*** 具体的job运行细节*/
void jobDetail(Integer recordNum);
}

job之所以分了上面两个接口,只是因为设计失误,完全可以融合为一个方法。jobInit的内容,后来我改写到job的afterPropertiesSet中了。

(job实现了org.springframework.beans.factory.InitializingBean接口,保证初始化数据只被调用一次,所谓的初始化数据是指:读文件,读数据库之类的准备工作,后续的假数据都从这里面取)

这边是出问题的job的源码:

@Component
public class TopicWebsiteJob extends BaseJob implements DataProduceJob {
@Autowired
private TopicWebsiteMapper topicWebsiteMapper;
private Date date;Random random = new Random();
private List<TopicWebsite> topicWebsites;
/*** 当前job执行时的时间,会作为创建时间写入数据库表** @param date*/
@Override
public void jobInit(Date date) {
this.date = date;topicWebsites = topicWebsiteMapper.selectAll();}
@Override
public void jobDetail() {
for (TopicWebsite website : topicWebsites) {
for (int i = 0; i < 5; i++) {TopicWebsite topicWebsite = new TopicWebsite();topicWebsite.setWebsiteName(website.getWebsiteName());topicWebsite.setIconUrl(website.getIconUrl());topicWebsite.setHotValue((long) random.nextInt(6354147));
//设置时间topicWebsite.setCreateTime(date);topicWebsiteMapper.insert(topicWebsite);}}}
}

2、job的历史数据初始化器

初始化器,主要是用于生成历史数据,用的是随机生成的过去30天内的时间,去new一个job。然后调用job的init,设置date;然后调用job的细节。上面我也说了,没必要搞两个,只是最初设计失误了。

总体逻辑,就是传入日期,然后根据那个日期,去造假数据。

package com..datavisual.quartz.init;
/*** desc:* 用于造初始化数据* @author :* creat_date: 2018/6/11 0011* creat_time: 14:29**/
public interface Initer {
/*** 具体的初始化逻辑,可参考* @return 成功或失败*/
Boolean init();
}

出问题的初始化器的源码:

/*** desc:** @author:* creat_date: 2018/6/11 0011* creat_time: 14:28**/
@Component
public class TopicWebsiteIniter implements Initer {
@Autowired
private TopicWebsiteJob job;
@Override
public Boolean init() {DateTime now = DateTime.now();
//日期循环,30天
for (int a = -29; a < 1; a++) {
for (int b = 0; b < 24; b++) {
int minutes = (int) (Math.random() * 60);Date date = com.ceiec.datavisual.quartz.DateUtils.getNeedTime(b, minutes, 0, a);
if (a == 0 && date.after(now.toDate())) {} else {job.jobInit(date);job.jobDetail(360);}}}
return true;}
}

3、目前为止,运行正常?

到目前为止,运行没什么问题,因为我都是用单元测试的方式去调用上面的initer.init方法。真的吗?

4、加上定时触发机制

这些job,在上线后,还是需要继续运行。具体的间隔,是每十秒触发一次。

code如下:

@Component
public class TopicWebsiteScheduler implements DataProduceScheduler {
private static final Logger logger = LoggerFactory.getLogger(TopicWebsiteScheduler.class);
@Autowired
private TopicWebsiteJob job;@Override
@Scheduled(cron = "0/10 * * * * ?}")
public Boolean schedule() {logger.info("start...");job.jobInit(new Date());job.jobDetail(1);
return true;}
}

5、问题出来了

就上面的代码,上线一运行,因为job比较多,说实话,也没注意一些细节,没去查看数据库的数据条数。我一直以为没啥问题,直到运行了没一会,程序假死了,卡着不动了。

后来将堆转储拿出来分析,才发现,是因为每次init被多次调用了,每次调用都会从表里面查所有数据(一直以为只有10条真实数据)。然后根据这些数据,去生成新的假数据。再插回表里。这时候表里的数据,差不多翻倍了。

再过10s后,再次查询,这次查到20条,然后,又造了20条假数据,写到表里,变成了40条。再过10s后,再次查询,这次查到40条,然后,又造了40条假数据,写到表里,变成了80条。

...

然后就越来越慢,越来越卡。直到发现表里竟然变成了千万条数据,然后将java程序的内存撑爆了。

三、总结

其实这次主要的坑,在于自己设计功力不够,没有考虑清楚。数据库的数据是变化的,而我拿变化的东西作为基准,来生成假数据,再将假数据写入到原表,造成了

表里数据的指数级增长,然后撑爆了内存。

抛开这块不说,比较有意思的是,查找这个bug背后原因的过程,后边单独写。如果喜欢我的文章,记得点赞转发支持一波~~~

往期推荐居然仅用浏览器,就完成了Spring Boot应用的开发与部署!「赠书」贾扬清推荐,国内首本数据竞赛图书使用IntelliJ IDEA查看类图,内容极度舒适Stack Overflow 2020 开发者调查中的 21 条关键结果Serverless 初体验:快速开发与部署一个Hello World
最后,推荐一个专注分享后端面试要点的公众号「后端面试那些事儿」,置顶标星。每日一篇常问的面试问题,秀的一批~扫描下方二维码关注!

不想 CRUD 干到老,就来看看这篇 OOM 排查的实战案例!相关推荐

  1. 不想CRUD干到老,就来看看这篇OOM排查的实战案例!

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 一.经历概要 程序里有个跑数据的job,这个job的主要功能是往数 ...

  2. CRUD 干到老也没什么不好,只要你不想上进

    总有粉丝问我有什么免费的学习资料分享么? 我会慎重分享,因为我觉得太多的免费资料,都是"坑",有的没啥干货,有的甚至出现很多低级错误,所以建议大家慎重选择,偶尔付费一些没啥损失. ...

  3. 如果给你一个亿,你想去干嘛?各专业的科研狗是这样回答的……

    全世界只有3.14 % 的人关注了 爆炸吧知识 "如果现在给你一个亿, 你想去做什么?" 每天都被穷醒的小天一看到, 立马展开了丰富的想象力: 首先,当然少不了买买买, 将一切之前 ...

  4. 懒不是傻懒,如果你想少干,就要想出懒的方法。要懒出风格,懒出境界。

    今天是我第一次和雅虎的朋友们面对面交流.我希望把我成功的经验和大家分享,尽管我认为你们其中的绝大多数勤劳聪明的人都无法从中获益,但我坚信,一定有个别懒的去判断我讲的是否正确就效仿的人,可以获益匪浅. ...

  5. 想法越多越贫穷,赚钱的人,都是少想多干

    说实话,赚钱是个特别枯燥的过程,而且经常需要去做一些重复,看起来没啥作用的事情. 很多人去创业,为啥失败居多呀? 就是做之前,想象很美好,真的去干了,发现完全不是那么回事. 电影里的创业,一般老板都是 ...

  6. linux查看nas剩余大小,老徐玩NAS 篇二:我的群晖储存空间哪儿去了——100%破案的教程...

    老徐玩NAS 篇二:我的群晖储存空间哪儿去了--100%破案的教程 2019-05-26 23:28:21 74点赞 866收藏 36评论 前言 为了更好的体验Nas的功能,我前段时间终于安耐不住在J ...

  7. vue2.0实战案例之高级教程-老孟编程

    vue2.0实战案例之高级教程 008:vue.js前端框架安装和使用范围. vue版本升级和使用方式,vue和jquery.js,和zepto.js的优劣势分析. vue为什么无法在开发小程序里无法 ...

  8. Flutter基础篇(2)-- 老司机用一篇博客带你快速熟悉Dart语法

    版权声明:本文为博主原创文章,未经博主允许不得转载.https://www.jianshu.com/p/3d927a7bf020 转载请标明出处: https://www.jianshu.com/p/ ...

  9. 我想成为一名计算机管理员英语作文,我想成为一名老师英语作文4篇

    我想成为一名老师英语作文4篇来自小周记热点推荐.我有一个理想,我想成为一名老师,如何写一篇我想成为老师的英语作文呢?下面是小周记 XiaozhouJi.Com小编给大家整理的我想成为一名老师的英语作文 ...

最新文章

  1. 新生 语不惊人死不休 —— 《无限恐怖》读后有感
  2. MySQL安装使用的2个问题
  3. 网络推广是做什么的介绍网站内容更新时需要遵循哪些原则?
  4. Python中可变数据类型和不可变数据类型
  5. Linux为什么受欢迎?
  6. 小甲鱼python课后题简书_Python练习题100道
  7. Mybatis 一对多 简单映射配置
  8. 二分分类2.1 二分分类
  9. 《OSPF和IS-IS详解》一2.4 理解内部BGP
  10. Scrapy1.0教程 - 目录汇总贴
  11. 台式计算机摄像头怎么打开,如何打开摄像头,教您Win7摄像头怎么打开
  12. matlab重叠保留法,【matlab实现】重叠相加法与重叠保留法
  13. web网页设计——体育气步枪射击主题(5页面)带图片轮播特效(HTML+CSS) ~学生网页设计作业源码
  14. 智能微型断路器集计量,保护,控制于一体让用电更安全更智能-安科瑞 汤婉茹/孟强荣
  15. 这家自动驾驶公司在招聘感知实习研究员!
  16. 数组 Map 使用小结
  17. 求N个数中的第二大的数字
  18. Kindle 2 和 iPad,不是零和游戏
  19. python语法糖的本质_科学网—Python语法糖之:列表解析、集合解析和字典解析 - 陈同的博文...
  20. 能够改变一生的5句话

热门文章

  1. python 报错 cannot import name ‘byte_string‘ from ‘Crypto.Util.py3compat‘ 解决方法
  2. docker mysql 报错 “Too many connections 1040“ 修改最大连接数 未生效 解决方法
  3. linux rsyslog 系统日志转发
  4. webrtc 泄漏真实 ip 地址
  5. python3 判断list是否包含另一个list
  6. python3 if else 简洁写法 三元运行
  7. linux c execve, execlp, execvp, execle 执行文件 执行二进制 介绍
  8. Linux中与命令相关的命令
  9. 使用1个盘三个5G分区创建12G逻辑卷
  10. 使用bpf 排查 fd 泄漏