Nebula Graph学习篇3_多线程完成6000w+关系数据迁移

2023-05-16

背景

nebula支持excel文件数据迁入,因此xxx系统可以上传从MySQL或其他工具导出的excel文件然后执行映射节点、关系导入。为了解耦和提升用户体验,过程使用kafka异步完成。

对于小数据量的情景是完全没问题的,但是一旦数据量大于100w+,由于excel单页也仅仅支持100w+数据,如6000w+甚至更多则需要拆分60多个excel显然繁琐且不太现实,因此需要实现一种快速方便的方式来完成这个小需求。

场景分析

整个迁移过程大致分为三个阶段

1、怎么尽可能快的将数据查出来?多线程数据合并问题?
2、数据查出来之后格式转化?nebula不支持的特殊字符怎么处理?
3、格式化后的数据如何保存到nebula?插入分组数量多少合适?插入失败如何处理?

1、多线程分段查数据

单表6000w+数据查询,单线程和多线程拿到数据效率哪个高呢?直觉是多线程,但是查询数据库是IO密集型的,而多线程主要是压栈CPU提升CPU密集型任务的效果明显。

实践出真知,本机代码跑一下。为了防止OOM,先用单表数据量6538587测试查询+转化nGQL格式。

单线程查询的思路代码就不说了,主要说多线程思路。代码就不再贴出来了。

// 就是开个线程池,核心线程数量根据业务场景以及服务器cpu个数设置,我这里直接设置的是20个。

// 之后逻辑处理借助[CompleteableFuture异步编排工具API](https://www.liaoxuefeng.com/wiki/1252599548343744/1306581182447650)完成多线程任务的提交supplyAsyc()

// 然后可以链式的处理异常、nGQL格式处理、结果集合并。

// main线程将多个task放入一个list然后遍历get()阻塞等待全部线程任务处理完成即可。
1.1limit分段查询

因为线程数写死20个,因此可以先查询单表数据量的总数count。

然后count % 20 是否有余数来决定每组数量 count % 20 == 0? count / 20 : count / 20 + 1;

后根据id分段即可,如果表id是主键而且连续,直接使用where id <= maxPartId and id >= minPartId即可,我这里为了防止出现键值不连续的表,使用limit+offset来完成,贴下sql

<select id="selectProductBrandByInterval" resultType="xxx" parameterType="java.lang.Integer">
        select
        id as id,
        sku_id as sku_id,
        brand_id as brand_id
        from xxx limit  #{offset},#{limit}
    </select>

测试 6538587数据多线程查询+转化为nGQL处理 耗时间

  • // 单个线程查询 130s
  • // 10个线程查询 110s
  • // 20个线程查询 110 s

效率提升还是有的,而且如果放在多cpu的服务器上效果应该会更明显。回头看看sql写的还是可以在优化的

知道limit和offst原理是先全部取,然后丢到offset前面的部分,这样随着offset的过大,丢弃的也会越多,理论效率也会更低。

为什么 offset 偏大之后 limit 查找会变慢?这需要了解 limit 操作是如何运作的,以下面这句查询为例:

select * from table_name limit 10000,10
这句 SQL 的执行逻辑是
1.从数据表中读取第N条数据添加到数据集中
2.重复第一步直到 N = 10000 + 10
3.根据 offset 抛弃前面 10000 条数
4.返回剩余的 10 条数据

显然,导致这句 SQL 速度慢的问题出现在第二步!这前面的 10000 条数据完全对本次查询没有意义,但是却占据了绝大部分的查询时间!如何解决?首先我们得了解为什么数据库为什么会这样查询。

作者:jaren
链接:https://www.jianshu.com/p/efecd0b66c55
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
1.2优化limit分段查询

当偏移量offset过大的时候,使用limit的效率就不是那么高了,可以进行优化。

(1)如果id键无序,可以使用父查询将in替换成连接查询inner join
(2)如果id键有序,可以使用id>= 、limit

先查找出需要数据的索引列(假设为 id,子查询因为只需要id字段,val会走覆盖索引。不用子查询的索引还需要回表。)再通过索引列查找出需要的数据。

贴下sql

