WorkaHolic

Linux, UNIX 및 Windows용 DB2에서 파이프를 사용하여 데이터 로드하기

2010. 9. 2. 15:30
반응형

파이프를 처리하는 코드를 작성하여 DB2 Load 명령을 사용하는 방법을 학습한다.

소스 데이터베이스의 데이터를 IBM? DB2? for Linux?, UNIX?, and Windows?으로 이동하기는 매우 어렵습니다. 특히 소스 데이터베이스의 규모가 매우 크고 중간 데이터 파일을 유지할 수 있을 만한 충분한 공간이 없는 경우에는 더욱 그렇습니다. 이 기사를 통해 살펴볼 샘플 코드를 이용하면, 중간 파일을 사용하지 않고도 Windows와 UNIX 환경에서 파이프를 사용하여 데이터를 DB2로 로드할 수 있습니다.

 

이 기사를 쓰게 된 계기

UNIX 및 Linux 환경에서는 mkfifo 명령을 사용하여 Named Pipe를 지정한다. 그런 다음, DB2 LOAD 명령을 실행하여 이 파이프를 사용한다. 그러나 Windows 운영 체제에서는 mkfifo와 같은 외부 명령을 사용하여 Named Pipe를 작성할 수 없다. Windows에서는 Windows API를 통해 Named Pipe를 작성해야 하며 파이프의 범위가 파이프가 작성된 세션 내로 한정된다. 또한, Java로 작성한 Named Pipe는 Windows 쉘과 상호 작용할 수 없으며, 따라서 DB2 LOAD 명령에서 이 파이프를 사용할 수 없다.

이 기사의 다운로드 섹션에 링크된 pipeload.zip 파일에는 이 문제점을 해결할 수 있는 샘플 코드가 있다. 이 샘플 코드를 이용하면 DB2 LOAD 명령에서 사용할 수 있는 Windows 파이프를 작성할 수 있다. 이렇게 하려면 C 프로그램을 통해 Windows API를 호출하는 JNI(Java Native Interface) 랩퍼를 작성한 다음, 이 랩퍼를 통해 Named Pipe를 작성하고 사용해야 한다. 그러면 Java 프로그램을 사용하여 데이터를 Windows 파이프로 전송할 수 있다.

 

샘플 코드 개요

상위 레벨의 샘플 코드에서는 Java 드라이버 프로그램(TestPipe.java 또는 TestUnixPipe.java)을 사용하여 데이터를 Named Pipe로 전송한다. 그러면, db2load.sql에서 별도의 명령 쉘에 있는 파이프를 통해 데이터를 처리한다. 이러한 개념을 사용하는 사례는 IBM Data Movement Tool에서 이미 살펴보았다. (developerWorks에 있는 IBM Data Movement Tool 설명 기사의 참고자료 세션을 참조한다.)

pipeload.zip 다운로드 파일에 포함된 기본 파일에 대한 설명은 다음과 같다.

- Pipe.c ― Windows API를 사용하여 Windows Named Pipe를 작성하고 사용하거나 닫는 C 코드 있음
- Pipes.java ― Pipe.c에서 사용된 모든 Windows 원시 메소드의 랩퍼를 선언하는 JNI(Java Native Interface) 포함
- TestPipe.java ― Windows Named Pipe를 작성하고 사용하거나 닫는 JNI 호출 포함
- TestUnixPipe.java ― UNIX에서 Named Pipe를 작성하고 사용하거나 삭제하는 mkfifo 명령에 대한 시스템 호출 포함
- db2load.sql ― 파이프를 사용하는 DB2 LOAD 명령 포함

Windows의 Named Pipe

UNIX 및 Linux와 마찬가지로 Windows에서 작성하는 Named Pipe는 영구적인 것이 아니며 특수 파일로 작성할 수도 없다. Windows Named Pipe를 파일처럼 액세스할 수 있지만 Windows 탐색기에서는 이 파이프를 볼 수 없다. Windows 파이프는 특수 파일 시스템에 마운트된다. CreateFile, ReadFile, WriteFile 및 CloseHandle과 같은 Windows SDK 함수를 사용하여 파이프에서 읽고 쓰거나 닫을 수 있다.

 

UNIX와 Linux의 Named Pipe

