摘要:MySQL JDBC抽取到底应该采用什么样的方式,且听小编给你娓娓道来。

小编最近在云上的一个迁移项目中被MySQL抽取模式折磨的很惨。一开始爆内存被客户怼,再后来迁移效率低下再被怼。MySQL JDBC抽取到底应该采用什么样的方式,且听小编给你娓娓道来。

Java-JDBC通信原理

JDBC与数据库之间的通信是通过socket完,大致流程如下图所示。Mysql Server ->内核Socket Buffer -> 客户端Socket Buffer ->JDBC所在的JVM

JDBC读取数据的三种模式

方式1:使用JDBC默认参数读取数据

主要分为以下几步:

1)Mysql Server通过OuputStream 向 Socket Server 本地Kennel Buffer 写入数据,这里是一次内存拷贝。

2)当Socket Server 本地Kennel Buffer 有数据,就会通过TCP链路把数据传输到Socket Client 所在机器的Kennel Buffer。

3)JDBC 所在JVM利用InputSream读取本地Kennel Buffer 数据到JVM内存,没有数据时,则读取被阻塞。

接下来就是不断重复1,2,3的过程。问题是,Socket Client 端的JVM在默认模式下读取Kennel Buffer是没有考虑本机内存大小的,有多少读多少。如果数据太大,就会造成FULL GC,紧接着内存溢出。

参考 JDBC API docs,默认模式 Java demo 代码如下

