因为使用ArrayList.removeAll(List list)导致的机器重启
背景
先说一下背景,博主所在的业务组有一个核心系统,需要同步两个不同数据源给过来的数据到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秒!
结合以下类图:
从图中可以看到,图中相关的集合类(HashSet
、LinkedList
、ArrayList
),除了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
封装为HashSet
:arrayList.removeAll(new HashSet(subList))
- 将
arrayList
改为LinkedList
:new 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);
}
相关文章:

因为使用ArrayList.removeAll(List list)导致的机器重启
背景 先说一下背景,博主所在的业务组有一个核心系统,需要同步两个不同数据源给过来的数据到redis中,但是每次同步之前需要过滤掉一部分数据,只存储剩下的数据。每次同步的数据与需要过滤掉的数据量级大概在0-100w的数据不等。 由…...

Let‘s Encrypt
创建文件夹 mkdir /usr/local/develop/ 安装Certbot客户端 yum install certbot 首先确保example.com和www.example.com这两个域名通过DNS解析绑定了你的web 服务器的公网 IP 就是说先要完成域名解析到服务器 下面命令会验证 /var/www/example 他会将一些命令文件存在…...

C语言 | Leetcode C语言题解之第24题两两交换链表中的节点
题目: 题解: struct ListNode* swapPairs(struct ListNode* head) {struct ListNode dummyHead;dummyHead.next head;struct ListNode* temp &dummyHead;while (temp->next ! NULL && temp->next->next ! NULL) {struct ListNod…...
【LeetCode热题100】【回溯】电话号码的字母组合
题目链接:17. 电话号码的字母组合 - 力扣(LeetCode) 组合的过程是一个长树的过程,可以用深度遍历实现,每一个数字对应的字符串都是一层,一种字母组合就是一条路径,当递归的深度达到层数就找到了…...
解析mysql的DDL语句生成高斯内表及表字段主键配置
mysql的DDL语句如下: CREATE TABLE gg_zr (id bigint(20) NOT NULL COMMENT 责任信息表主键id,zrdm varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 责任代码,zrmc varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAU…...

ANSYS Electromagnetics Suite 2023 R2 三维电磁(EM)仿真软件下载
Ansys家最新的三维电磁(EM)仿真软件ANSYS Electromagnetics Suite 2023 R2日前发布了,老wu这次分享得有点晚  ̄ω ̄,现在已经将资源上传到了网盘供大家免费下载,同时,为了让大家都能与…...

pbootcms百度推广链接打不开显示404错误页面
PbootCMS官方在2023年4月21日的版本更新中(对应V3.2.5版本),对URL参数添加了如下判断 if(stripos(URL,?) ! false && stripos(URL,/?tag) false && stripos(URL,/?page) false && stripos(URL,/?ext_) false…...

springboot 整合 swagger2
整合步骤 pom 添加依赖 <dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2</artifactId><version>2.9.2</version></dependency><dependency><groupId>io.springfox</groupId>&…...

redis-缓存穿透与雪崩
一,缓存穿透(查不到) 在默认情况下,用户请求数据时,会先在缓存(Redis)中查找,若没找到即缓存未命中,再在数据库中进行查找,数量少可能问题不大,可是一旦大量的请求数据&a…...
K8S临时存储-本地存储-PV和PVC的使用-动态存储(StorageClass)
介绍 容器中的文件在磁盘上是临时存放的,当容器崩溃或停止时容器上面的数据未保存, 因此在容器生命周期内创建或修改的所有文件都将丢失。 在崩溃期间,kubelet 会以干净的状态重新启动容器。 当多个容器在一个 Pod 中运行并且需要共享文件时…...

jeecg-boot安装
我看大家都挺关注,所以集中上传了下代码和相关工具,方便大家快速完成 链接:https://pan.baidu.com/s/1-Y9yHVZ-4DQFDjPBWUk4-A 提取码:op1r 1. 下载代码 下载地址 : JEECG官方网站 - 基于BPM的低代码开发平台(低代码平台_零代…...

Unity面经(自整)——移动开发与Shader
Unity与Android混合开发 为什么使用Flutter构建 Flutter 是 Google 的开源工具包,用于从单个代码库为移动、Web、桌面和嵌入式设备构建应用程序(一套代码跨平台构建app是它最大的优点),并且可以构建高性能、稳定和丰富UI的应用程…...
Nginx实现反向代理、负载均衡、动静分离
1. 什么是Nginx的反向代理? Nginx的反向代理是指Nginx作为服务器的前端,接收客户端的请求,然后将请求转发给后端的真实服务器,并将真实服务器的响应返回给客户端。这种代理方式使得客户端并不知道真实服务器的存在,它…...

【Linux】网络基础(一)
文章目录 一、计算机网络背景1. 网络发展2. 认识“协议” 二、网络协议初识1. 协议分层2. OSI七层模型3. TCP/IP五层(或四层)模型 三、网络传输基本流程1. 同局域网的两台主机通信数据包封装和分用封装分用 2. 跨网络的两台主机通信 四、网络中的地址管理…...
前端小白学习Vue框架(二)
一.属性计算、属性监听、属性过滤 1.认识MVVM V (用户视图界面)通过VM (应用程序) 向Model(数据模型) 取值与赋值的过程! 数据双向绑定 视图改变更新数据,数据改变更新视图 2.属性计算 //在vue实例中通过computed去计算new …...

飞书api增加权限
1,进入飞书开发者后台:飞书开放平台 给应用增加权限 2,进入飞书管理后台 https://fw5slkpbyb3.feishu.cn/admin/appCenter/audit 审核最新发布的版本 如果还是不行,则需要修改数据权限,修改为全部成员可修改。 改完…...

CSS3 平面 2D 变换+CSS3 过渡
个人主页:学习前端的小z 个人专栏:HTML5和CSS3悦读 本专栏旨在分享记录每日学习的前端知识和学习笔记的归纳总结,欢迎大家在评论区交流讨论! 文章目录 ✍一、CSS3 平面 2D 变换💎1 坐标轴💎2 transform 语法…...

【Jenkins】Jenkins自动化工具介绍
目录 技术背景常规的手动打包步骤 Jenkins简介起源与发展Jenkins的核心价值1.自动化1.1代码构建1.2测试自动化1.3自动部署 2.持续集成与持续部署CI/CD的概念如何减少集成问题更快速地发布软件版本 Jenkins优势Jenkins的主要竞争对手Travis CI:CircleCI:GitLab CI: Jenkins与其他…...

课时93:流程控制_函数进阶_综合练习
1.1.3 综合练习 学习目标 这一节,我们从 案例解读、脚本实践、小结 三个方面来学习。 案例解读 案例需求 使用shell脚本绘制一个杨辉三角案例解读 1、每行数字左右对称,从1开始变大,然后变小为1。 2、第n行的数字个数为n个…...

oracle创建整个数据库的只读账户
在源用户readonly 下创建只读用户 reader readonly 的表空间为AA 一、创建只读用户 create user reader identified by 密码 default tablespace AA; 二、授权 grant connect to reader ; 三、获取原账号readonly 的查询权限 select grant select on ||owner||.||object…...

Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件
今天呢,博主的学习进度也是步入了Java Mybatis 框架,目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学,希望能对大家有所帮助,也特别欢迎大家指点不足之处,小生很乐意接受正确的建议&…...
连锁超市冷库节能解决方案:如何实现超市降本增效
在连锁超市冷库运营中,高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术,实现年省电费15%-60%,且不改动原有装备、安装快捷、…...

蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练
前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1):从基础到实战的深度解析-CSDN博客,但实际面试中,企业更关注候选人对复杂场景的应对能力(如多设备并发扫描、低功耗与高发现率的平衡)和前沿技术的…...
是否存在路径(FIFOBB算法)
题目描述 一个具有 n 个顶点e条边的无向图,该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序,确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数,分别表示n 和 e 的值(1…...
Java数值运算常见陷阱与规避方法
整数除法中的舍入问题 问题现象 当开发者预期进行浮点除法却误用整数除法时,会出现小数部分被截断的情况。典型错误模式如下: void process(int value) {double half = value / 2; // 整数除法导致截断// 使用half变量 }此时...

华为OD机考-机房布局
import java.util.*;public class DemoTest5 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseSystem.out.println(solve(in.nextLine()));}}priv…...
【学习笔记】erase 删除顺序迭代器后迭代器失效的解决方案
目录 使用 erase 返回值继续迭代使用索引进行遍历 我们知道类似 vector 的顺序迭代器被删除后,迭代器会失效,因为顺序迭代器在内存中是连续存储的,元素删除后,后续元素会前移。 但一些场景中,我们又需要在执行删除操作…...

Golang——7、包与接口详解
包与接口详解 1、Golang包详解1.1、Golang中包的定义和介绍1.2、Golang包管理工具go mod1.3、Golang中自定义包1.4、Golang中使用第三包1.5、init函数 2、接口详解2.1、接口的定义2.2、空接口2.3、类型断言2.4、结构体值接收者和指针接收者实现接口的区别2.5、一个结构体实现多…...
Python 训练营打卡 Day 47
注意力热力图可视化 在day 46代码的基础上,对比不同卷积层热力图可视化的结果 import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import DataLoader import matplotlib.pypl…...
智能职业发展系统:AI驱动的职业规划平台技术解析
智能职业发展系统:AI驱动的职业规划平台技术解析 引言:数字时代的职业革命 在当今瞬息万变的就业市场中,传统的职业规划方法已无法满足个人和企业的需求。据统计,全球每年有超过2亿人面临职业转型困境,而企业也因此遭…...