Sqoop1的架构图如下所示。
第一代Sqoop的设计目标很简单:sqoop import --connect jdbc:mysql://localhost/testdb --table PERSON --username test --password ****
上面这条命令形成一系列任务:
sqoop export --connect jdbc:mysql://localhost/testdb --table CLIENTS_INTG --username test --password **** --export-dir /user/localadmin/CLIENTS
上面这条命令形成一系列任务:
Sqoop2的架构图如下所示。
Sqoop2体系结构比Sqoop1复杂得多,被设计用来解决Sqoop1的问题。特性 |
Sqoop1 |
Sqoop2 |
所有主要RDBMS的连接器 |
支持 |
不支持 变通方案:使用的通用的JDBC连接器,它已经在Microsoft SQL Server、PostgreSQL、MySQL和Oracle数据库上测试过。 这个连接器应该可以在任何JDBC兼容的数据库上使用,但性能比不上Sqoop1的专用连接器。 |
Kerberos整合 |
支持 |
不支持 |
数据从RDBMS传输到Hive或Hbase |
支持 |
不支持 变通方案:用下面两步方法。 1. 数据从RDBMS导入HDFS 2. 使用适当的工具或命令(如Hive的LOAD DATA语句)手工把数据导入Hive或Hbase。 |
数据从Hive或Hbase传输到RDBMS |
不支持 变通方案:用下面两步方法。 1. 从Hive或Hbase抽出数据到HDFS(文本文件或Avro文件) 2. 使用Sqoop将上一步的输出导入RDBMS |
不支持 变通方案如Sqoop1。 |
完全抽取和变化数据捕获(CDC)
如果数据量很小并且易处理,一般来说采取完全源数据抽取(将所有的文件记录或所有的数据库表数据抽取至数据仓库)。这种方式适合引用类型的源数据,比如邮政编码。引用型源数据通常是维度表的源。如果源数据量很大,抽取全部数据是不可行的,那么只能抽取变化的源数据(自最后一次抽取以来变化的数据)。这种数据抽取模式称为变化数据捕获(CDC),通常被用于抽取操作型系统的事务数据,比如销售订单。
CDC大体可以分为两种,一种是侵入式的,另一种是非侵入式的。所谓侵入式的是指CDC操作会给源系统带来性能的影响。只要CDC操作以任何一种方式执行了SQL语句,就可以认为是侵入式的CDC。常用的四种CDC方法中有三种是侵入性的,这四种方法是:基于时间戳的CDC、基于触发器的CDC、基于快照的CDC、基于日志的CDC。下表总结了四种CDC方案的特点。
|
时间戳方式 |
快照方式 |
触发器方式 |
日志方式 |
能区分插入/更新 |
否 |
是 |
是 |
是 |
周期内,检测到多次更新 |
否 |
否 |
是 |
是 |
能检测到删除 |
否 |
是 |
是 |
是 |
不具有侵入性 |
否 |
否 |
否 |
是 |
支持实时 |
否 |
否 |
是 |
是 |
需要DBA |
否 |
否 |
是 |
是 |
不依赖数据库 |
是 |
是 |
否 |
否 |
源数据表 |
数据仓库RDS表 |
抽取模式 |
customer |
customer |
整体、拉取 |
product |
product |
整体、拉取 |
sales_order |
sales_order |
基于时间戳的CDC、拉取 |
sqoop import --connect jdbc:mysql://cdh1:3306/source?useSSL=false --username root --password mypassword --table customer --hive-import --hive-table rds.customer --hive-overwrite
sqoop import --connect jdbc:mysql://cdh1:3306/source?useSSL=false --username root --password mypassword --table product --hive-import --hive-table rds.product --hive-overwrite
(2)增量导入
参数 |
描述 |
--check-column (col) |
在确定应该导入哪些行时,指定被检查的列。(列不应该是CHAR/NCHAR/VARCHAR/VARNCHAR/LONGVARCHAR/LONGNVARCHAR数据类型) |
--incremental (mode) |
指定Sqoop怎样确定哪些行是新行。有效值是append和lastmodified。 |
--last-value (value) |
指定已经导入数据的被检查列的最大值 |
有了对Sqoop增量导入的基本了解,下面看一下如何在本示例中使用它抽取数据。对于sales_order这个表采用基于时间戳的CDC拉取方式抽数据。这里假设源系统中销售订单记录一旦入库就不再改变,或者可以忽略改变。也就是说销售订单是一个随时间变化单向追加数据的表。sales_order表中有两个关于时间的字段,order_date表示订单时间,entry_date表示订单数据实际插入表里的时间,在后面讨论“迟到的事实”时就会看到两个时间可能不同。那么用哪个字段作为CDC的时间戳呢?设想这样的场景,一个销售订单的订单时间是2015年1月1日,实际插入表里的时间是2015年1月2日,ETL每天0点执行,抽取前一天的数据。如果按order_date抽取数据,条件为where order_date >= '2015-01-02' AND order_date < '2015-01-03',则2015年1月3日0点执行的ETL不会捕获到这个新增的订单数据。所以应该以entry_date作为CDC的时间戳。
下面测试一下增量导入:sqoop job --create myjob_1 \
-- \
import \
--connect "jdbc:mysql://cdh1:3306/source?useSSL=false&user=root&password=mypassword" \
--table sales_order \
--columns "order_number, customer_number, product_code, order_date, entry_date, order_amount" \
--where "entry_date < current_date()" \
--hive-import \
--hive-table rds.sales_order \
--incremental append \
--check-column entry_date \
--last-value '1900-01-01'
说明:在作业中使用的--where参数,是为了只导入前一天的数据。
sqoop job --show myjob_1 | grep last.value
可以看到,last-value的值为初始的'1900-01-01'
sqoop job --exec myjob_1
sqoop job --show myjob_1 | grep last.value
可以看到,last-value的值为当前最大值'2016-06-30 05:20:47'
SET @customer_number := floor(1 + rand() * 6);
SET @product_code := floor(1 + rand() * 2);
SET @order_date := from_unixtime(unix_timestamp('2016-07-03') + rand() * (unix_timestamp('2016-07-04') - unix_timestamp('2016-07-03')));
SET @amount := floor(1000 + rand() * 9000);
INSERT INTO sales_order
VALUES (101,@customer_number,@product_code,@order_date,@order_date,@amount);
SET @customer_number := floor(1 + rand() * 6);
SET @product_code := floor(1 + rand() * 2);
SET @order_date := from_unixtime(unix_timestamp('2016-07-04') + rand() * (unix_timestamp('2016-07-05') - unix_timestamp('2016-07-04')));
SET @amount := floor(1000 + rand() * 9000);
INSERT INTO sales_order
VALUES (102,@customer_number,@product_code,@order_date,@order_date,@amount);
COMMIT;
上面的语句向sales_order插入了两条记录,一条是7月3日的,另一条是7月4日的,如下图所示。
sqoop job --exec myjob_1
sqoop job --show myjob_1 | grep last.value
可以看到,last-value的值已经变为'2016-07-03 22:45:46'
select * from sales_order order by order_number desc;
结果如下图所示,可以rds.sales_order表中只新增了一条数据,7月4日的记录被作业中的where过滤掉。