# 父查询使用连接查询inner join ,110s
<select id="selectProductBrandByIntervalImprove" resultType="xxx" parameterType="java.lang.Integer">
        select
            id as id,
            sku_id as sku_id,
            brand_id as brand_id
        from xxx
        inner join
            ( select id from xxx  limit #{offset},#{limit}) b using (id)
    </select>

# 父查询id>=子查询
<select id="selectProductBrandByIntervalImprove" resultType="xxx" parameterType="java.lang.Integer">
        select
            id as id,
            sku_id as sku_id,
            brand_id as brand_id
        from xxx
       where id >= 
            ( select id from xxx  limit #{offset},1) limit #{limit}
    </select>

效率,在Navicat禁用cache效率提升不少,但是java来跑效果相差不大。

  • // 20个线程优化limit(父查询使用inner join)查询 —》110s
  • // 20个线程优化limit(父亲查询id有序,直接>=子查询)—》113 s

2、合并结果完成迁移

每个线程任务查询出来数据,然后转化为List<String> ,需要注意的就是nebula不支持特殊字符的替换(如单引号、逗号、转义斜杠、中英文括号等都要替换,而且建议nGQL语句插入字符串使用单引号而不是双引号,防止转义插入失败)

每个item就是一个1000条记录的nGQL插入组。多线程合并,需要用线程安全的CopyOnWriteList集合。当然在600w+数据的时候整个查询、转化、插入都没问题,在后续单表6000w+数据的时候出现问题。

只有部分线程能查询成功,大概1000w+数据,而且转化nGQL过程直接抛出 heap OOM异常。

使用jps、jmap、jconsole分析内存,修改默认的最大堆内存从4G改为-Xmx6044m,发现能到2000w左右的查询就又OOM了,因此在本机内存条件有限的情况下只能另外想办法了。

结合业务可以推断出,新生代频繁GC,老年代内存过高,导致OOM,而老年代内存不断波动结合业务分析应该是CopyOnWriteList的原因,每个线程查询结果都要汇总到CopyOnWriteList。

根据数据初步分析,一个Java Object占的内存大小应该为16Bytes,加上对象中的String 成员属性5个,共占 4Bytes * 5 + 16Bytes = 36Bytes。6000w+个对象需要:6000w+ * 36bytes / 1024 / 1024 = 2059M,大致也就是2G多,而且还有List<String>集合和CopyOnWriteList的备份复制等需要大量内存。

而默认的-Xmx1024参数指定的堆内存只有1G,显然不够用,频繁的发生400多次young gc 对象都堆积到了老年代。

在这里插入图片描述

最后靠着根据id分组,每次处理1000w+数据,共跑了6次串行才解决。

下面是使用JVM内存分析工具进行分析的一些命令步骤。

累计导入数量
在这里插入图片描述

3、JVM内存分析

一、工具

1、jps:查看java进程号pid

2、jconsole:可视化界面,查看内存,线程数等。

3、jmap:生成dump文件
# 或者可以手动的直接生成dump文件,使用mat分析或者在线网站
# 拿到dump文件下一步就是分析,由于电脑上没有JDK环境,下载的MAT工具也报错。
# 所以可以使用在线的一个dump分析网站:https://heaphero.io/index.jsp或者是https://gceasy.io/index.jsp

jmap -dump:format=b,file=heap.dump 8544

Dumping heap to D:\myidea_projects\data-conversion\data-conversion\heap.bin ...
Heap dump file created [1141143851 bytes in 10.996 secs]

二、参数

# 1、使其发生OOM时候生成dump文件
# 让JVM在遇到OOM(OutOfMemoryError)时生成Dump文件
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/path/heap/dump

# 2、打印GC日志
-XX:+PrintGCDetails
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Nebula Graph学习篇3_多线程完成6000w+关系数据迁移 的相关文章

  • C语言线程基本函数

    学习笔记 xff1a C语言线程基本函数 学习内容 xff1a 线程常用基本函数 xff1a pthread create 创建线程pthread exit 退出当前线程pthread join 等待其他线程结束pthread self 自
  • 《大话设计模式》笔记——简单工厂模式

    前言 我 xff08 长胖的阿诏 xff09 是新入行的嵌入式程序员 xff0c 所以用C语言做示例演示 xff0c 我看到书上都是 C 语言的代码 xff0c 所以我只能先领会精神 xff0c 再通过C语言复刻 在我的资源里好像没有见过用
  • 《大话设计模式》笔记——策略模式

    策略模式 34 我 34 的理解 策略模式 是指同一个对象在不同情况下的策略行为有所差异 xff0c 继续以之前的四则运算为例 加 减 乘 除 就是两个参数在不同情况下计算过程的差异性行为 所以在某种程度上 xff0c 策略模式可能比简单工
  • md文件目录生成器

    md文件目录生成器 目录 md文件目录生成器 md文件目录生成器 step1 下载脚本文件 step1 下载脚本文件 step2 生成脚本文件 step2 生成脚本文件 step3 设置环境变量 step3 设置环境变量 step4 可以用
  • Python __file__ 详解

    这个功能纠结了一下午 xff0c 做了测试以后总算是明白了 file 表示显示文件当前的位置 但是 xff1a 如果当前文件包含在sys path里面 xff0c 那么 xff0c file 返回一个相对路径 xff01 如果当前文件不包含
  • 48.HTTP基本认证与摘要认证

    文章目录 基本认证摘要认证 转载请注明原始出处 xff1a http blog csdn net a464057216 article details 52705855 后续此博客不再更新 xff0c 欢迎大家搜索关注微信公众号 测开之美
  • CircleProgressBar 圆形进度条,支持多种属性

    效果图 xff1a xff0c 直接从新项目里面摘出来的 xff0c 给自己做个记录 所以就不多加说明 xff0c 1 自定义控件 xff1a 网上摘录修改 public class CircleProgressBar extends Vi
  • c语言入门这一篇就够了-学习笔记(一万字)

    内容来自慕课网 xff0c 个人学习笔记 加上了mtianyan标签标记知识点 C语言入门 gt Linux C语言编程基本原理与实践 gt Linux C语言指针与内存 gt Linux C语言结构体 https www imooc co
  • GPS接收机(一)概述

    概述 接下来的几篇博客包括如下内容 1 xff0c 圆极化天线 xff1a 包括圆极化天线的设计 xff0c 场路协同仿真 xff08 电磁场和电路 xff09 xff0c 相位中心的计算 2 xff0c 低噪放 xff1a 包括低噪放的设
  • ERROR: invalid message type: fl_com/sensor_connect_state. If this is a valid message type, perhaps y

    ERROR invalid message type fl com sensor connect state If this is a valid message type perhaps you need to type rosmake
  • libcurl进行post

    libcurl进行post main函数 xff0c 初始化和清理curl 全局初始化curl curl global init CURL GLOBAL ALL std string url 61 34 http xxxx 34 std s
  • STL几个容器的比较

    vector xff1a 连续内存 xff0c 随机访问数据成员快 xff0c 但是频繁的插入 xff08 需要移动要插入的元素的后面的所有元素 xff09 或者扩容 vector扩容后会清掉原来的数据 xff0c 拷贝到新的申请的大的内存
  • STL注意问题

    1 由于继承的存在 xff0c 拷贝会导致分割 那就是说 xff0c 如果你以基类对象建立一个容器 xff0c 而你试图插入派生类对象 xff0c 那么当对象 xff08 通过基类的拷贝构造函数 xff09 拷入容器的时候对象的派生部分会被
  • CAN总线通信协议详讲

    CAN简介 CAN是Controller Area Network 的缩写 xff08 以下称为CAN xff09 xff0c 是ISO国际标准化的串行通信协议 由德国电气商博世公司在1986 年率先提出 此后 xff0c CAN 通过IS
  • gazebo仿真——controller配置(transmission/hardwareInterface标签)

    参考roswiki controller官方说明 本文作为古月大神的补充ROS探索总结 xff08 三十一 xff09 ros control 为了在gazebo中实现机器人关节的控制 xff0c 需要求建立一个controller控制器
  • NDK--CMakeLists配置第三方so库

    当我们创建一个NDK工程时 xff0c 会自动创建一个CMakeLists txt的文件 xff0c 在AS中c 43 43 的编译器是使用LLVM xff0c 规则为cmake xff0c 今天来学习下cmake的基本套路 首先 xff0
  • postman插件下载安装教程(详细)

    一 前言 postman是一款强大网页接口调试工具 xff0c 我们在平时开发过程中经常会使用到 xff0c 一般使用最多的是postman的客户端 xff0c 实际上postman在谷歌浏览器上也提供了插件 xff0c 可以不必要安装客户
  • CMake交叉编译简单教程

    首先要安装cmaek 然后安装交叉编译链 一 CMake简介 xff1a CMake是一个跨平台的安装 编译 工具 可以通过简单的语句来描述所有平台的安装 编译过程 他能够输出各种各样的 makefile 或者 project 文件 二 C
  • 锂电池的常见接口

    我们在做一些小型化便携式设备的时候 xff0c 经常会用到锂电池 xff0c 常见的锂电池接口如图 xff1a
  • Ubuntu14.04_ROS学习笔记(7) odroid板上操作系统和电脑端主从连接

    4 29日 xff0c 距离上次写过于odroid ROS的博客已经过去近4周 xff0c 在这四周发生了很多曲折事 xff0c 研究生的调剂和面试问题 xff0c 导师双向选择也出现了问题 xff0c 调档问题 xff0c 然后和GF出去

随机推荐