首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> DB2 資料庫 >> DB2 教學

DB2 for LUW 管道載入技術詳解:跨平台無中間檔案大量載入方案

     編輯:DB2 教學

動機與挑戰

UNIX/Linux 可用 mkfifo 建立命名管道,配合 DB2 LOAD 實現無中間檔案載入。但 Windows 原生不支援 mkfifo,需呼叫 Windows API 建立命名管道,且管道生命週期限於建立會話;Java 建立的管道無法與 Windows Shell 互通,導致 DB2 LOAD 無法直接使用。

本文提供完整 JNI 包裝方案:C 程式呼叫 Windows API 建立/讀寫管道,Java 透過 JNI 驅動資料流向管道,DB2 LOAD 從管道讀取並載入。

方案架構

  • Java 驅動程式 (TestPipe.java / TestUnixPipe.java):產生資料寫入管道
  • JNI 包裝層 (Pipes.java + Pipe.c):對應 Windows API (CreateNamedPipe, ReadFile, WriteFile, CloseHandle 等)
  • DB2 LOAD 腳本 (db2load.sql):指定管道路徑作為輸入源

IBM Data Movement Tool 內建管道選項,若無客製化需求可直接使用。

Windows 命名管道關鍵 API 對照

功能Windows APIJNI 方法
建立管道CreateNamedPipeCreateNamedPipe
等待連線ConnectNamedPipeConnectNamedPipe
寫入資料WriteFileWriteFile
讀取資料ReadFileReadFile
刷新緩衝FlushFileBuffersFlushFileBuffers
斷開/關閉DisconnectNamedPipe / CloseHandleDisconnectNamedPipe / CloseHandle

核心代碼實作

1. JNI 介面宣告 (Pipes.java)

public class Pipes {
  static { System.loadLibrary("Pipe"); }
  public static final native int CreateNamedPipe(String pipeName, int openMode, 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 object);
  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);
}

2. 生成 JNI Header

javac ibm/Pipes.java
javah -jni ibm.Pipes   # 產生 ibm_Pipes.h,複製至 C 專案目錄

3. C 實作 (Pipe.c 節錄)

JNIEXPORT jint JNICALL Java_ibm_Pipes_CreateNamedPipe(JNIEnv *env, jclass cls, JString sPipeName, jint dwOpenMode, jint dwPipeMode, jint nMaxInstances, jint nOutBufferSize, jint nInBufferSize, jint nDefaultTimeOut, jint lpSecurityAttributes) {
  LPCSTR pipeName = (*env)->GetStringUTFChars(env, sPipeName, NULL);
  HANDLE h = CreateNamedPipe(pipeName, dwOpenMode, dwPipeMode, nMaxInstances, nOutBufferSize, nInBufferSize, nDefaultTimeOut, (LPSECURITY_ATTRIBUTES)lpSecurityAttributes);
  (*env)->ReleaseStringUTFChars(env, sPipeName, pipeName);
  return (jint)h;
}
// ... 其他方法對應 ReadFile, WriteFile, CloseHandle 等

4. 編譯 DLL (Visual Studio / cl.exe)

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

5. Java 測試程式 (TestPipe.java 關鍵邏輯)

private boolean createPipe() {
  namedPipeHandle = Pipes.CreateNamedPipe(pipeName, 0x00000003, 0x00000000, 2, pipeBuffer, pipeBuffer, 0xffffffff, 0);
  return namedPipeHandle != -1;
}
private boolean connectToPipe() {
  boolean connected = Pipes.ConnectNamedPipe(namedPipeHandle, 0);
  if (!connected && Pipes.GetLastError() == 535) connected = true; // ERROR_PIPE_CONNECTED
  return connected;
}
public void runPipe() {
  if (createPipe() && connectToPipe()) {
    FileInputStream in = new FileInputStream(srcFile);
    byte[] buf = new byte[fileBuffer];
    int len;
    while ((len = in.read(buf)) > 0) {
      int written = Pipes.WriteFile(namedPipeHandle, buf, len);
      if (written == -1) log(Pipes.FormatMessage(Pipes.GetLastError()));
    }
    in.close();
    Pipes.FlushFileBuffers(namedPipeHandle);
    Pipes.CloseHandle(namedPipeHandle);
    Pipes.DisconnectNamedPipe(namedPipeHandle);
  }
}

6. UNIX 版本 (TestUnixPipe.java)

直接呼叫 Runtime.exec("mkfifo " + path),再用 FileChannel 寫入,無需 JNI。

執行與同步示範

  1. 啟動 Java 程式:java -Djava.library.path=.. ibm.TestPipe → 建立管道 \\.\pipe\mynamedpipe 並等待連線
  2. 另開 DB2 CLP 視窗執行:db2 -tf db2load.sql

db2load.sql 範例:

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;

在 Windows 命令提示中運行 Java 示例代碼
運行 db2load.sql 的 DB2 CLP 窗口以及運行 TestPipe.Java

同步機制:Java 端 ConnectNamedPipe 阻塞直到 DB2 LOAD 開啟管道;隨後 Java 串流寫入,DB2 同步消費載入,無臨時檔案產生。

IBM Data Movement Tool 內建管道選項

IBM Data Movement Tool 的屏幕快照

勾選 Use Pipe,工具自動建立管道並同步執行 UNLOAD/LOAD,支援多表並行。

結語

管道載入消除中間檔案空間壓力,發揮 DB2 LOAD 平行處理優勢。Windows 下透過 JNI 呼叫原生 API 即可實現,本文提供完整可編譯執行的範例代碼(見下載區),可直接套用或客製化整合至自動化 ETL 流程。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved