点击上方“方志朋”,选择“设为星标”

回复”666“获取新整理的面试文章

一、经历概要

程序里有个跑数据的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的源码:

package com.ceiec.datavisual.quartz.job;import com.ceiec.common.utils.FileUtils;
import com.ceiec.common.utils.MathUtils;
import com.ceiec.datavisual.dao.GpsLocationSampleMapper;
import com.ceiec.datavisual.dao.TopicAccountMapper;
import com.ceiec.datavisual.dao.TopicMapper;
import com.ceiec.datavisual.dao.TopicWebsiteMapper;
import com.ceiec.datavisual.model.GpsLocationSample;
import com.ceiec.datavisual.model.Topic;
import com.ceiec.datavisual.model.TopicAccount;
import com.ceiec.datavisual.model.TopicWebsite;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Random;@Component
public class TopicWebsiteJob extends BaseJob implements DataProduceJob {@Autowiredprivate TopicWebsiteMapper topicWebsiteMapper;private Date date;Random random = new Random();private List<TopicWebsite> topicWebsites;/*** 当前job执行时的时间,会作为创建时间写入数据库表** @param date*/@Overridepublic void jobInit(Date date) {this.date = date;topicWebsites = topicWebsiteMapper.selectAll();}@Overridepublic 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();
}

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

package com.ceiec.datavisual.quartz.init;
import com.ceiec.datavisual.quartz.job.TopicWebsiteJob;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import java.util.Date;/*** desc:** @author: * creat_date: 2018/6/11 0011* creat_time: 14:28**/
@Component
public class TopicWebsiteIniter implements Initer {@Autowiredprivate TopicWebsiteJob job;@Overridepublic 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如下:

package com..datavisual.quartz.schedule;import com..datavisual.quartz.job.TopicWebsiteJob;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;import java.util.Date;@Component
public class TopicWebsiteScheduler implements DataProduceScheduler {private static final Logger logger = LoggerFactory.getLogger(TopicWebsiteScheduler.class);@Autowiredprivate 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背后原因的过程,后边单独写。如果喜欢我的文章,记得点赞转发支持一波~~~

作者:三国梦回

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

热门内容:我们已经不用AOP做操作日志了!Spring Boot+JWT+Shiro+MyBatisPlus 实现 RESTful 快速开发后端脚手架
MyBatis动态SQL(认真看看, 以后写SQL就爽多了)
选型必看:RabbitMQ 七战 Kafka,差异立现为什么阿里规定需要在事务注解@Transactional中指定rollbackFor?最近面试BAT,整理一份面试资料《Java面试BAT通关手册》,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。获取方式:点“在看”,关注公众号并回复 666 领取,更多内容陆续奉上。明天见(。・ω・。)ノ♡

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

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

    点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 作者:三国梦回 来源:cnblogs.com/grey- ...

  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. python怎么建立画板_Python基于opencv实现的简单画板功能示例
  2. python决策树怎么选择_【机器学习+python(8)】分类决策树的介绍与实现
  3. C#基础系列 - 抽象类及其方法的学习
  4. php利用ajax文件上传,如何在PHP中利用AjaxForm实现一个文件上传功能
  5. Linux 命令解压缩
  6. pig:group by之后的其它统计方法一
  7. 《团队作业第三、第四周》五小福团队作业--Scrum 冲刺阶段--Day7
  8. CTR 预估模型简介--深度学习篇
  9. 什么是数据库存储过程?
  10. python控制窗口_python小笔记-控制窗口的现实和隐藏
  11. Burp Suite爆破Basic认证密码
  12. EXCEL表格-按条件求和、求平均值、求个数详解
  13. 简易实现AI虚拟鼠标—手势控制鼠标
  14. total uninstall 7(卸载清除工具) v7.00
  15. iOS 关于sim卡
  16. MySQL数据库中如何查询分组后每组中的最后一条记录?
  17. LMS与RLS算法学习笔记
  18. 计算机磁盘扫描教程,win7系统下关闭“检查磁盘”和“扫描并修复”提示的方法...
  19. Android - 批量发送短信的实现方式
  20. MyBatis——》转义字符(大于,小于,大于等于,小于等于)

热门文章

  1. [Angular JS教程] HeroService: getHeroes failed: undefined 问题解决方法
  2. OpenCV编译viz模块
  3. scrapy模拟用户登录
  4. 使用JavaScript实现在页面上所有内容加载完之前一直显示loading...页面
  5. JAVA SHA1 加密 对应 c# SHA1 加密
  6. C++构造函数(一)
  7. [002] The Perks of Being a Wallflower - 读后记
  8. Freemarker详细解释
  9. OD使用教程6 - 调试篇06|解密系列
  10. 【直播】林锦弘:CV赛事高分经验分享