UNIX나 Linux에서는 mkfifo나 mknod 명령을 사용하여 Named Pipe를 작성할 수 있다. 별도의 두 가지 프로세스를 통해 작성한 파이프를 액세스할 수 있다. mkfifo 명령을 사용하여 FIFO(First In First Out) 파일을 작성한다. 그러면, 중간 파일에 데이터를 저장하지 않아도 이 FIFO 파일을 사용하여 하나의 애플리케이션에서 다른 애플리케이션으로 정보를 이동할 수 있다. 따라서 임시 파일을 저장하는 데 사용할 공간을 절약할 수 있다. 또한, mknod 명령을 사용하는 경우에는 유형에 p 옵션을 지정하여 Named Pipe를 작성할 수 있다.

다음 샘플에는 Unix나 Linux 환경에서 Named Pipe를 작성하여 DB2 데이터를 로드하는 두 가지 단계를 수행하는 명령이 표시되어 있다.

1단계 ― 다음과 같이 Named Pipe를 작성한 다음 압축되지 않은 데이터를 파이프로 전송한다.

$ mkfifo db2pipe
$ gunzip tabledata.gz > db2pipe

2단계 ― 다음과 같이 파이프를 사용하여 압축되지 않은 데이터를 DB2로 로드한다.

$ db2 connect to sample
$ db2 "LOAD FROM db2pipe OF DEL INSERT INTO mytable"
$ db2 terminate

 

Windows 원시 메소드를 액세스하는 JNI

Pipe.c 프로그램을 통해 구현된 Windows 원시 API 호출을 다시 가리키는 Java 메소드를 Pipes.java 프로그램의 코드에서 선언한다. 이 메소드의 서명과 일치하도록 헤더 파일(Pipe.c 프로그램에 대한 스펙이 있는)을 작성하려면 javah 프로그램을 통해 Pipes.java를 실행해야 한다.

이 기사에서는 JNI에 대해 자세히 설명하지 않는다.

Listing 1. Pipes.java 파일에서의 JNI 메소드 선언

public class Pipes
{
static
{
System.loadLibrary("Pipe");
}

public static final native int CreateNamedPipe(String pipeName,
int ppenMode, int pipeMode, int maxInstances,
int outBufferSize, int inBufferSize, int defaultTimeOut,
int securityAttributes);

public static final native boolean ConnectNamedPipe(int namedPipeHandle,
int overlapped);
public static final native int GetLastError();
public static final native boolean CloseHandle(int bbject);
public static final native byte[] ReadFile(int file, int numberOfBytesToRead);
public static final native int WriteFile(int file, byte[] buffer,
int numberOfBytesToWrite);
public static final native boolean FlushFileBuffers(int file);
public static final native boolean DisconnectNamedPipe(int namedPipeHandle);
public static final native int CreateFile(String fileName,
int desiredAccess, int shareMode, int securityAttributes,
int creationDisposition, int flagsAndAttributes,
int templateFile);

public static final native boolean WaitNamedPipe(String namedPipeName, int timeOut);
public static final native String FormatMessage(int errorCode);
public static final native void Print(String message);
}

 

JNI 헤더 파일 작성

다음 코드를 사용하여 Pipes.java의 JNI 헤더 파일을 컴파일하고 작성한다.

javac ibm/Pipes.java
javah -jni ibm.Pipes

샘플 코드에 있는 ibm_Pipes.h 파일을 C 프로그램 폴더로 복사한다.

 

Windows 메소드를 랩핑하는 C 코드

Listing 2에 있는 샘플 C 코드는 다운로드한 샘플에 있는 Pipe.c 파일의 일부이다. 이 코드는 이전 단계에서 javah 프로그램을 사용하여 작성한 헤더 파일에 있는 선언과 일치하는 C 함수를 작성한다.

Listing 2. JNI 코드의 샘플 코드 리스팅

#include <windows.h>
#include <strsafe.h>
#include <jni.h>
#include "ibm_Pipes.h"

#define DEBUG 0

