【摘要】Sqoop是一种用于在Apache Hadoop和结构化数据存储(如关系数据库)之间高效传输批量数据的工具 。本文将简单介绍Sqoop作业执行时相关的类及方法,并将该过程与MapReduce的执行结合,分析数据如何从源端迁移到目的端。

Sqoop作业执行过程

抛开MR的执行过程,Sqoop执行时用到的关键类总共有5个,Initializer、Partitioner、Extractor、Loader、Destroyer。执行流程如下图所示
  • Initializer:初始化阶段,源数据校验,参数初始化等工作;
  • Partitioner:源数据分片,根据作业并发数来决定源数据要切分多少片;
  • Extractor:开启extractor线程,根据用户配置从内存中构造数据写入队列;
  • Loader:开启loader线程,从队列中读取数据并抛出;
  • Destroyer:资源回收,断开sqoop与数据源的连接,并释放资源;
因此,每次新建一个连接器都要实现上述5个类。

Initializer

Initializer是在sqoop任务提交到MR之前被调用,主要是做迁移前的准备,例如连接数据源,创建临时表,添加依赖的jar包等。它是sqoop作业生命周期的第一步,主要API如下
public abstract void initialize(InitializerContext context, LinkConfiguration linkConfiguration,JobConfiguration jobConfiguration); public List<String> getJars(InitializerContext context, LinkConfiguration linkConfiguration,JobConfiguration jobConfiguration){ return new LinkedList<String>(); } public abstract Schema getSchema(InitializerContext context, LinkConfiguration linkConfiguration,JobConfiguration jobConfiguration) { return new NullSchema(); }
其中getSchema()方法被From或者To端的connector在提取或者载入数据时用来匹配数据。例如,一个GenericJdbcConnector会调用它获取源端Mysql的数据库名,表名,表中的字段信息等。

Destroyer

Destroyer 是在作业执行结束后被实例化,这是Sqoop作业的最后一步。清理任务,删除临时表,关闭连接器等。
public abstract void destroy(DestroyerContext context, LinkConfiguration linkConfiguration,JobConfiguration jobConfiguration);

Partitioner

Partitioner创建分区Partition,Sqoop默认创建10个分片,主要API如下
public abstract List<Partition> getPartitions(PartitionerContext context, LinkConfiguration linkConfiguration, FromJobConfiguration jobConfiguration);
Partition类中实现了readFields()方法和write()方法,方便读写
public abstract class Partition { public abstract void readFields(DataInput in) throws IOException; public abstract void write(DataOutput out) throws IOException; public abstract String toString(); }

Extractor

Extractor类根据分片partition和配置信息从源端提取数据,写入SqoopMapDataWriter中,SqoopMapDataWriter是SqoopMapper的内部类它继承了DataWriter类。此外它打包了SqoopWritable类,以中间数据格式保存从源端读取到的数据。
public abstract void extract(ExtractorContext context, LinkConfiguration linkConfiguration, JobConfiguration jobConfiguration, SqoopPartition partition);
该方法内部核心代码如下
while (resultSet.next()) { ... context.getDataWriter().writeArrayRecord(array); ... }

Loader

loader从源端接受数据,并将其载入目的端,它必须实现如下接口
public abstract void load(LoaderContext context, ConnectionConfiguration connectionConfiguration, JobConfiguration jobConfiguration) throws Exception;
load方法从SqoopOutputFormatDataReader中读取,它读取“中间数据格式表示形式” _中的数据并将其加载到数据源。此外Loader必须迭代的调用DataReader()直到它读完。
while ((array = context.getDataReader().readArrayRecord()) != null) { ... }

MapReduce执行过程

上一节避开MR执行过程,仅仅从Extractor和Loader过程描述迁移过程。下面将结合MR的执行过程详细的介绍一个Sqoop迁移作业流程。
初始化
1)作业初始化阶段,SqoopInputFormat读取给源端数据分片的过程
  • SqoopInputFormat的getSplits方法会调用Partitioner类的getPartitions方法
  • 将返回的Partition列表包装到SqoopSplit中;
  • 默认分片个数为10
