테이블 전체가 아니라 조건이나 조인이 들어간 결과를 가져오려면 --query 를 쓴다.
sqoop import \
--connect "jdbc:oracle:thin:@//db-host:1521/ORCLPDB1" \
--username "${DB_USER}" --password-file hdfs:///user/etl/.db_pw \
--query 'SELECT o.id, o.amt, c.name FROM orders o JOIN cust c ON o.cid = c.id WHERE $CONDITIONS' \
--split-by o.id \
--num-mappers 4 \
--target-dir /user/etl/orders \
--delete-target-dir \
--as-parquetfile
지켜야 할 규칙이 몇 가지 있다.
질의에 $CONDITIONS 가 반드시 들어가야 한다. Sqoop 이 이 자리를 맵퍼별 분할 조건으로 치환하기 때문이며, 없으면 실행 전에 거부된다. 맵퍼가 하나여도 마찬가지다.
작은따옴표로 감싸야 한다. 큰따옴표를 쓰면 셸이 $CONDITIONS 를 빈 문자열로 치환해 버린다. 큰따옴표가 꼭 필요하면 \$CONDITIONS 로 이스케이프한다.
질의 끝에 세미콜론을 붙이지 않는다.
--split-by 에는 값이 고르게 분포한 숫자 컬럼을 지정한다. 분포가 치우치면 맵퍼 하나가 대부분을 처리한다. 적절한 컬럼이 없으면 --num-mappers 1 로 두는 편이 낫다.
--table 과 --query 는 함께 쓸 수 없다.
sqoop import \
--connect ... --username ... --password-file ... \
--table orders \
--incremental lastmodified \
--check-column updated_at \
--last-value '2026-09-19 00:00:00' \
--merge-key id \
--target-dir /user/etl/orders
append 는 증가하는 키 기준으로 새 행만, lastmodified 는 변경 시각 기준으로 새 행과 갱신된 행을 가져온다. 후자는 --merge-key 가 있어야 기존 파일과 병합된다. 반복 실행하려면 sqoop job 으로 등록해 마지막 값을 자동으로 이어받게 한다.
--password 로 평문을 주면 프로세스 목록과 작업 설정에 그대로 남는다. 파일이나 자격 증명 저장소를 쓴다.
echo -n "${DB_PASSWORD}" > db_pw
hdfs dfs -put db_pw /user/etl/.db_pw
hdfs dfs -chmod 400 /user/etl/.db_pw
hadoop credential create db.pw -provider jceks://hdfs/user/etl/etl.jceks
sqoop import -Dhadoop.security.credential.provider.path=jceks://hdfs/user/etl/etl.jceks \
--connect ... --username "${DB_USER}" --password-alias db.pw ...
--password-file 로 지정한 파일은 줄바꿈까지 비밀번호로 취급하므로 echo -n 으로 만든다.
명령행에서 되던 것이 Oozie 에서만 실패하는 원인은 대부분 정해져 있다.
JDBC 드라이버가 없다. Sqoop 액션은 워크플로의 lib/ 디렉터리와 공유 라이브러리에서 jar 을 찾는다. 명령행에서는 /var/lib/sqoop 에 있던 드라이버를 쓰지만 Oozie 실행 컨테이너에는 그 경로가 없다.
hdfs dfs -put ojdbc11.jar /user/oozie/share/lib/lib_<timestamp>/sqoop/
oozie admin -oozie http://oozie-host:11000/oozie -sharelibupdate
oozie admin -oozie http://oozie-host:11000/oozie -shareliblist sqoop
인자 분해가 다르다. <command> 에 한 줄로 적으면 공백으로 잘리므로 공백을 포함한 질의가 깨진다. <arg> 로 하나씩 나눠 적는다.
<action name="sqoop-import">
<sqoop xmlns="uri:oozie:sqoop-action:1.0">
<resource-manager>${resourceManager}</resource-manager>
<name-node>${nameNode}</name-node>
<arg>import</arg>
<arg>--connect</arg>
<arg>jdbc:oracle:thin:@//db-host:1521/ORCLPDB1</arg>
<arg>--username</arg>
<arg>${dbUser}</arg>
<arg>--password-file</arg>
<arg>hdfs:///user/etl/.db_pw</arg>
<arg>--query</arg>
<arg>SELECT id, amt FROM orders WHERE $CONDITIONS</arg>
<arg>--split-by</arg>
<arg>id</arg>
<arg>--target-dir</arg>
<arg>/user/etl/orders</arg>
<arg>--delete-target-dir</arg>
</sqoop>
<ok to="end"/>
<error to="fail"/>
</action>
$CONDITIONS 는 Oozie 의 EL 치환 대상이 아니라 그대로 전달된다. 다만 ${...} 형식과 혼동하지 않도록 질의에 다른 변수를 섞지 않는 편이 안전하다.
실행 사용자가 다르다. Oozie 는 워크플로 제출자 계정으로 실행한다. 대상 HDFS 경로 권한과 Kerberos 자격을 그 계정 기준으로 확인한다.
Error: java.lang.OutOfMemoryError: Java heap space
맵퍼가 한 번에 가져오는 행이 많거나 LOB 컬럼이 큰 경우다.
sqoop import \
-Dmapreduce.map.memory.mb=4096 \
-Dmapreduce.map.java.opts=-Xmx3276m \
--fetch-size 1000 \
...
--fetch-size 로 JDBC 가 한 번에 읽는 행 수를 줄이는 것이 먼저다. 메모리만 키우면 맵퍼 수만큼 클러스터 메모리를 잡아먹는다. LOB 컬럼은 --map-column-java 로 타입을 조정하거나 질의에서 제외한다.
--delete-target-dir 은 실행 때마다 대상 디렉터리를 지운다. 증분 적재와 함께 쓰면 안 된다.