JNIEXPORT jint JNICALL Java_ibm_Pipes_CreateNamedPipe
(
JNIEnv *env,
jclass className,
jstring sPipeName,
jint dwOpenMode,
jint dwPipeMode,
jint nMaxInstances,
jint nOutBufferSize,
jint nInBufferSize,
jint nDefaultTimeOut,
jint lpSecurityAttributes
)
{
HANDLE pipeHandler;
LPCSTR pipeName;
pipeName = (*env)->GetStringUTFChars(env, sPipeName, NULL);
if (pipeName == NULL)
return -1;

if (DEBUG)
{
printf("Native: Pipe Name %s\n", pipeName);
printf("Native: dwOpenMode %d\n", dwOpenMode);
printf("Native: dwPipeMode %d\n", dwPipeMode);
printf("Native: nMaxInstances %d\n", nMaxInstances);
printf("Native: nOutBufferSize %d\n", nOutBufferSize);
printf("Native: nInBufferSize %d\n", nInBufferSize);
printf("Native: nDefaultTimeOut %d\n", nDefaultTimeOut);
}

pipeHandler = CreateNamedPipe((LPCSTR)pipeName, dwOpenMode,
dwPipeMode, nMaxInstances, nOutBufferSize,
nInBufferSize, nDefaultTimeOut,
(LPSECURITY_ATTRIBUTES) lpSecurityAttributes);

(*env)->ReleaseStringUTFChars(env, sPipeName, pipeName);
return (jint) pipeHandler;
}

JNIEXPORT jboolean JNICALL Java_ibm_Pipes_ConnectNamedPipe
(
JNIEnv *env,
jclass className,
jint hNamedPipe,
jint lpOverlapped
)
{
BOOL fConnected;
HANDLE pipeHandler = (HANDLE) hNamedPipe;
fConnected = ConnectNamedPipe(pipeHandler,
(LPOVERLAPPED) lpOverlapped);
return fConnected;
}

JNIEXPORT jint JNICALL Java_ibm_Pipes_GetLastError
(
JNIEnv *env,
jclass className
)
{
DWORD errorNumber = GetLastError();
return (jint) errorNumber;
}

JNIEXPORT jboolean JNICALL Java_ibm_Pipes_CloseHandle
(
JNIEnv *env,
jclass className,
jint hNamedPipe
)
{
BOOL result;
HANDLE pipeHandler = (HANDLE) hNamedPipe;
result = CloseHandle(pipeHandler);
return result;
}

