共计 2668 个字符,预计需要花费 7 分钟才能阅读完成。
摘要:MySQL JDBC 抽取到底应该采纳什么样的形式,且听小编给你娓娓道来。
小编最近在云上的一个迁徙我的项目中被 MySQL 抽取模式折磨的很惨。一开始爆内存被客户怼,再起初迁徙效率低下再被怼。MySQL JDBC 抽取到底应该采纳什么样的形式,且听小编给你娓娓道来。
1.1 Java-JDBC 通信原理
JDBC 与数据库之间的通信是通过 socket 完,大抵流程如下图所示。Mysql Server -> 内核 Socket Buffer -> 客户端 Socket Buffer ->JDBC 所在的 JVM
1.2 JDBC 读取数据的三种模式
1.2.1 形式 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 代码如下
1.2.2 形式 2:游标查问
为了解决形式 1 爆内存的问题,JDBC 提供了一个游标参数,在建设 jdbc 连贯时加上 useCursorFetch=true。设置游标后,JDBC 每次会通知 Server 端每次抽取的数据量,防止爆内存。通信过程如下图所示。
形式 2 游标查问尽管解决了内存溢出的问题,形式 2 极大的依赖网络品质。当网络时延增大,假如每次通信减少 10ms,10 万次通信就会多出 1000s。这里仅仅是每次发申请的 RT,TCP 每次发送报文,都要求反馈 ACK 保证数据可靠性。client 每取 100 行(申请行数可配置),就会有屡次通信,进一步放大时延减少导致的效率问题。此外,游标查问下,Mysql 无奈预知查问的完结时延,为了应答本身的 DML 操作会在本地建设一个长期空间寄存要抽取的数据。因而,游标查问时会有以下几个景象产生
a. IOPS 飙升,Mysql 将数据写入到长期空间,数据传输时从长期空间读取数据,这都会引发大量 IO 操作。
b. 磁盘空间飙升,长期空间生命周期存在于整个 JDBC 读取阶段,直到客户端发动 Result.close()时才会被 Mysql 回收。
c. CPU 和内存有肯定比例回升。
无关游标查问的原理可参考博客 MySQL JDBC StreamResult 通信原理浅析以及 JDBC 源码,本文不在赘述。
参考 JDBC API docs,游标模式 Java demo 代码如下
1.2.3 形式 3:Stream 读取数据
形式 1 会导致 JVM 内存溢出,形式 2 尽管不会 FULL GC 然而通信效率较低,而且也会导致 Mysql 服务端 IOPS 飙升,耗费磁盘空间等问题。因而,咱们介绍 Stream 读取数据,流式须要在读取 Result 前设置
形式 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=trueResultSet.FETCH_REVERSE 等,其实小编钻研完 JDBC 驱动源码后发现,只须要设 fetchSize=Integer. MIN_VALUE,其余配置均和默认配置保持一致即可。游标模式 Java demo 代码如下
1.3 云数据迁徙服务在三种模式下的调优
云数据迁徙服务(Cloud Data Migration, CDM)是华为云上一个迁徙工具,详见 CDM 官网, 小编则通过 CDM 介绍如何切换三种模式抽取数据。CDM 默认应用的是形式 3,流式抽取数据,如果须要切换形式 1,形式 2 需额定配置。
1.3.1 配置形式 1:默认读取
新建 Mysql 连接器,建设办法详见官网,在高级属性中减少 useCursorFetch=false 和 adopt.stream=false
1.3.2 配置形式 2:游标查问
编辑 Mysql 连接器,在高级属性中减少 useCursorFetch=true 和 adopt.stream=false。游标查问的大小可通过界面上的 Fetch Size 调整,默认 1000。
1.3.3 配置形式 3:流式
CDM 默认走的流式,无需额定配置。留神 Stream 模式下,界面上的 Fetch Size
是不起作用的,起因参考上一节。
1.3.4 性能比照
新建 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。
点击关注,第一工夫理解华为云陈腐技术~