Iceberg 테이블의 데이터 디렉터리에는 Parquet 파일이 그대로 들어 있다. pd.read_parquet() 로 그 디렉터리를 통째로 읽으면 무언가 읽히기는 한다. 하지만 결과가 테이블의 현재 상태와 다르다.
정확한 결과가 필요하면 메타데이터를 읽는 쪽으로 간다. 파일 직접 읽기는 "이 파일에 뭐가 들어 있나" 를 눈으로 확인할 때만 쓴다.
Iceberg 카탈로그를 거쳐 현재 스냅샷을 해석한다. 이것이 정답이다.
pip install "pyiceberg[hive,pyarrow]" pandas
from pyiceberg.catalog import load_catalog
catalog = load_catalog(
"prod",
**{
"type": "hive",
"uri": "thrift://metastore.example.com:9083",
"warehouse": "hdfs://nameservice1/warehouse/tablespace/external/hive",
},
)
table = catalog.load_table("db.sales")
df = (
table.scan(
row_filter="ordered_at >= '2026-01-01'",
selected_fields=("order_id", "amount", "ordered_at"),
)
.to_pandas()
)
row_filter 와 selected_fields 를 주면 메타데이터 단계에서 읽을 파일과 컬럼을 줄인다. 전부 읽은 뒤 pandas 에서 거르는 것과 비용이 크게 다르다.
REST 카탈로그를 쓴다면 설정만 바꾼다.
catalog = load_catalog("prod", **{
"type": "rest",
"uri": "https://catalog.example.com/api/catalog",
"warehouse": "prod",
})
~/.pyiceberg.yaml 에 카탈로그 설정을 두면 코드에서 딕셔너리를 넘기지 않아도 된다.
구조를 눈으로 볼 목적이라면 PyArrow 로 연다.
import pyarrow.parquet as pq
pf = pq.ParquetFile("/tmp/00000-0-xxxx.parquet")
print(pf.schema_arrow)
print(pf.metadata.num_rows, pf.metadata.num_row_groups)
df = pf.read(columns=["order_id", "amount"]).to_pandas()
여러 파일을 한꺼번에 볼 때는 데이터셋으로 연다. 파티션 경로의 값도 함께 컬럼으로 잡아 준다.
import pyarrow.dataset as ds
dataset = ds.dataset("/data/warehouse/db.db/sales/data", format="parquet", partitioning="hive")
table = dataset.to_table(filter=ds.field("dt") == "2026-01-01")
df = table.to_pandas()
다시 말하지만 이 결과는 Iceberg 테이블의 현재 상태가 아니다.
OSError: prior attempt to load libhdfs failed
PyArrow 의 hdfs:// 접근은 Hadoop 의 네이티브 libhdfs.so 를 JNI 로 부른다. 그 라이브러리와 JVM, 그리고 Hadoop 의 CLASSPATH 가 모두 맞아야 한다. 셋 중 하나만 빠져도 위 오류가 난다.
파이썬을 실행하기 전에 환경변수를 잡는다. 파이썬 안에서 os.environ 으로 바꾸면 이미 늦은 경우가 많다.
export JAVA_HOME=/usr/lib/jvm/java-11-openjdk
export HADOOP_HOME=/opt/cloudera/parcels/CDH/lib/hadoop
export ARROW_LIBHDFS_DIR=$HADOOP_HOME/lib/native
export LD_LIBRARY_PATH=$HADOOP_HOME/lib/native:$JAVA_HOME/lib/server:$LD_LIBRARY_PATH
export CLASSPATH=$(hadoop classpath --glob)
확인한다.
ls -l "$ARROW_LIBHDFS_DIR/libhdfs.so"
ls -l "$JAVA_HOME/lib/server/libjvm.so"
echo "$CLASSPATH" | tr ':' '\n' | head
| 빠진 것 | 증상 |
|---|---|
libhdfs.so |
prior attempt to load libhdfs failed |
libjvm.so (LD_LIBRARY_PATH) |
JVM 생성 실패 |
CLASSPATH |
연결은 되는데 ClassNotFoundException |
--glob 없이 hadoop classpath |
와일드카드가 풀리지 않아 클래스를 못 찾는다 |
Kerberos 클러스터라면 kinit 티켓이 있어야 한다.
libhdfs 경로는 환경에 민감해 재현하기 어렵다. 선택지가 있다면 피한다.
S3FileSystem 을 쓴다. JVM 이 필요 없다.httpfs)로 HTTP 접근하는 편이 설정이 훨씬 단순하다.| 목적 | 방법 |
|---|---|
| 테이블의 현재 데이터를 정확히 | PyIceberg table.scan().to_pandas() |
| 대량 집계 | Trino · Spark · Impala 에서 집계하고 결과만 받는다 |
| 특정 파일 내용 확인 | PyArrow ParquetFile |
| 스냅샷 이력·파일 목록 확인 | PyIceberg 의 메타데이터 테이블 (table.inspect) |
pandas 는 메모리에 다 올린다. 수억 행짜리 테이블을 그대로 to_pandas() 하면 죽는다. 필터와 컬럼 선택을 스캔 단계에서 걸거나, to_arrow() 로 받아 Arrow 상태로 처리한다.