JNIEXPORT jbyteArray JNICALL Java_ibm_Pipes_ReadFile
(
JNIEnv *env,
jclass className,
jint hNamedPipe,
jint nNumberOfBytesToRead
)
{
int bytesRead = 0;
BOOL result;
HANDLE pipeHandler = (HANDLE) hNamedPipe;
LPVOID buffer;
jbyteArray lpBuffer;

buffer = (LPVOID)LocalAlloc(LMEM_ZEROINIT, nNumberOfBytesToRead);

if (DEBUG)
{
printf("Native: Before ReadFile pipeHandler %d
nNumberOfBytesToRead %d\n", pipeHandler, nNumberOfBytesToRead);
}
result = ReadFile(pipeHandler, (LPVOID) buffer,
(DWORD) nNumberOfBytesToRead,
&bytesRead, (LPOVERLAPPED) 0);
if (result)
{
lpBuffer = (*env)->NewByteArray(env, (jsize) bytesRead);
(*env)->SetByteArrayRegion(env, lpBuffer, 0,
(jsize) bytesRead, (jbyte *) buffer);
} else
bytesRead = 0;

LocalFree(buffer);

if (DEBUG)
{
printf("Native: After ReadFile BytesRead %d\n", bytesRead);
}
return lpBuffer;
}

JNIEXPORT jint JNICALL Java_ibm_Pipes_WriteFile
(
JNIEnv *env,
jclass className,
jint hNamedPipe,
jbyteArray lpBuffer,
jint nNumberOfBytesToWrite
)
{
int bytesWritten = 0;
BOOL result;
HANDLE pipeHandler = (HANDLE) hNamedPipe;
LPVOID buffer;

buffer = (LPVOID)LocalAlloc(LMEM_ZEROINIT, nNumberOfBytesToWrite);

(*env)->GetByteArrayRegion(env, lpBuffer, 0,
nNumberOfBytesToWrite, buffer);
result = WriteFile(pipeHandler, buffer,
(DWORD) nNumberOfBytesToWrite,
(LPDWORD) &bytesWritten, (LPOVERLAPPED) 0);
LocalFree(buffer);

if (DEBUG)
{
printf("Native: After WriteFile BytesReadWritten %d\n",
bytesWritten);
}

if (!result)
{
if (GetLastError() != ERROR_IO_PENDING)
result = 0;
else
result = 1;
}
if (!result)
{
bytesWritten = -1;
}
return bytesWritten;
}

JNIEXPORT jboolean JNICALL Java_ibm_Pipes_FlushFileBuffers
(
JNIEnv *env,
jclass className,
jint hNamedPipe
)
{
BOOL result;
HANDLE pipeHandler = (HANDLE) hNamedPipe;
result = FlushFileBuffers(pipeHandler);
return result;
}

JNIEXPORT jboolean JNICALL Java_ibm_Pipes_DisconnectNamedPipe
(
JNIEnv *env,
jclass className,
jint hNamedPipe
)
{
BOOL result;
HANDLE pipeHandler = (HANDLE) hNamedPipe;
result = DisconnectNamedPipe(pipeHandler);
return result;
}

JNIEXPORT jint JNICALL Java_ibm_Pipes_CreateFile
(
JNIEnv *env,
jclass className,
jstring lpFileName,
jint dwDesiredAccess,
jint dwShareMode,
jint lpSecurityAttributes,
jint dwCreationDisposition,
jint dwFlagsAndAttributes,
jint hTemplateFile
)
{
HANDLE pipeHandler;
const jbyte *fileName;
fileName = (*env)->GetStringUTFChars(env, lpFileName, NULL);
if (fileName == NULL)
return -1;
pipeHandler = CreateFile((LPCSTR) fileName,
(DWORD) dwDesiredAccess, (DWORD) dwShareMode,
(LPSECURITY_ATTRIBUTES) lpSecurityAttributes,
(DWORD) dwCreationDisposition,
(DWORD) dwFlagsAndAttributes,
(HANDLE) hTemplateFile);
return (jint) pipeHandler;
}

JNIEXPORT jboolean JNICALL Java_ibm_Pipes_WaitNamedPipe
(
JNIEnv *env,
jclass className,
jstring lpNamedPipeName,
jint nTimeOut
)
{
BOOL result;
const jbyte *pipeName;
pipeName = (*env)->GetStringUTFChars(env, lpNamedPipeName, NULL);
if (pipeName == NULL)
return 0;
result = WaitNamedPipe((LPCSTR) pipeName, (DWORD) nTimeOut);
return result;
}

JNIEXPORT jstring JNICALL Java_ibm_Pipes_FormatMessage
(
JNIEnv *env,
jclass className,
jint errorCode
)
{
LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
DWORD dw = (DWORD) errorCode;

FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0, NULL );

lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
(lstrlen((LPCTSTR)lpMsgBuf) + 40) * sizeof(TCHAR));
StringCchPrintf((LPTSTR)lpDisplayBuf,
LocalSize(lpDisplayBuf) / sizeof(TCHAR),
TEXT("Failed with error %d: %s"), dw, lpMsgBuf);
return (jstring) (*env)->NewStringUTF(env, lpDisplayBuf);
}

JNIEXPORT void JNICALL Java_ibm_Pipes_Print(JNIEnv *env,
jclass className,
jstring lpMsgBuf)
{
const jbyte *str;
str = (*env)->GetStringUTFChars(env, lpMsgBuf, NULL);
if (str == NULL)
return;
printf("Native: %s\n", str);
(*env)->ReleaseStringUTFChars(env, lpMsgBuf, str);
return;
}

 

Windows에서 C 프로그램 컴파일

DLL(Dynamic Link Library)을 컴파일하고 작성하려면 Windows cl.exe 컴파일러가 필요하다. 아직 cl.exe가 없는 경우에는 Microsoft Visual Studio Express Edition을 다운로드하면 이 컴파일러를 얻을 수 있다. 이 기사에서는 Visual Studio를 사용하여 C 프로그램을 컴파일하는 방법에 관한 지시사항은 설명하지 않는다. 그러나 명령행에서 간단히 cl.exe를 사용하여 다음과 같이 DLL을 작성할 수 있다.

cl -I"C:\Program Files\IBM\Java50\include" -I"C:\Program Files\IBM\Java50\include\win32"
-LD Pipe.c -FePipe.dll