这里每个Partition分片会交给一个Mapper执行。每个Mapper分别启动一个extractor线程和Loader线程迁移数据。
Mapper
2)作业执行阶段的Mapper过程
  • SqoopMapper包含了一个SqoopMapDataWriter类,
  • Mapper的run()调用Extractor.extract方法,该方法迭代的获取源端数据再调用DataWriter写入Context中
private Class SqoopMapDataWriter extends DataWriter { ... private void writeContent() { ... context.wirte(writable, NullWritable.get()); // 这里的writable 是SqoopWritable的一个对象 ... } ... }
注意:这里的Context中存的是KV对,K是SqoopWritable,而V仅是一个空的Writable对象。SqoopWritable中实现了write和readField,用于序列化和反序列化。
Reducer
3)作业执行阶段的Reduce过程,
  • SqoopOutputFormatLoadExecutor包装了SqoopOuputFormatDataReader,SqoopRecordWriter, ConsumerThread三个内部类;
  • SqoopNullOutputFormat调用getRecordWriter时创建一个线程:ConsumerThread,代码如下
public RecordWriter<SqoopWritable, NullWritable> getRecordWriter() { executorService = Executors.newSingleThreadExecutor(...); consumerFuture = executorService.submit(new ConsumerThread(context)); return writer; }
  • ConsumerThread集成了Runnable接口,线程内部调用Loader.load(...)方法,该方法用DataReader迭代的从Context中读取出SqoopWritable,并将其写入一个中间数据格式再写入目的端数据库中。
private class ConsumerThread implements Runnable { ... public void run() { ... Loader.load(loaderContext, connectorLinkConfig, ConnectorToJobConfig); ... } ... }
注意:
  • 再本地模式下,Sqoop提交任务时没有设置SqoopReducer.class,MR会调用一个默认的reducer.class。
  • setContent就是SqoopRecordWriter.write(...),它将SqoopWritable反序列化后存入中间存储格式中,即IntermediateDataFormat。与之对应,getContent就是从该中间存储格式中读取数据。
  • Sqoop定义了一个可插拔的中间数据格式抽象类,IntermediateDataFormat类,SqoopWritable打包了这个抽象类用来保存中间数据。
以上即为Sqoop作业执行时相关的类及方法内容,希望对大家在进行数据迁移过程中有所帮助。
 
 

深入浅出Sqoop之迁移过程源码分析的更多相关文章

  1. 大数据之Oozie——源码分析(一)程序入口

    工作中发现在oozie中使用sqoop与在shell中直接调度sqoop性能上有很大的差异.为了更深入的探索其中的缘由,开始了oozie的源码分析之路.今天第一天阅读源码,由于没有编译成功,不能运行测 ...

  2. Sqoop-1.4.6 Merge源码分析与改造使其支持多个merge-key

    Sqoop中提供了一个用于合并数据集的工具sqoop-merge.官方文档中的描述可以参考我的另一篇博客Sqoop-1.4.5用户手册. Merge的基本原理是,需要指定新数据集和老数据集的路径,根据 ...

  3. [Abp vNext 源码分析] - 文章目录

    一.简要介绍 ABP vNext 是 ABP 框架作者所发起的新项目,截止目前 (2019 年 2 月 18 日) 已经拥有 1400 多个 Star,最新版本号为 v 0.16.0 ,但还属于预览版 ...

  4. linux内存源码分析 - 内存压缩(同步关系)

    本文为原创,转载请注明:http://www.cnblogs.com/tolimit/ 概述 最近在看内存回收,内存回收在进行同步的一些情况非常复杂,然后就想,不会内存压缩的页面迁移过程中的同步关系也 ...

  5. 【转】HashMap实现原理及源码分析

    哈希表(hash table)也叫散列表,是一种非常重要的数据结构,应用场景极其丰富,许多缓存技术(比如memcached)的核心其实就是在内存中维护一张大的哈希表,而HashMap的实现原理也常常出 ...

  6. Spring IOC 容器源码分析系列文章导读

    1. 简介 Spring 是一个轻量级的企业级应用开发框架,于 2004 年由 Rod Johnson 发布了 1.0 版本.经过十几年的迭代,现在的 Spring 框架已经非常成熟了.Spring ...

  7. [阿里DIEN] 深度兴趣进化网络源码分析 之 Keras版本

    [阿里DIEN] 深度兴趣进化网络源码分析 之 Keras版本 目录 [阿里DIEN] 深度兴趣进化网络源码分析 之 Keras版本 0x00 摘要 0x01 背景 1.1 代码进化 1.2 Deep ...

  8. mongodb 数据块迁移的源码分析

    1. 简介 上一篇我们聊到了mongodb数据块的基本概念,和数据块迁移的主要流程,这篇文章我们聊聊源码实现部分. 2. 迁移序列图 数据块迁移的请求是从配置服务器(config server)发给( ...

  9. JUC源码学习笔记8——ConcurrentHashMap源码分析1 如何实现低粒度锁的插入,如何实现统计元素个数,如何实现并发扩容迁移

    源码基于jdk1.8 这一片主要讲述ConcurrentHashMap如何实现低粒度锁的插入,如何实现统计元素个数,如何实现并发扩容迁移 系列文章目录和关于我 一丶ConcurrentHashMap概 ...

  10. ABP源码分析一:整体项目结构及目录

    ABP是一套非常优秀的web应用程序架构,适合用来搭建集中式架构的web应用程序. 整个Abp的Infrastructure是以Abp这个package为核心模块(core)+15个模块(module ...

随机推荐

  1. jmeter不用工具获取随机值的几种方法

    第一种:直接获取 "vcContent": "${__time(yyyyMMddHHmmss)}${__RandomString(8,QWERTYUIOPASDFGHJK ...

  2. Error in v-on handler: “TypeError: _user.default is not a function“

    碰到这个问题一开始以为是方法名重复了,后来检查了一遍也没发现方法名或者属性名重复然后发现是 这个导入方法时没加{}的问题. , 无语.

  3. Stable-diffusion WebUI API调用方法

    写这篇文章的主要原因是工作中需要写一个用训练好的模型批量生图的脚本,开始是想用python直接加载模型,但后来发现webui的界面中有不少好用的插件和参数,所以最终改成调用WebUI接口的方式来批量生 ...

  4. ExtJS的使用方法汇总(1)——配置和表格控件使用

    在网上差一些关于ExtJS的相关资料,看到这篇博客写的不错,拿出来分享一下! 博客文章:ExtJS的使用方法汇总(1)--配置和表格控件使用              ExtJS的使用方法汇总(2)- ...

  5. P1119 floyd

    最开始看错数据了没看到Q = 100 是50%的数据以为跑q遍floyd能过,结果只有30,其他全t 1.要注意题目中的条件,挖掘一些性质 2.本题的另一个关键的是要对floyd的过程原理比较熟悉,f ...

  6. IPv4:根据CIDR显示地址范围

    最近遇到一个很有意思的点,于是就记录下来. CIDR一般是由IP地址和子网掩码组成,即 IP地址/子网掩码 格式. 子网掩码表示前面地址中的前多少位,为网络位,后面部分代表主机部分.例如:192.16 ...

  7. 大白话说Python+Flask入门(一)

    写在前面 技术这东西就得用,不用就会忘,之前写博客感觉就是给自己记笔记用,还有大部分,估计睡在语雀里都落灰了,哈哈! 在Python领域,我觉得我还是算个小白吧,会写讲不明白,所以我决定想做一件事,先 ...

  8. WPF --- 如何重写WPF原生控件样式

    引言 上一篇中 WPF --- 重写DataGrid样式,因新产品UI需要,重写了一下微软 WPF 原生的 DataGrid 的样式,包含如下内容: 基础设置,一些基本背景色,字体颜色等. 滚动条样式 ...

  9. JavaSE面试题01:自增变量

    JavaSE面试题:自增变量 来源:https://runwsh.com/ 代码 public static void main(String[] args) { int i=1; i=i++; in ...

  10. springboot如何用jar包启动,同时为不同机房设置不同的配置文件

    1.首先先把配置文件从jar中抽离 示例代码: <plugin> <groupId>org.apache.maven.plugins</groupId> <a ...