上節(jié)課學(xué)習(xí)了Hadoop集群測試,這節(jié)課我們一起學(xué)習(xí)一下Sqoop,Sqoop是專門用來遷移數(shù)據(jù)的,它可以把數(shù)據(jù)庫中的數(shù)據(jù)遷移到HDFS文件系統(tǒng),當(dāng)然也可以從HDFS文件系統(tǒng)導(dǎo)回到數(shù)據(jù)庫。
我來說一下Sqoop的使用場景,假如你們公司有個項目運(yùn)行好長時間了,積累了大量的數(shù)據(jù),現(xiàn)在想升級項目并換種數(shù)據(jù)庫進(jìn)行存儲原來的數(shù)據(jù),那么我們就需要先把數(shù)據(jù)都存放到另一個地方,然后再用新數(shù)據(jù)庫的語句把這些數(shù)據(jù)插入到新的數(shù)據(jù)庫。在沒有Sqoop之前,我們要做到這一點是很困難的,但是現(xiàn)在有了Sqoop,事情就變的簡單多了,Sqoop是運(yùn)行在Hadoop之上的一個工具,底層運(yùn)用了MapReduce的技術(shù),多臺設(shè)備并行執(zhí)行任務(wù),速度當(dāng)然大大提高,而且不用我們寫這方面的代碼,它提供了非常強(qiáng)大的命令,我們只需要知道怎樣使用這些命令,再加上一些SQL語句就可以輕輕松松實現(xiàn)數(shù)據(jù)的遷移工作。
接下來我們便正式開始學(xué)習(xí)怎樣使用Sqoop。
第一步:環(huán)境準(zhǔn)備
1.1 從Sqoop官網(wǎng)下載安裝包并解壓到/itcast目錄
大家可以參考:http://blog.csdn.net/u012453843/article/details/52951277這篇博客下載**sqoop-1.4.6.bin__hadoop-2.0.4-alpha.tar.gz**
Sqoop的運(yùn)行需要Yarn和HDFS,它只需要知道Yarn的管理者角色ResourceManager和HDFS的管理者角色NameNode在哪臺設(shè)備上就可以,我們在搭建集群的時候每臺設(shè)備上都有相同的6個配置文件,因此我們把Sqoop放到任何一臺設(shè)備上都可以。現(xiàn)在itcast03上的進(jìn)程是最少的,我們便把Sqoop放到itcast03上吧。
我們使用工具把sqoop-1.4.6.bin__hadoop-2.0.4-alpha.tar.gz上傳到Itcast03的root根目錄下,如下圖所示。
接著我們把sqoop-1.4.6.bin__hadoop-2.0.4-alpha.tar.gz解壓到/itcast目錄下,執(zhí)行的命令如下。
[root@itcast03 ~]#tar -zxvf sqoop-1.4.6.bin__hadoop-2.0.4-alpha.tar.gz -C /itcast/
解壓完之后我們到/itcast目錄下查看一下,發(fā)現(xiàn)已經(jīng)有sqoop的包了,如下所示。
[root@itcast03 ~]# cd /itcast/
[root@itcast03 itcast]# ls **hadoop-2.2.0****sqoop-1.4.6.bin__hadoop-2.0.4-alpha**
[root@itcast03 itcast]#
sqoop-1.4.6.bin__hadoop-2.0.4-alpha這么長的名字我們操作起來不方便,給它起個簡單的名字sqoop-1.4.6,如下所示。
[root@itcast03 itcast]#**mv sqoop-1.4.6.bin__hadoop-2.0.4-alpha/ sqoop-1.4.6**
[root@itcast03 itcast]# ls
hadoop-2.2.0 **sqoop-1.4.6**
[root@itcast03 itcast]#
那么,我們用不用配置Sqoop呢,如果你的環(huán)境已經(jīng)配置了NameNode和ResourceManager在哪臺設(shè)備上,那么我們不用配置任何內(nèi)容便可以使用Sqoop,現(xiàn)在我們的集群環(huán)境中core-site.xml和hdfs-site.xml兩個文件指定了NameNode在哪兩臺設(shè)備上,yarn-site.xml指定了ResourceManager在哪臺設(shè)備上,因此我們不用作任何配置了。
那么問題又來了,Sqoop是怎么知道我們集群的配置呢,其實Sqoop會自動去查找這臺設(shè)備上HADOOP_HOME的位置,找到之后它會自動讀取hadoop下面的配置文件,從而它也就知道了NameNode和ResourceManager的位置。
[root@itcast03 itcast]#**echo $HADOOP_HOME **
**/itcast/hadoop-2.2.0**
[root@itcast03 itcast]#
1.2 安裝MySQL數(shù)據(jù)庫
大家可以參考:http://blog.csdn.net/u012453843/article/details/52951422這篇博客來下載并配置使用Mysql
1.3 Hadoop集群
大家可以參考:http://blog.csdn.net/u012453843/article/details/52829830這篇博客來搭建集群。
第二步:啟動Sqoop
2.1 經(jīng)過了第一步的準(zhǔn)備工作后,我們終于可以來啟動Sqoop了,啟動之前我們先看看Sqooop的目錄,如下所示??梢钥吹?,在sqoop-1.4.6的bin目錄下有很多命令,我們最最常用的便是sqoop了,它基本上能滿足我們的所有要求了。因為它后面可以接很多參數(shù),這些參數(shù)的功能非常強(qiáng)大。
[root@itcast03 itcast]# ls
hadoop-2.2.0 sqoop-1.4.6
[root@itcast03 itcast]# cd sqoop-1.4.6/
[root@itcast03 sqoop-1.4.6]# ls
bin conf lib README.txt src
build.xml docs LICENSE.txt sqoop-1.4.6.jar testdata
CHANGELOG.txt ivy NOTICE.txt sqoop-patch-review.py
COMPILING.txt ivy.xml pom-old.xml sqoop-test-1.4.6.jar
[root@itcast03 sqoop-1.4.6]# cd bin
[root@itcast03 bin]# ls
configure-sqoop sqoop-export sqoop-list-tables
configure-sqoop.cmd sqoop-help sqoop-merge
sqoop sqoop-import sqoop-metastore
sqoop.cmd sqoop-import-all-tables sqoop-version
sqoop-codegen sqoop-import-mainframe start-metastore.sh
sqoop-create-hive-table sqoop-job stop-metastore.sh
sqoop-eval sqoop-list-databases
[root@itcast03 bin]#
我們再來看看sqoop-1.4.6的conf目錄,conf目錄下有個sqoop-site.xml文件,在這里面我們可以配置一些個性化的內(nèi)容,這里我們可以不做任何處理。
[root@itcast03 sqoop-1.4.6]# cd conf/
[root@itcast03 conf]# ls
oraoop-site-template.xml sqoop-env-template.cmd sqoop-env-template.sh sqoop-site-template.xml sqoop-site.xml
[root@itcast03 conf]#
接下來我們便啟動sqoop,我們還是先回到sqoop的bin目錄下,然后使用命令**./sqooop**來啟動,啟動的過程有很多警告,這是因為我們還沒有安裝相關(guān)工具。我們不管它。
[root@itcast03 bin]# ./sqoop
Warning: /itcast/sqoop-1.4.6/bin/../../hbase does not exist! HBase imports will fail.
Please set $HBASE_HOME to the root of your HBase installation.
Warning: /itcast/sqoop-1.4.6/bin/../../hcatalog does not exist! HCatalog jobs will fail.
Please set $HCAT_HOME to the root of your HCatalog installation.
Warning: /itcast/sqoop-1.4.6/bin/../../accumulo does not exist! Accumulo imports will fail.
Please set $ACCUMULO_HOME to the root of your Accumulo installation.
Warning: /itcast/sqoop-1.4.6/bin/../../zookeeper does not exist! Accumulo imports will fail.
Please set $ZOOKEEPER_HOME to the root of your Zookeeper installation.
Try 'sqoop help' for usage.
[root@itcast03 bin]#
第三步:把Sqoop目錄配置到環(huán)境變量(這步可以不配置,只是所有命令需要到sqoop的bin目錄下去操作,比較麻煩)
由于我們是在itcast03上操作sqoop的,因此我們便在itcast03上配置它的環(huán)境變量,使用的命令是vim /etc/profile,在最下方添加我們的SQOOP_HOME,如下紅色字體的內(nèi)容就是我們要添加的內(nèi)容。
export JAVA_HOME=/usr/java/jdk1.7.0_80
export HADOOP_HOME=/itcast/hadoop-2.2.0
export SQOOP_HOME=/itcast/sqoop-1.4.6
export PATH=$JAVA_HOME/bin:$HADOOP_HOME/bin:$SQOOP_HOME/bin:$PATH
配置完環(huán)境變量之后別忘了使我們的環(huán)境變量生效,我們使用的命令是:source /etc/profile
第四步:導(dǎo)入
4**.1 我們現(xiàn)在想把數(shù)據(jù)庫sqoop下的Student表中的數(shù)據(jù)遷移到HDFS,我們先看看Student表中都有哪些數(shù)據(jù),可以看到有10條數(shù)據(jù),我們就要把這10條數(shù)據(jù)上傳到HDFS。(****需要注意的是:Student表一定要有主鍵,否則會有問題**)
4.1.1 在開始導(dǎo)入之前,我們需要先把jdbc的驅(qū)動放到**/itcast/sqoop-1.4.6/lib**目錄下,驅(qū)動包大家可以到官網(wǎng)下載也可以直接到:http://download.csdn.net/detail/u012453843/9667329這個網(wǎng)址下載**。**
[root@itcast03 ~]# ls
anaconda-ks.cfg Music
Desktop mysql-connector-java-5.1.40-bin.jar
Documents Pictures
Downloads Public
install.log sqoop-1.4.6.bin__hadoop-2.0.4-alpha.tar.gz
install.log.syslog Templates
jdk-7u80-linux-x64.gz Videos
[root@itcast03 ~]# cp mysql-connector-java-5.1.40-bin.jar /itcast/sqoop-1.4.6/lib
[root@itcast03 ~]#
4.1.2 設(shè)置你的itcast03設(shè)備可以遠(yuǎn)程登錄到數(shù)據(jù)庫。
默認(rèn)情況下我們的itcast03是無法遠(yuǎn)程登錄到數(shù)據(jù)庫的,直接進(jìn)行導(dǎo)入操作的話會報如下錯誤:
manager.SqlManager: **Error executing statement: java.sql.SQLException: null, message from server: "Host 'itcast03' is not allowed to connect to this MySQL server"**
java.sql.SQLException: null, message from server: "Host 'itcast03' is not allowed to connect to this MySQL server"
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:964)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:897)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:886)
at com.mysql.jdbc.MysqlIO.doHandshake(MysqlIO.java:1040)
at com.mysql.jdbc.ConnectionImpl.coreConnect(ConnectionImpl.java:2253)
at com.mysql.jdbc.ConnectionImpl.connectOneTryOnly(ConnectionImpl.java:2284)
at com.mysql.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:2083)
at com.mysql.jdbc.ConnectionImpl.<init>(ConnectionImpl.java:806)
at com.mysql.jdbc.JDBC4Connection.<init>(JDBC4Connection.java:47)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
at com.mysql.jdbc.Util.handleNewInstance(Util.java:425)
at com.mysql.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:410)
at com.mysql.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:328)
at java.sql.DriverManager.getConnection(DriverManager.java:571)
at java.sql.DriverManager.getConnection(DriverManager.java:215)
at org.apache.sqoop.manager.SqlManager.makeConnection(SqlManager.java:885)
at org.apache.sqoop.manager.GenericJdbcManager.getConnection(GenericJdbcManager.java:52)
at org.apache.sqoop.manager.SqlManager.execute(SqlManager.java:744)
at org.apache.sqoop.manager.SqlManager.execute(SqlManager.java:767)
at org.apache.sqoop.manager.SqlManager.getColumnInfoForRawQuery(SqlManager.java:270)
at org.apache.sqoop.manager.SqlManager.getColumnTypesForRawQuery(SqlManager.java:241)
at org.apache.sqoop.manager.SqlManager.getColumnTypes(SqlManager.java:227)
at org.apache.sqoop.manager.ConnManager.getColumnTypes(ConnManager.java:295)
at org.apache.sqoop.orm.ClassWriter.getColumnTypes(ClassWriter.java:1833)
at org.apache.sqoop.orm.ClassWriter.generate(ClassWriter.java:1645)
at org.apache.sqoop.tool.CodeGenTool.generateORM(CodeGenTool.java:107)
at org.apache.sqoop.tool.ImportTool.importTable(ImportTool.java:478)
at org.apache.sqoop.tool.ImportTool.run(ImportTool.java:605)
at org.apache.sqoop.Sqoop.run(Sqoop.java:143)
at org.apache.hadoop.util.ToolRunner.run(ToolRunner.java:70)
at org.apache.sqoop.Sqoop.runSqoop(Sqoop.java:179)
at org.apache.sqoop.Sqoop.runTool(Sqoop.java:218)
at org.apache.sqoop.Sqoop.runTool(Sqoop.java:227)
at org.apache.sqoop.Sqoop.main(Sqoop.java:236)
16/10/29 02:21:06 ERROR tool.ImportTool: Encountered IOException running import job: java.io.IOException: No columns to generate for ClassWriter
at org.apache.sqoop.orm.ClassWriter.generate(ClassWriter.java:1651)
at org.apache.sqoop.tool.CodeGenTool.generateORM(CodeGenTool.java:107)
at org.apache.sqoop.tool.ImportTool.importTable(ImportTool.java:478)
at org.apache.sqoop.tool.ImportTool.run(ImportTool.java:605)
at org.apache.sqoop.Sqoop.run(Sqoop.java:143)
at org.apache.hadoop.util.ToolRunner.run(ToolRunner.java:70)
at org.apache.sqoop.Sqoop.runSqoop(Sqoop.java:179)
at org.apache.sqoop.Sqoop.runTool(Sqoop.java:218)
at org.apache.sqoop.Sqoop.runTool(Sqoop.java:227)
at org.apache.sqoop.Sqoop.main(Sqoop.java:236)
解決方法如下:
mysql -u root -p
mysql>use mysql;
mysql>select host from user where user='root';
mysql>update user set host = '%' where user ='root';
mysql>flush privileges;
mysql>select host from user where user='root';
第一句是以權(quán)限用戶root登錄
第二句:選擇mysql庫
第三句:查看mysql庫中的user表的host值(即可進(jìn)行連接訪問的主機(jī)/IP名稱)
第四句:修改host值(以通配符%的內(nèi)容增加主機(jī)/IP地址),當(dāng)然也可以直接增加IP地址
第五句:刷新MySQL的系統(tǒng)權(quán)限相關(guān)表
第六句:再重新查看user表時,有修改。。
重起mysql服務(wù)即可完成。
4.1.3 設(shè)置NameNode和DataNode時間同步,有可能NameNode和DataNode的時間是不同步的,這樣的話,在執(zhí)行導(dǎo)入操作的時候會報如下的錯誤。
16/10/29 03:01:50 INFO mapreduce.Job: Job job_1477729967303_0001 failed with state FAILED due to: Application application_1477729967303_0001 failed 2 times due to Error launching appattempt_1477729967303_0001_000002. Got exception: org.apache.hadoop.yarn.exceptions.YarnException: Unauthorized request to start container.
This token is expired. current time is 1477736540684 found 1477735910751
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
at org.apache.hadoop.yarn.api.records.impl.pb.SerializedExceptionPBImpl.instantiateException(SerializedExceptionPBImpl.java:152)
at org.apache.hadoop.yarn.api.records.impl.pb.SerializedExceptionPBImpl.deSerialize(SerializedExceptionPBImpl.java:106)
at org.apache.hadoop.yarn.server.resourcemanager.amlauncher.AMLauncher.launch(AMLauncher.java:122)
at org.apache.hadoop.yarn.server.resourcemanager.amlauncher.AMLauncher.run(AMLauncher.java:249)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
. Failing the application.
解決方法如下:
我們的集群有6臺設(shè)備,在每臺設(shè)備上使用以下兩條命令。有個問題是,在執(zhí)行第二條命令的時候,我在沒連網(wǎng)的情況下無法執(zhí)行,但是在連網(wǎng)的情況下執(zhí)行成功了??!下面是我在itcast01上執(zhí)行的信息,注意要在其它設(shè)備上也執(zhí)行一下。
[root@itcast01 ~]# cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
cp: overwrite `/etc/localtime'? y
[root@itcast01 ~]# ntpdate pool.ntp.org
29 Oct 11:20:27 ntpdate[2800]: step time server 168.63.242.24 offset -28761.836642 sec
[root@itcast01 ~]#
4.1.4 下面我們便開始真正開始我們的導(dǎo)入功能即將數(shù)據(jù)庫中的數(shù)據(jù)導(dǎo)入到HDFS系統(tǒng)。使用的命令如下紅色字體的內(nèi)容,我說一下命令各項參數(shù)的意思。
首先./sqoop是我們操作sqoop最常用的命令也是功能最強(qiáng)大的命令。接著,import是導(dǎo)入的意思,接著,--connect jdbc:mysql://169.254.254.1:3306意思是以jdbc的方式連接數(shù)據(jù)庫,169.254.254.1是我們的Windows的IP地址(該IP可以和集群連通),3306是端口。接著,sqoop是我們Student表所在的數(shù)據(jù)庫的名稱。接著,--username root --password root 是指數(shù)據(jù)庫的用戶名和密碼。接著,--table Student意思是我們要導(dǎo)的是Student表。
[root@itcast03 bin]# ./sqoop import --connect jdbc:mysql://169.254.254.1:3306/sqoop --username root --password root --table Student
命令的執(zhí)行信息如下圖所示,看到紅色圈住的信息時說明執(zhí)行成功了,這里大家發(fā)現(xiàn)了沒有,執(zhí)行過程中只有map,reduce的進(jìn)度始終是0%,說明導(dǎo)入功能根本就沒用到reduce的功能,這個其實也好理解,我們是要把數(shù)據(jù)庫中的數(shù)據(jù)導(dǎo)入到HDFS系統(tǒng),只需要多臺設(shè)備同時到數(shù)據(jù)庫中去讀取一條一條數(shù)據(jù)然后直接上傳到HDFS,根本就不需要進(jìn)行合并操作。如果是要計算很多數(shù)的和的話,就要用到reduce了,顯然我們的導(dǎo)入功能用不到reduce。
<main style="font-family: "WenQuanYi Micro Hei Mono", "WenQuanYi Micro Hei", "Microsoft Yahei Mono", "Microsoft Yahei", sans-serif, Simsun !important; float: left; width: 840px;">
<article style="font-family: "WenQuanYi Micro Hei Mono", "WenQuanYi Micro Hei", "Microsoft Yahei Mono", "Microsoft Yahei", sans-serif, Simsun !important; box-shadow: rgba(0, 0, 0, 0.0470588) 0px 2px 4px 0px; background-color: rgb(255, 255, 255); padding: 20px 0px;">
4.1.5 執(zhí)行完了導(dǎo)入命令,我們到HDFS系統(tǒng)查看一下生成的結(jié)果。
我們到狀態(tài)為active的namenode的HDFS界面,如下圖所示,可以看到itcast02現(xiàn)在是active狀態(tài),我們點擊"Browse the filesystem"。
點擊上圖的"Browse the filesystem"鏈接之后我們會進(jìn)入到如下圖所示的界面,由于我們在導(dǎo)入的時候沒有指定要導(dǎo)入到HDFS的哪個目錄,因此系統(tǒng)會自動幫我們存到一個地方,它自動存的目錄在user文件夾下,如下圖所示,我們點擊"user"。
點擊上圖的"user"文件夾之后,我們會進(jìn)入到如下圖所示的界面,由于我們是以root的身份進(jìn)行導(dǎo)入操作的,因此它自動幫我們生成了一個root文件夾,我們點擊它。
點擊上圖的"root"文件夾之后,我們會進(jìn)入到如下圖所示的界面,我們導(dǎo)的是數(shù)據(jù)庫中的Student表,因此這里生成了一個叫Student的文件夾,我們點擊它。
點擊上圖的"Student"文件夾之后我們會鍵入到如下圖所示的界面,可以看到有4個結(jié)果文件,也就是說使用了4個mapper來參與導(dǎo)入操作了,我們還發(fā)現(xiàn)文件的名字中中間都是"m",“m”代表的意思是mapper生成的文件,"r"代表的意思是reducer生成的文件。我們依次點開這四個文件,看看生成的結(jié)果是否正確。
我們先看part-m-00000文件的內(nèi)容,我們點擊上圖的"part-m-00000"鏈接,會看到如下圖所示的內(nèi)容,可以看到有三條數(shù)據(jù)信息,列與列之間默認(rèn)是以","分隔的。
接著我們再來看part-m-00001文件的內(nèi)容,如下圖所示,這個文件承接上個文件,存儲的是ID為4和5的兩條記錄。
接著我們再來看看part-m-00002文件的內(nèi)容,如下圖所示,這個文件承接上個文件,存儲的是ID為6和7的兩條記錄。
接著我們再來看看最后一個文件part-m-00003文件的內(nèi)容,如下圖所示,該文件承接上個文件,可以看到有ID為8、9、10的三條記錄。從而可以得出結(jié)論,我們剛才的導(dǎo)入操作完全沒問題。
4.2 我們上面以最簡單的方式導(dǎo)入了一下數(shù)據(jù),現(xiàn)在我們再多使用兩個參數(shù)來進(jìn)入導(dǎo)入操作,我們使用的命令如下,可以看到我們加了兩個參數(shù),--target-dir(指定要存放到服務(wù)器的哪個目錄下)和-m(指定要起的mapper的數(shù)量,注意:m前面是一個"-",其它參數(shù)前面是兩個"--",由于用不到reducer合并數(shù)據(jù),因此起幾個mapper就會生成幾個文件。)
[root@itcast03 bin]# ./sqoop import --connect jdbc:mysql://169.254.254.1:3306/sqoop --username root --password root --table Student--target-dir /sqoop/td1 -m 2
上面的命令執(zhí)行完之后,我們到服務(wù)器上查看一下生成的結(jié)果,我們先到服務(wù)器根目錄下,如下圖所示,可以看到確實生成了sqoop文件夾,我們點擊進(jìn)入
點擊上圖的sqoop鏈接之后,我們進(jìn)入到如下圖所示的界面,可以看到確實有我們剛才命名的td1文件夾,我們點擊進(jìn)入td1。
點擊上圖的td1鏈接之后,我們會看到如下圖所示的界面,可以看到生成了兩個結(jié)果文件part-m-00000和part-m-00001,這符合我們設(shè)置的參數(shù)-m 2的要求,我們具體看看這兩個文件中的內(nèi)容。
我們先看看part-m-00000文件中的內(nèi)容,可以看到內(nèi)容是ID從1到5的Student表中的數(shù)據(jù)。
接著我們再看看part-m-00001文件中的內(nèi)容,可以看到這里面的內(nèi)容是Student表ID從6到10的數(shù)據(jù),也完全沒問題?。。?

4.3 我們接著來增加參數(shù),--fields-terminated-by '\t'意思是指定列與列的分隔符為制表符,--columns 'ID,Name,Age'意思是我們要導(dǎo)入的只有ID、Name和Age這三列。如下所示
[root@itcast03 bin]# ./sqoop import --connect jdbc:mysql://169.254.254.1:3306/sqoop --username root --password root --table Student --target-dir /sqoop/td2 -m 2--fields-terminated-by '\t' --columns 'ID,Name,Age'
已經(jīng)截的圖很多了,我就不挨個截圖了,直接看生成的part-m-00001文件的內(nèi)容吧,如下圖所示,可以看到列與列之間確實是用\t來分隔了。
4.4 我們現(xiàn)在來玩一個更高級的,我們使用where條件來篩選數(shù)據(jù)并導(dǎo)入符合條件的數(shù)據(jù),增加的參數(shù)是--where 'ID>=3 and ID<=8',顧名思義,就是要把ID從3到8的數(shù)據(jù)導(dǎo)入到服務(wù)器。****
[root@itcast03 bin]# ./sqoop import --connect jdbc:mysql://169.254.254.1:3306/sqoop --username root --password root --table Student --target-dir /sqoop/td3 -m 2 --fields-terminated-by '\t' --columns 'ID,Name,Age'--where 'ID>=3 and ID<=8'
** 命令執(zhí)行成功之后,我們來看看生成的兩個文件的內(nèi)容是否符合條件,如下圖所示,發(fā)現(xiàn)完全符合我們的where條件。**
4.5 接下來我們玩更高級一點的,我們使用query語句來篩選我們的數(shù)據(jù),這意味著我們可以導(dǎo)入多張表的數(shù)據(jù),我們還是來個簡單的,命令如下。我們發(fā)現(xiàn)使用query語句的話,就不用指定table了,由于數(shù)量很少,現(xiàn)在我們指定mapper的數(shù)量為1。我們執(zhí)行下面的命令,該命令目前有個問題。
[root@itcast03 bin]# ./sqoop import --connect jdbc:mysql://169.254.254.1:3306/sqoop --username root --password root--query 'select * from Student where ID>5'--target-dir /sqoop/td4-m 1 --fields-terminated-by '\t'
執(zhí)行上面的命令會出現(xiàn)如下所示的錯誤信息:**must contain '$CONDITIONS' in WHERE clause.**的意思是在我們的query的where條件當(dāng)中必須有**$CONDITIONS'這個條件,這個條件就相當(dāng)于一個占位符,動態(tài)接收傳過來的參數(shù),從而查詢出符合條件的結(jié)果。**
16/10/29 18:09:46 ERROR tool.ImportTool: Encountered IOException running import job: java.io.IOException: Query [select * from Student where ID>5]must contain '$CONDITIONS' in WHERE clause.
at org.apache.sqoop.manager.ConnManager.getColumnTypes(ConnManager.java:300)
at org.apache.sqoop.orm.ClassWriter.getColumnTypes(ClassWriter.java:1833)
at org.apache.sqoop.orm.ClassWriter.generate(ClassWriter.java:1645)
at org.apache.sqoop.tool.CodeGenTool.generateORM(CodeGenTool.java:107)
at org.apache.sqoop.tool.ImportTool.importTable(ImportTool.java:478)
at org.apache.sqoop.tool.ImportTool.run(ImportTool.java:605)
at org.apache.sqoop.Sqoop.run(Sqoop.java:143)
at org.apache.hadoop.util.ToolRunner.run(ToolRunner.java:70)
at org.apache.sqoop.Sqoop.runSqoop(Sqoop.java:179)
at org.apache.sqoop.Sqoop.runTool(Sqoop.java:218)
at org.apache.sqoop.Sqoop.runTool(Sqoop.java:227)
at org.apache.sqoop.Sqoop.main(Sqoop.java:236)
我們在上面的命令語句當(dāng)中加上**$CONDITIONS,如下所示。然后再執(zhí)行,就可以執(zhí)行成功。**
[root@itcast03 bin]#./sqoop import --connect jdbc:mysql://169.254.254.1:3306/sqoop --username root --password root --query 'select * from Student where ID>5 and $CONDITIONS'--target-dir /sqoop/td4 -m 1 --fields-terminated-by '\t'
**執(zhí)行成功后,我們看看結(jié)果文件,如下圖所示,發(fā)現(xiàn)確實導(dǎo)入的是ID從6到10的數(shù)據(jù)。**

假如我們要把-m 1改成-m 2的話,導(dǎo)入操作會失敗,我們來看一下命令執(zhí)行及異常信息,如下所示。異常信息的意思是,我們沒有指定mapper按什么規(guī)則來分割數(shù)據(jù)。即我這個mapper應(yīng)該讀取哪些數(shù)據(jù),一個mapper的時候沒有問題是因為它一個mapper就讀取了所有數(shù)據(jù),現(xiàn)在mapper的數(shù)量是2了,那么我第一個mapper讀取多少數(shù)據(jù),第二個mapper就讀取第一個mapper剩下的數(shù)據(jù),現(xiàn)在兩個mapper缺少一個分割數(shù)據(jù)的條件,找一個唯一標(biāo)識的一列作為分割條件,這樣兩個mapper便可以迅速知道表中一共有多少條數(shù)據(jù),兩者分別需要讀取多少數(shù)據(jù)。
[root@itcast03 bin]# ./sqoop import --connect jdbc:mysql://169.254.254.1:3306/sqoop --username root --password root --query 'select * from Student where ID>5 and $CONDITIONS' --target-dir /sqoop/td5-m 2 --fields-terminated-by '\t'
Please set $ZOOKEEPER_HOME to the root of your Zookeeper installation.
16/10/29 19:01:47 INFO sqoop.Sqoop: Running Sqoop version: 1.4.6
16/10/29 19:01:47 WARN tool.BaseSqoopTool: Setting your password on the command-line is insecure. Consider using -P instead.
When importing query results in parallel, you must specify --split-by.
Try --help for usage instructions
** 知道了異常的原因,我們便加上分割數(shù)據(jù)的條件,我們使用的是Student表的ID字段。命令如下。**
[root@itcast03 bin]# ./sqoop import --connect jdbc:mysql://169.254.254.1:3306/sqoop --username root --password root --query 'select * from Student where ID>5 and $CONDITIONS' --target-dir /sqoop/td5 -m 2 --fields-terminated-by '\t' --split-by Student.ID
執(zhí)行上面的命令,導(dǎo)入操作便可以成功。如下圖所示。發(fā)現(xiàn)確實生成了兩個文件,并且兩個文件中的內(nèi)容加起來剛好就是我們query中的條件ID>5。這里我們再詳細(xì)說說$CONDITIONS'的作用,sqoop首先根據(jù)Student.ID將數(shù)據(jù)統(tǒng)計出來,然后傳給$CONDITIONS',query語句就知道一共有多條數(shù)據(jù)了,假如第一個mapper讀取了2條數(shù)據(jù),那么也會把這個2傳給$CONDITIONS,這樣第二個mapper在讀取數(shù)據(jù)的時候便可以根據(jù)第一個mapper讀取的數(shù)量讀取剩下的內(nèi)容。
第五步:導(dǎo)出
**我們把第四步最后導(dǎo)入的td5文件夾下兩個文件的數(shù)據(jù)導(dǎo)出到數(shù)據(jù)庫。**
** 5.1 我們新建一個table表,這個table表和我們的Student表結(jié)構(gòu)要一致,在mysql中有一個復(fù)制表的語句:create table student_copy like Student;這樣我們便可以得到一張表結(jié)構(gòu)和Student完全一樣的表,如下圖所示。**
** 5.2 接下來我們便開始將服務(wù)器上的數(shù)據(jù)導(dǎo)出到數(shù)據(jù)庫中,我們先來看一個錯誤的命令。**
[root@itcast03 bin]# ./sqoop export --connect jdbc:mysql://169.254.254.1:3306/sqoop --username root --password root --export-dir /sqoop/td5 -m 1 --table student_copy
** 錯誤信息如下:可以看到是數(shù)據(jù)轉(zhuǎn)換出現(xiàn)了異常。那么是什么原因?qū)е碌哪兀鋵嵤橇信c列的分隔符導(dǎo)致的,td5下的兩個文件中的數(shù)據(jù)是以"\t"來分隔的,而sqoop默認(rèn)是以","來分隔的,因此出現(xiàn)了問題。**
Caused by: java.lang.RuntimeException: Can't parse input data: '6 baiyansong 45 88.0'
at student_copy.__loadFromFields(student_copy.java:335)
at student_copy.parse(student_copy.java:268)
at org.apache.sqoop.mapreduce.TextExportMapper.map(TextExportMapper.java:83)
... 10 more
Caused by: java.lang.NumberFormatException: For input string: "6 baiyansong 45 88.0"
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.lang.Integer.parseInt(Integer.java:492)
at java.lang.Integer.valueOf(Integer.java:582)
at student_copy.__loadFromFields(student_copy.java:317)
... 12 more
知道了問題,我們對癥下藥,人為指定分隔符--fields-terminated-by '\t',命令如下所示。這里說明一點的是,--export-dir /sqoop/td5這個參數(shù)有些人可能會有疑惑,因為td5文件夾下除了part-m-00000和part-m-00001兩個結(jié)果文件外,還有一個名為“SUCCESS”的文件,導(dǎo)出的時候會不會連這個文件的內(nèi)容都導(dǎo)出去了呢?其實不會的,我們指定到文件夾,程序會去掃描這個文件夾下的所有文件,凡是不以""開頭的文件都會被導(dǎo)出去,SUCCESS文件是以“”開頭的,因此它不會被導(dǎo)出去,大家放心。
[root@itcast03 bin]#./sqoop export --connect jdbc:mysql://169.254.254.1:3306/sqoop --username root --password root --export-dir /sqoop/td5 -m 1 --table student_copy--fields-terminated-by '\t'
** 執(zhí)行成功后我們看看我們剛才復(fù)制的student_copy表中是否有了我們導(dǎo)出的數(shù)據(jù)。發(fā)現(xiàn)確實導(dǎo)出到數(shù)據(jù)庫5條數(shù)據(jù),這5條數(shù)據(jù)也就是td5下面兩個文件的內(nèi)容,說明我們的導(dǎo)出功能也完全正常??!**