cl.exe를 사용하여 DLL을 작성할 경우에는 ibm_Pipes.h 파일을 현재 디렉토리로 복사한 다음, 참조를 해당 시스템에 있는 디렉토리의 실제 위치가 있는 Java 인클루드 디렉토리로 바꾸어야 한다.

 

Windows 파이프를 사용하는 샘플 Java 프로그램

Pipes.h 헤더 파일에서 선언하고 Pipe.c 코드에서 구현한 원시 메소드를 사용하여 Windows에서 Named Pipe를 작성하려면 Listing 3에 있는 코드를 사용한다.

Listing 3. Windows 파이프를 사용하는 샘플 TestPipe.java 프로그램

package ibm;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

public class TestPipe
{
static final int ERROR_PIPE_CONNECTED = 535;
static final int ERROR_BROKEN_PIPE = 109;
private int namedPipeHandle;
private String pipeName, srcFile;
private int pipeBuffer = 131072, fileBuffer = 8192;

public TestPipe(String pipeName, String srcFile)
{
this.pipeName = pipeName;
this.srcFile = srcFile;
}

private void log(String message)
{
System.out.println(message);
}

private boolean createPipe()
{
boolean ok = false;
namedPipeHandle = Pipes.CreateNamedPipe(pipeName,
0x00000003, 0x00000000, 2, pipeBuffer,
pipeBuffer, 0xffffffff, 0);
if (namedPipeHandle == -1)
{
log("CreateNamedPipe failed for " + pipeName +
" for error " + " Message " +
Pipes.FormatMessage(Pipes.GetLastError()));
ok = false;
} else
{
log("Named Pipe " + pipeName +
" created successfully Handle=" + namedPipeHandle);
ok = true;
}
return ok;
}

private boolean connectToPipe()
{
log("Waiting for a client to connect to pipe " + pipeName);
boolean connected = Pipes.ConnectNamedPipe(namedPipeHandle, 0);
if (!connected)
{
int lastError = Pipes.GetLastError();
if (lastError == ERROR_PIPE_CONNECTED)
connected = true;
}
if (connected)
{
log("Connected to the pipe " + pipeName);
} else
{
log("Falied to connect to the pipe " + pipeName);
}
return connected;
}

public void runPipe()
{
if (createPipe())
{
if (!connectToPipe())
{
log("Connect ConnectNamedPipe failed : " +
Pipes.FormatMessage(Pipes.GetLastError()));
return;
} else
{
log("Client connected.");
}

try
{
File f1 = new File(this.srcFile);
InputStream in = new FileInputStream(f1);
log("Sending data to the pipe");
byte[] buf = new byte[fileBuffer];
int len, bytesWritten;
while ((len = in.read(buf)) > 0)
{
bytesWritten = Pipes.WriteFile(namedPipeHandle, buf, len);
log("Sent " + len + "/" + bytesWritten +
" bytes to the pipe");
if (bytesWritten == -1)
{
int errorNumber = Pipes.GetLastError();
log("Error Writing to pipe " +
Pipes.FormatMessage(errorNumber));
}
}
in.close();
Pipes.FlushFileBuffers(namedPipeHandle);
Pipes.CloseHandle(namedPipeHandle);
Pipes.DisconnectNamedPipe(namedPipeHandle);
log("Writing to the pipe completed.");
} catch (Exception e)
{
e.printStackTrace();
}
}
}

public static void main(String[] args) throws IOException,
InterruptedException
{
String pipeName = "\\\\.\\pipe\\mynamedpipe";
String fileName = "C:\\db2tabledata.txt";;
TestPipe testPipe = new TestPipe(pipeName, fileName);
testPipe.runPipe();
}
}

 

UNIX 파이프를 사용하는 샘플 Java 프로그램

UNIX에서는 C 프로그램을 호출하여 Named Pipe를 작성하는 Java 코드가 필요 없다. UNIX에서는 mkfifo나 mknod 명령을 사용하여 Named Pipe를 작성할 수 있기 때문이다. Listing 4에 있는 코드에는 UNIX에서 Named Pipe를 사용하는 예제가 표시되어 있다.

Listing 4. UNIX 파이프를 사용하는 샘플 TestUnixPipe.java 프로그램

