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

天津葛沽 网站建设公司北京城乡建设网站首页

天津葛沽 网站建设公司,北京城乡建设网站首页,网站经营方案 备案,花都网站 建设信科网络背景 先说一下背景,博主所在的业务组有一个核心系统,需要同步两个不同数据源给过来的数据到redis中,但是每次同步之前需要过滤掉一部分数据,只存储剩下的数据。每次同步的数据与需要过滤掉的数据量级大概在0-100w的数据不等。 由…

背景

先说一下背景,博主所在的业务组有一个核心系统,需要同步两个不同数据源给过来的数据到redis中,但是每次同步之前需要过滤掉一部分数据,只存储剩下的数据。每次同步的数据与需要过滤掉的数据量级大概在0-100w的数据不等。

由于是两个数据源,虽然拿到数据后存数据的代码能共用,但是从数据源拿数据由于协议不同所以还是需要分开写,就安排了两位同事完成这个任务。

重启现象

项目上线大半年,线上运行一直很平稳,突然在某一天ops开始报警该系统的两台机器一直在重启,cpu也一直报警,线上cpu监控如下所示:

机器也处于不断重启中:

两台机器表现几乎一致,于是马上重启一台机器,同时联系ops运维同学帮助临时扩容机器,另外一台机器抓取一下当时的运行详情。直接用下面的火线图更明显:

问题分析

可以看到几乎80%的cpu都在做一件事情:ArrayList.removeAll(),根据线程栈找到了线上的代码大致如下:

protected void updateMeta(String redisField, List<String> oldHotels, List<String> newHotels) {//1.diff两次数据涉及的酒店//2.从老数据中删除新数据oldHotels.removeAll(newHotels);
}

可以看到其实cpu大部分的时间都在执行一行代码oldHotels.removeAll(newHotels),所以可以定位到问题所在。

前面提到我们同步数据其实是有两个数据源的,前面任务堵塞的数据源成为数据源1,另一个数据源称为数据源2,那么为什么数据源2没有阻塞呢?经过定位,发现关于数据源2更新数据的代码大致如下:

    private List<String> calculateNeedDeleteHotelSeqByRedis(String tableName, Set<String> thisHotelSeqs) {List<String> saveHotelSeqs = queryHotelSeqs(STRING_OLD_SEQ_TABLE_PREFIX + tableName);if (CollectionUtils.isNotEmpty(saveHotelSeqs)) {// 删除diff数据saveHotelSeqs.removeAll(thisHotelSeqs);return saveHotelSeqs;}

其实两个方法要做的事情都是一样,只是各自的实现方式不一样,但是都有一个关键的步骤就是从新数据集合中批量删除掉老数据。第一个数据源调用的api是ArrayList.removeAll(List list),第二个数据源调用的api是ArrayList.removeAll(Set set),其实两个api都是同一个api,他的定义为:

//java.util.ArrayList#removeAllpublic boolean removeAll(Collection<?> c) {Objects.requireNonNull(c);return batchRemove(c, false);}

所以,可以看出来其实区别就在于传参类型不同,接下来就需要深究为什么传参类型为List集合时会导致cpu上涨。

通过查询相关资料可以得知:在集合数据比较多的情况下, ArrayList.removeAll(Set)的速度远远高于ArrayList.removeAll(List)!从1百万数据中remove掉30万数据,前者需要0.031秒,后者需要1267秒!

结合以下类图:

从图中可以看到,图中相关的集合类(HashSetLinkedListArrayList),除了ArrayList自己实现了removeAll()方法外,其他两个集合都是借助父类(或超父类)的Iterator迭代器进行删除。接下来再来看一下ArrayList类的removeAll()方法的实现。

    private boolean batchRemove(Collection<?> c, boolean complement) {final Object[] elementData = this.elementData;int r = 0, w = 0;boolean modified = false;try {for (; r < size; r++)if (c.contains(elementData[r]) == complement)elementData[w++] = elementData[r];} finally {// Preserve behavioral compatibility with AbstractCollection,// even if c.contains() throws.if (r != size) {System.arraycopy(elementData, r,elementData, w,size - r);w += size - r;}if (w != size) {// clear to let GC do its workfor (int i = w; i < size; i++)elementData[i] = null;modCount += size - w;size = w;modified = true;}}return modified;}

从火线图中可以看出,主要是卡在执行contains()方法,而contains()方法则是调用入参自身的方法,因此需要对比的是HashSet.contains() vs ArrayList.contains()。

ArrayList.contains()

实现很简单,即调用indexOf(),一个一个地遍历查找。最坏时间复杂度为O(总数据量)

HashSet.contains()

我们知道,HashSet的底层是HashMap,因此,实际也就是调用map.containKey()方法。

大家都知道,HashMap的查找速度非常快!因此,到这里,我们也就解释题目的问题。

 解决方案

在数据量比较大的的情况下,使用arrayList.removeAll(subList)时,可以更改为:

  • subList封装为HashSetarrayList.removeAll(new HashSet(subList))
  • arrayList改为LinkedListnew LinkedList(arrayList).removeAll(subList)

最终我们将数据源一的代码修改如下,解决问题:

protected void updateMeta(String redisField, List<String> oldHotels, List<String> newHotels) {//1.diff两次数据涉及的酒店//2.从老数据中删除新数据// 包装为set集合Set<String> newHotelSet = Sets.newHashSet(newHotels);oldHotels.removeAll(newHotels);
}
http://www.yayakq.cn/news/111700/

相关文章:

  • 做网站的公司没有技术网站公司备案有用
  • 广州网站建设泸州外国网站打开很慢
  • 怎么夸客户网站做的好静态网站开发步骤
  • 手表商城网站建设美声广告网站建设
  • 提供网站建设公司哪家好招聘网站建设初衷
  • 微信公众号可以做几个微网站sem竞价代运营公司
  • 衣服网站设计泉州玖码网络设计
  • 网站推广方案中确定目标是指免费logo设计工具
  • 大型网站建设行情网站制作行业越来越难做
  • 国外做兼职网站怎么用ftp上传网站
  • 英文seo 文章发布类网站常用的建一个网站要多少钱
  • 微网站 留言板wordpress 管理员权限
  • 北京网站备案注销中心手机做网站对比路由器做网站
  • 寻花问柳-专注做一家男人的网站猪网站建设的案例教程
  • 外贸公司网站如何做网上推广2019网站建设
  • 如何防范钓鱼网站网站申请免费
  • 品牌网站设计建设开发公司购买的游乐设备计入什么科目
  • 做游戏代练去那个网站崇文手机网站建设
  • 制作网站的图片哪来电子商务网站建设初学视频教程
  • 如何介绍设计的网站模板下载fr后缀网站
  • 网站开发工程师求职信网站管理手册
  • 网站主题制作网站seo流程
  • 网站设计流程是什么柳州网站建设优化推广
  • 外贸网站建设书籍蒙古文网站建设情况
  • 湖州童装网站兰州seo优化入门
  • 在建设部网站如何查询注册信息设计一个好的wordpress主题的10个准则_
  • 个人网站如何做淘宝客服务号微网站怎么做
  • 做个公司网站大概多少钱手机网站模版更换技巧
  • 商城网站开发时间个人 网站备案 幕布
  • 教育行业网站建设审批联合建设官方网站