不想 CRUD 干到老,就来看看这篇 OOM 排查的实战案例!
点击上方蓝色“程序猿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 排查的实战案例!相关推荐
- 不想CRUD干到老,就来看看这篇OOM排查的实战案例!
点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 一.经历概要 程序里有个跑数据的job,这个job的主要功能是往数 ...
- CRUD 干到老也没什么不好,只要你不想上进
总有粉丝问我有什么免费的学习资料分享么? 我会慎重分享,因为我觉得太多的免费资料,都是"坑",有的没啥干货,有的甚至出现很多低级错误,所以建议大家慎重选择,偶尔付费一些没啥损失. ...
- 如果给你一个亿,你想去干嘛?各专业的科研狗是这样回答的……
全世界只有3.14 % 的人关注了 爆炸吧知识 "如果现在给你一个亿, 你想去做什么?" 每天都被穷醒的小天一看到, 立马展开了丰富的想象力: 首先,当然少不了买买买, 将一切之前 ...
- 懒不是傻懒,如果你想少干,就要想出懒的方法。要懒出风格,懒出境界。
今天是我第一次和雅虎的朋友们面对面交流.我希望把我成功的经验和大家分享,尽管我认为你们其中的绝大多数勤劳聪明的人都无法从中获益,但我坚信,一定有个别懒的去判断我讲的是否正确就效仿的人,可以获益匪浅. ...
- 想法越多越贫穷,赚钱的人,都是少想多干
说实话,赚钱是个特别枯燥的过程,而且经常需要去做一些重复,看起来没啥作用的事情. 很多人去创业,为啥失败居多呀? 就是做之前,想象很美好,真的去干了,发现完全不是那么回事. 电影里的创业,一般老板都是 ...
- linux查看nas剩余大小,老徐玩NAS 篇二:我的群晖储存空间哪儿去了——100%破案的教程...
老徐玩NAS 篇二:我的群晖储存空间哪儿去了--100%破案的教程 2019-05-26 23:28:21 74点赞 866收藏 36评论 前言 为了更好的体验Nas的功能,我前段时间终于安耐不住在J ...
- vue2.0实战案例之高级教程-老孟编程
vue2.0实战案例之高级教程 008:vue.js前端框架安装和使用范围. vue版本升级和使用方式,vue和jquery.js,和zepto.js的优劣势分析. vue为什么无法在开发小程序里无法 ...
- Flutter基础篇(2)-- 老司机用一篇博客带你快速熟悉Dart语法
版权声明:本文为博主原创文章,未经博主允许不得转载.https://www.jianshu.com/p/3d927a7bf020 转载请标明出处: https://www.jianshu.com/p/ ...
- 我想成为一名计算机管理员英语作文,我想成为一名老师英语作文4篇
我想成为一名老师英语作文4篇来自小周记热点推荐.我有一个理想,我想成为一名老师,如何写一篇我想成为老师的英语作文呢?下面是小周记 XiaozhouJi.Com小编给大家整理的我想成为一名老师的英语作文 ...
最新文章
- 新生 语不惊人死不休 —— 《无限恐怖》读后有感
- MySQL安装使用的2个问题
- 网络推广是做什么的介绍网站内容更新时需要遵循哪些原则?
- Python中可变数据类型和不可变数据类型
- Linux为什么受欢迎?
- 小甲鱼python课后题简书_Python练习题100道
- Mybatis 一对多 简单映射配置
- 二分分类2.1 二分分类
- 《OSPF和IS-IS详解》一2.4 理解内部BGP
- Scrapy1.0教程 - 目录汇总贴
- 台式计算机摄像头怎么打开,如何打开摄像头,教您Win7摄像头怎么打开
- matlab重叠保留法,【matlab实现】重叠相加法与重叠保留法
- web网页设计——体育气步枪射击主题(5页面)带图片轮播特效(HTML+CSS) ~学生网页设计作业源码
- 智能微型断路器集计量,保护,控制于一体让用电更安全更智能-安科瑞 汤婉茹/孟强荣
- 这家自动驾驶公司在招聘感知实习研究员!
- 数组 Map 使用小结
- 求N个数中的第二大的数字
- Kindle 2 和 iPad,不是零和游戏
- python语法糖的本质_科学网—Python语法糖之:列表解析、集合解析和字典解析 - 陈同的博文...
- 能够改变一生的5句话
热门文章
- python 报错 cannot import name ‘byte_string‘ from ‘Crypto.Util.py3compat‘ 解决方法
- docker mysql 报错 “Too many connections 1040“ 修改最大连接数 未生效 解决方法
- linux rsyslog 系统日志转发
- webrtc 泄漏真实 ip 地址
- python3 判断list是否包含另一个list
- python3 if else 简洁写法 三元运行
- linux c execve, execlp, execvp, execle 执行文件 执行二进制 介绍
- Linux中与命令相关的命令
- 使用1个盘三个5G分区创建12G逻辑卷
- 使用bpf 排查 fd 泄漏