package ibm;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class TestUnixPipe
{
FileChannel fc;
int multiTables[] = new int[1];
String filesep = System.getProperty("file.separator");
String fileName, OUTPUT_DIR = ".", pipeName;
int pipeBuffer = 131072, fileBuffer = 8192;

public TestUnixPipe(String fileName, String output)
{
this.fileName = fileName;
this.OUTPUT_DIR = output;
multiTables[0] = 0;
}

private void log(String message)
{
System.out.println(message);
}

public void runPipe()
{
int bytesReturn;
pipeName = OUTPUT_DIR + "data" + filesep + pipeName + ".pipe";
File pipeFile = new File(pipeName);
pipeFile.deleteOnExit();
if (!pipeFile.exists())
{
try
{
Runtime.getRuntime().exec("mkfifo " +
pipeFile.getAbsolutePath());
} catch (Exception e)
{
e.printStackTrace();
}
}
FileOutputStream fos = null;
try
{
if (multiTables[0] == 0)
{
fos = new FileOutputStream(pipeFile);
fc = fos.getChannel();
} else
{
fc = fc;
}
} catch (Exception e)
{
e.printStackTrace();
}
try
{
File f1 = new File(this.fileName);
InputStream in = new FileInputStream(f1);
log("Sending data to the pipe");
byte[] buf = new byte[fileBuffer];
int len;
while ((len = in.read(buf)) > 0)
{
bytesReturn = fc.write(ByteBuffer.wrap(buf));
log("Sent " + len + "/" + bytesReturn +
" bytes to the pipe");
if (bytesReturn == -1)
{
log("Error Writing to pipe " + pipeName);
}
}
in.close();
log("Writing to the pipe completed.");
} catch (Exception e)
{
e.printStackTrace();
}
}

public static void main(String[] args) throws IOException,
InterruptedException
{
String output = ".";
String fileName = "/home/db2inst1/db2tabledata.txt";;
TestUnixPipe testPipe = new TestUnixPipe(fileName, output);
testPipe.runPipe();
}
}

 

Windows 파이프를 사용한 TestPipe.java 프로그램 실행

다음 명령을 사용하여 샘플 TestPipe.java 프로그램(Listing 3)을 컴파일하고 실행한다.

javac TestPipe.java
java -Djava.library.path=".." ibm.TestPipe

위 예제에서는 JVM에서 Windows 원시 메소드를 호출하는 Pipe.dll을 찾을 수 있는 위치를 표시하기 위해 java.library.path 시스템 특성을 JVM에 전달한다. 이 예제에서는 Pipe.dll이 현재 디렉토리보다 한 레벨 위의 디렉토리에 있다.

TestPipe를 실행하면 \\.\pipe\mynamedpipe Named Pipe가 작성되어 파이프에 연결된다. 그런 다음, 다른 프로그램(이 경우, DB2 LOAD 명령)이 파이프에 연결되기를 기다리고 나서 데이터를 파이프에 쓰기 시작한다. DB2 LOAD 명령이 파이프에 연결되면 Java 프로그램이 db2tabledata.txt 파일의 내용을 파이프로 전송하기 시작한다. 그러면 DB2 LOAD 명령을 통해 데이터가 처리된다.

그림 1. Windows 명령 프롬프트에서 Java 샘플 코드 실행

파이프를 사용하여 테이블을 처리하는 DB2 Load 스크립트

이전 섹션에서 살펴본 바와 같이 TestPipe 프로그램은 파이프에 연결되고 나서 다른 프로그램이 파이프에 연결될 때까지 기다린다. 다른 프로그램이 연결되게 하려면 샘플 db2load.sql DB2 스크립트(Listing 5)를 실행한다. 그러면 테이블이 하나 작성되고 mynamedpipe에서 로드를 시작한다.

Listing 5. 테이블을 작성하고 나서 로드를 시작하는 샘플 DB2 스크립트

CONNECT TO SAMPLE;

CREATE TABLE PIPE_TABLE
(
"ID" NUMBER(10) NOT NULL ,
"NAME" VARCHAR2(35) ,
"LOC_ID" NUMBER(4)
)
;