public static Connection getConnection() throws SQLException {Properties connectionProps = new Properties();connectionProps.put("user", this.userName);connectionProps.put("password", this.password);Connection conn = DriverManager.getConnection("jdbc:" + this.dbms + "://" + "127.0.0.1:3306",connectionProps);return conn;
}public static void viewTable(Connection con) throws SQLException {String query = "select COF_NAME, SUP_ID, PRICE, SALES, TOTAL from COFFEES";Connection conn = getConnection();try (Statement stmt = conn.createStatement()) {ResultSet rs = stmt.executeQuery(query);while (rs.next()) {// ...}} catch (SQLException e) {JDBCTutorialUtilities.printSQLException(e);}
}

方式2:游标查询

为了解决方式1爆内存的问题,JDBC提供了一个游标参数,在建立jdbc连接时加上 useCursorFetch=true。设置游标后,JDBC 每次会告诉Server端每次抽取的数据量,避免爆内存。通信过程如下图所示。

方式2游标查询虽然解决了内存溢出的问题,方式2极大的依赖网络质量。当网络时延增大,假设每次通信增加10ms,10万次通信就会多出1000s。这里仅仅是每次发请求的RT,TCP每次发送报文,都要求反馈ACK保证数据可靠性。client每取100行(请求行数可配置),就会有多次通信,进一步放大时延增加导致的效率问题。此外,游标查询下,Mysql无法预知查询的结束时延,为了应对自身的DML操作会在本地建立一个临时空间存放要抽取的数据。因此,游标查询时会有以下几个现象发生,

  1. IOPS飙升,Mysql将数据写入到临时空间,数据传输时从临时空间读取数据,这都会引发大量IO操作。
  2. 磁盘空间飙升,临时空间生命周期存在于整个JDBC读取阶段,直到客户端发起Result.close()时才会被Mysql回收。
  3. CPU和内存有一定比例上升。

有关游标查询的原理可参考博客MySQL JDBC StreamResult通信原理浅析以及JDBC源码,本文不在赘述。

参考 JDBC API docs,游标模式 Java demo 代码如下

 public static Connection getConnection() throws SQLException {Properties connectionProps = new Properties();connectionProps.put("user", this.userName);connectionProps.put("password", this.password);Connection conn = DriverManager.getConnection("jdbc:" + this.dbms + "://" + "127.0.0.1:3306?"+"useCursorFetch=true",connectionProps);return conn;
}public static void viewTable(Connection con) throws SQLException {String query = "select COF_NAME, SUP_ID, PRICE, SALES, TOTAL from COFFEES";Connection conn = getConnection();try (PrepareStatement stmt = conn.prepareStatement(query)) {stmt.setFetchSize(100);ResultSet rs = stmt.executeQuery();while (rs.next()) {// ...}} catch (SQLException e) {JDBCTutorialUtilities.printSQLException(e);}
}

方式3: Stream读取数据

方式1会导致JVM内存溢出,方式2虽然不会FULL GC但是通信效率较低,而且也会导致Mysql服务端IOPS飙升,消耗磁盘空间等问题。因此,我们介绍Stream读取数据 ,流式需要在读取Result前设置

statement.setFetchSize(Integer.MIN_VALUE)

方式3在通信前不会做任何Server-Cient的交互操作,避免通信效率低下。服务端准备好数据写入Server的Kennel Buffer中,这些数据通过TCP链路传输到Client的Kennel Buffer中,紧接着client端inputStream.read()方法被唤醒去读取数据,与方式1不同,client每次只会读取一个package大小的数据,如果一个package不满一行则会再读取一个package。当client消费数据的速度不及数据传输速率时,client端kennel区的数据就会被堆满,紧接着Server端的kennel数据也会堆满进而阻塞了OuputStream。这样,JDBC在Stream模式下就像一个水管连接两个蓄水池,Client和Server达到一个平衡。

对 于JDBC客户端,由于每次都是从kennel读取数据,效率会比方式2高很多,每次读取一小部分数据也不会导致JVM内存溢出。对于服务端,Mysql每次都是往kennel写数据,无需建立临时空间,不涉及IO读取,服务端压力也变小了。当然,方式3也有自己的问题,例如Stream流式时无法cancel,cancel不阻塞等等。

参考 JDBC API docs,网上很多教程需要设置useCursorFetch=true,ResultSet.FETCH_REVERSE等,其实小编研究完JDBC驱动源码后发现,只需要设fetchSize=Integer. MIN_VALUE,其他配置均和默认配置保持一致即可。游标模式 Java demo 代码如下,

 public static Connection getConnection() throws SQLException {Properties connectionProps = new Properties();connectionProps.put("user", this.userName);connectionProps.put("password", this.password);Connection conn = DriverManager.getConnection("jdbc:" + this.dbms + "://" + "127.0.0.1:3306",connectionProps);return conn;
}public static void viewTable(Connection con) throws SQLException {String query = "select COF_NAME, SUP_ID, PRICE, SALES, TOTAL from COFFEES";Connection conn = getConnection();try (PrepareStatement stmt = conn.prepareStatement(query)) {statement.setFetchSize(Integer.MIN_VALUE)ResultSet rs = stmt.executeQuery();while (rs.next()) {// ...}} catch (SQLException e) {JDBCTutorialUtilities.printSQLException(e);}
}

云数据迁移服务在三种模式下的调优

云数据迁移服务(Cloud Data Migration, CDM)是华为云上一个迁移工具,详见CDM官网,小编则通过CDM介绍如何切换三种模式抽取数据。CDM默认使用的是方式3,流式抽取数据,如果需要切换方式1,方式2需额外配置。

配置方式1:默认读取

新建Mysql连接器,建立方法详见官网,在高级属性中增加useCursorFetch=false和adopt.stream=false

配置方式2:游标查询

编辑Mysql连接器,在高级属性中增加useCursorFetch=true和adopt.stream=false。游标查询的大小可通过界面上的Fetch Size调整,默认1000。

配置方式3:流式

CDM默认走的流式,无需额外配置。注意Stream模式下,界面上的`Fetch Size`是不起作用的,原因参考上一节。

性能对比

新建Mysql2Hive的CDM迁移作业,源表101个字段,100万行数据,配置如下

方式1:写入100万行数据耗时1m22s

方式2:同样写入100万行,调整fetchSzie分别为1,10,100,100,最低耗时2m1s

方式3:同样写入100万行,耗时1m5s

小编还测试了100万的小表,明显方式1和方式3的速率要远远高于方式2,另外小编还测试了1000万的大表,方式1爆内存,方式2正常迁移但耗时20分钟以上,而方式3仍然可以在15分钟内跑完。

本文分享自华为云社区《从云数据迁移服务看MySQL大表抽取模式》,原文作者:Leef724。

点击关注,第一时间了解华为云新鲜技术~

从云数据迁移服务看MySQL大表抽取模式相关推荐

  1. mysql大表数据抽取_从云数据迁移服务看MySQL大表抽取模式

    摘要:MySQL JDBC抽取到底应该采用什么样的方式,且听小编给你娓娓道来. 小编最近在云上的一个迁移项目中被MySQL抽取模式折磨的很惨.一开始爆内存被客户怼,再后来迁移效率低下再被怼.MySQL ...

  2. 如何通过cdm连接mysql_CDM云数据迁移服务入门教程

    一.场景介绍 对于新上手的用户,可能还不太清楚如何使用CDM服务,今天就为大家科普下使用CDM服务的具体过程. 使用CDM服务的基本使用流程如下所示: 第一步:创建CDM集群 第二步:创建连接 第三步 ...

  3. 【开源项目】mysql大表数据迁移

    历史数据迁移 项目地址:https://gitee.com/xl-echo/dataMigration 历史迁移解决方案.微服务的架构为基础,使用多种设计模式,如:单利.桥接.工厂.模板.策略等.其中 ...

  4. 云图说|华为云数据复制服务 打造不一样的用户权限迁移

    业界主流的云数据库迁移方案中,无法完全继承源数据库的权限体系. 要同时迁移几套数据库,账号繁多,密码又由不同人维护,来回沟通,费时费力效率低. 华为云数据复制服务DRS推出业内首家用户权限迁移功能,一 ...

  5. 云小课|云小课带你快速掌握云数据迁移CDM

    阅识风云是华为云信息大咖,擅长将复杂信息多元化呈现,其出品的一张图(云图说).深入浅出的博文(云小课)或短视频(云视厅)总有一款能让您快速上手华为云.更多精彩内容请单击此处. 摘要:欢迎来到云数据迁移 ...

  6. MySQL大表历史数据迁移这么做

    历史数据迁移 项目地址:https://gitee.com/xl-echo/dataMigration 历史迁移解决方案.微服务的架构为基础,使用多种设计模式,如:单利.桥接.工厂.模板.策略等.其中 ...

  7. 云数据迁移(Cloud Data Migration,CDM)

    云数据迁移(Cloud Data Migration,CDM) 云数据迁移(Cloud Data Migration,CDM)提供同构/异构数据源之间批量数据迁移服务,帮助客户实现数据自由流动.支持客 ...

  8. 轻松上云系列之二:其他云数据迁移至阿里云

    2019独角兽企业重金招聘Python工程师标准>>> 本文档围绕如何将您其他云厂商上的数据迁移到阿里云,提供了多个场景的实践方案. 文档合集 AWS 数据迁移至阿里云 Amazon ...

  9. HDC.Cloud 2021|华为云数据使服务DAYU,加速数据价值释放

    4月24日-4月26日,华为开发者大会2021(Cloud)(简称HDC.Cloud 2021)将在深圳大学城举办,这是华为ICT基础设施业务面向全球开发者的年度盛会.大会期间,针对重点产品之一--数 ...

最新文章

  1. ‘%.2f‘ 与 ‘{:.2f}‘.format(w) 区别
  2. 别忽视新冠轻症,它会损害你的记忆力
  3. 在Linux中清空某个路径下的特定命名格式的文件夹
  4. 13_新闻客户端界面搭建
  5. 在mysql中字符串的操作_mysql中的字符串操作.doc
  6. 医院药品管理系统源码 HIS系统源码
  7. 贵族机要第二次半修改装备简单分配
  8. PR常用的效果和转场,视频防抖、宽银幕效果、设置默认效果
  9. 系统架构设计师论文范文
  10. MySQL索引数据结构与算法
  11. AndroidStudio入门基础(一)——基础布局
  12. 【PTA】藏头诗:输入四句古诗,输出每句诗的第一个字。
  13. IMDB影评数据集无法通过keras下载
  14. Linux系统配置(磁盘管理)
  15. Angular7 nz-rozzo nz-form
  16. 在桌面计算机找不到光盘驱动器,我照的婚纱,把光盘放进里面了,打开电脑了,在电脑上找不到相片...
  17. v35.03 鸿蒙内核源码分析(时间管理) | 内核基本时间单位是谁 | 百篇博客分析HarmonyOS源码
  18. 使用Python进行立体几何和立体校正的综合教程
  19. matlab gmt,科学网—在Matlab中调用GMT画图 - 徐逸鹤的博文
  20. Matlab滤波器的设计

热门文章

  1. Bootstrap3 滚动监听插件的调用方式
  2. Bootstrap 流式栅格系统
  3. es6 Class 的 Generator函数
  4. run、kill、return、stoprobot、stop
  5. c语言比两个人年龄大小,c语言结构体
  6. MySQL空闲会话_使用Oracle PROFILE控制会话空闲时间
  7. bat随机打开目录下的一个音乐文件_Tomcat bin目录详解
  8. win10系统自带的计算机无法使用吗,Win10如何使用系统自带的硬件设备检测工具?...
  9. Vue.JS学习笔记
  10. VC的若干实用小技巧 (如何干净的删除一个类)