LOAD FROM
"\\.\pipe\mynamedpipe"
OF DEL
MODIFIED BY CODEPAGE=1208 COLDEL~ ANYORDER
USEDEFAULTS CHARDEL"" DELPRIORITYCHAR NOROWWARNINGS
METHOD P (1,2,3)
MESSAGES "db2tabledata.txt"
REPLACE INTO PIPE_TABLE
(
"ID",
"NAME",
"LOC_ID"
)
NONRECOVERABLE
INDEXING MODE AUTOSELECT
;

TERMINATE;

db2load.sql 스크립트를 호출하려면, DB2 CLP(Command Line Processor) 창을 열고서 다음 명령을 실행한다.

db2 -tf db2load.sql

그림 2에는 db2load.sql 스크립트와 TestPipe.java 프로그램 간에 동시에 발생하는 데이터 처리 과정이 표시되어 있다. DB2 LOAD 명령이 Windows Named Pipe에 연결되면 TestPipe.java 프로그램이 파이프에 데이터를 쓰기 시작한다. 이와 동시에 DB2 LOAD 명령을 통해 데이터가 처리되어 DB2로 로드된다.

그림 2. db2load.sql을 실행 중인 DB2 CLP 창과 TestPipe를 실행하는 TestPipe.java를 실행 중인 Windows 명령 프롬프트

 

TestUnixPipe.java 프로그램을 실행하여 UNIX 파이프 사용하기

UNIX 시스템에서 TestUnixPipe.java 프로그램을 실행하면, 위에서 Windows 환경을 대상으로 설명한 것과 같은 동일한 유형의 작동을 확인할 수 있다. 물론, UNIX에 있는 기능을 사용하여 파이프를 직접 작성하는 경우는 예외이다.

 

IBM Data Movement Tool에서 파이프 사용하기

Java나 C/C++ 프로그램을 통해 데이터를 처리해야 하고 주로 중간 데이터 파일을 사용하지 않으려고 파이프를 사용하는 경우에는 이 기사에 있는 샘플 코드를 사용자 정의하여 사용할 수 있다. 그러나 파이프를 사용하는 목적이 단지 소스 데이터베이스에서 데이터를 언로드하고 나서 파이프를 사용하여 DB2로 로드하는 데 있다면, 그림 3과 같이 IBM Data Movement Tool에서 Use Pipe 옵션을 사용하면 간단히 처리할 수 있다.

IBM Data Movement Tool을 사용하여 먼저 소스 데이터베이스에서 테이블 정의를 추출하여 DB2에 테이블 정의를 작성하고 나서 Use Pipe 옵션을 사용하여 데이터를 로드한다. 이 작업은 언로드 작업과 병렬로 처리할 수 있다. 하나 또는 다수의 테이블에 있는 데이터를 동시에 로드하고 언로드할 수 있다. IBM Data Movement Tool에 관한 자세한 정보와 사용 방법은 참고자료 섹션에 있는 IBM Data Movement Tool 기사를 참조한다.

그림 3. IBM Data Movement Tool의 스크린샷

 

결론

DB2 LOAD 유틸리티는 기능이 매우 강력하며 이 유틸리티를 사용하면 서버의 하드웨어를 최대한 활용하여 데이터를 로드할 수 있다. 그러나 대규모의 데이터베이스 환경에서는 중간 데이터를 유지할 공간이 문제가 될 수 있다. 파이프의 기능을 사용하면 이러한 문제를 해결할 수 있다. Windows 환경에서 사용하는 경우를 제외하면, 플랫폼 간에 파이프를 사용하는 과정은 보통 파일을 사용하는 경우와 동일하다. Windows 환경에서 파이프를 작성하려면 별도의 단계가 필요하며 Java 프로그램에서 JNI를 사용하여 Windows API를 액세스해야 한다. 다운로드 파일에 포함된 샘플 코드와 함께 제공되는 이 기사의 지시사항을 따르면 이러한 프로세스를 단순하게 할 수 있으며 이러한 태스크 유형을 쉽게 처리할 수 있다.

 

 

필자소개

Vikram Vaswani는 오픈 소스 도구와 기술을 전문으로 다루는 컨설팅 서비스 회사인 Melonfire의 창립자이자 CEO이며 PHP Programming Solutions 및 PHP: A Beginners Guide의 저자이기도 하다.

 

출처 : 한국 IBM

반응형