P
PipeCraft

Roadmap Progress

ข้อมูลเบื้องต้นเกี่ยวกับวิศวกรรมข้อมูล (Introduction to Data Engineering) Python สำหรับ Data Engineering พื้นฐาน Linux & CLI สำหรับวิศวกรข้อมูล Git สำหรับระบบงานข้อมูลและทีมพัฒนา ระบบเครือข่ายและระบบประมวลผลแบบกระจายศูนย์ (Networking & Distributed Systems) SQL ขั้นสูง (Window Functions & Optimization) ฐานข้อมูลไม่ใช่เชิงสัมพันธ์ (NoSQL Databases & Modern Stores) การออกแบบโครงสร้างข้อมูล (Star/Snowflake & SCD) โครงสร้างพื้นฐานเครือข่ายระบบคลาวด์ (Cloud-based Networking) สถาปัตยกรรม Data Lakehouse (Apache Iceberg & MinIO) Local-first Data Engineering ด้วย DuckDB โครงสร้างพื้นฐานในรูปแบบโค้ด (IaC ด้วย Terraform) การแปลงข้อมูลระดับโปรด้วย dbt-core ข้อตกลงร่วมด้านข้อมูล (Data Contracts) การบริการและการส่งต่อข้อมูลวิเคราะห์ (Data Serving & Reverse ETL) การประมวลผลข้อมูลขนาดใหญ่แบบกระจายด้วย Apache Spark ระบบจัดการเวิร์กโฟลว์ข้อมูล (Airflow, Dagster & Prefect) การประมวลผลข้อมูลแบบเรียลไทม์ด้วย Kafka & Redpanda การจัดการคอนเทนเนอร์และคลัสเตอร์ (Containers & Kubernetes) ระบบ CI/CD และการเฝ้าระวังคุณภาพข้อมูล (CI/CD, Monitoring & Testing) ระบบความปลอดภัยและการควบคุมข้อมูล (Security, Governance & Privacy) ระบบปฏิบัติการและประมวลผลโมเดล (Machine Learning & MLOps)
บทที่ 3: คลังข้อมูลคลาวด์และสถาปัตยกรรมยุคใหม่ (Modern Warehouse & Lakehouse)

สถาปัตยกรรม Data Lakehouse (Apache Iceberg & MinIO)

3.1 สถาปัตยกรรม Data Lakehouse และกลไก Apache Iceberg

สถาปัตยกรรม Data Lakehouse เป็นการผสานรวมจุดเด่นด้านต้นทุนต่ำและขยายตัวได้ง่ายของ Object Storage เข้ากับโครงสร้างประวัติข้อมูลและระบบธุรกรรม ACID ของคลังข้อมูล เพื่อการันตีความถูกต้องของสกีมาในทุกการประมวลผล

Data Lakehouse Architecture Diagram

3D Isometric Open Table Format Data Lakehouse Architecture

1. สถาปัตยกรรม Lakehouse และธุรกรรม ACID (Decoupled Storage & Compute)

ทฤษฎีและกลไกการทำงาน (How it works): จุดเริ่มต้นของ Lakehouse คือการแยกส่วนประมวลผล (Compute) ออกจากส่วนจัดเก็บข้อมูล (Storage) อย่างเด็ดขาด โดยการใช้ระบบ Metadata Layer (เช่น Apache Iceberg, Delta Lake, Hudi) ครอบทับข้อมูลไฟล์ Parquet ใน Object Storage กลไกนี้ช่วยให้สามารถรันคำสั่ง ACID Transactions ได้เหมือนฐานข้อมูลแบบเดิม โดยใช้ระบบเลขเวอร์ชันกำกับ และนำการเขียนพร้อมกันมาตรวจสอบผ่านกลไก Optimistic Concurrency Control (OCC) โดยไม่มีโหนดศูนย์กลางล็อกไฟล์ประมวลผล

-- Spark SQL demonstrating ACID updates on an Iceberg table
BEGIN TRANSACTION;
UPDATE iceberg_prod.marts.customer_orders
SET order_status = 'COMPLETED'
WHERE order_id = 'ord_90124';
COMMIT;
                    

Use Case ในชีวิตจริง (Real-world Scenario): คลังข้อมูลระดับเทราไบต์ของสายการบิน นำเข้าพฤติกรรมการจองตั๋วแบบสตรีมมิ่งตลอดเวลาในขณะที่ทีมการเงินรันรวบรวมข้อมูลบัญชีสิ้นวันพร้อมกัน ระบบ Lakehouse ช่วยป้องกันไม่ให้การคำนวณสะดุดหรือได้ค่ารายงานการเงินที่คลาดเคลื่อน

ข้อควรระวังและวิธีแก้ (Pitfalls & Mitigations): หากเครื่องวิเคราะห์รันเขียนข้อมูลชนกันถ่วงเวลาพร้อมกันบ่อยครั้ง จะทำให้คำสั่งยกเลิกตัวเองเกิดบ่อยจากข้อผิดพลาด OCC (Concurrent Append Exceptions) แก้ไขโดยตั้งค่าจำกัดขอบเขตการเขียนให้สั้นที่สุด หรือกระจายสเปคพาร์ทิชันย่อยเพื่อหลีกเลี่ยงการเขียนลงตำแหน่งเดียวกัน

2. โครงสร้างเมทาดาต้าของ Apache Iceberg (Metadata Layers)

ทฤษฎีและกลไกการทำงาน (How it works): Apache Iceberg ทำงานโดยแบ่งสัดส่วนเมทาดาต้าออกเป็น 4 ระดับชั้นแบบต้นไม้กลับหัว: 1. **Iceberg Catalog**: ชี้พิกัดไปยังไฟล์ Metadata JSON ตัวล่าสุดอย่างเป็นทางการ 2. **Metadata File (JSON)**: เก็บลายเซ็นประวัติสกีมาตาราง (Schema Spec) ขอบเขตพาร์ทิชัน และบันทึกประวัติการบันทึกสแนปช็อต (Snapshots) 3. **Manifest List (Avro)**: เก็บไฟล์รายการของสแนปชอตเดี่ยว บันทึกค่าพารามิเตอร์ขอบเขตพาร์ทิชันเพื่อข้ามการเปิดดูโฟลเดอร์ที่ไม่เกี่ยวข้อง 4. **Manifest File (Avro)**: บันทึกรายชื่อไฟล์ข้อมูลดิบจริง (Data Files ในรูปแบบ Parquet/ORC) รวมถึงค่าสถิติ Min/Max ของแต่ละคอลัมน์ในไฟล์นั้น

# Abstract concept of Iceberg metadata tree hierarchy
[Iceberg Catalog Pointer]
       |
       v
[Metadata File JSON (Schema & Snapshots)]
       |
       v
[Manifest List Avro (Snapshot Specific)]
       |
       v
[Manifest File Avro (Data file stats)]
       |
       v
[Data Files (Parquet, Parquet, ...)]
                    

Use Case ในชีวิตจริง (Real-world Scenario): การย้อนเวลาข้อมูลวิเคราะห์ (Time Travel) ไปดูตารางยอดสั่งซื้อ ณ วันที่ 1 มกราคม 2026 เอนจิ้นคิวรี่จะไปไล่อ่านจากไฟล์ Metadata JSON เพื่อดึงลิสต์ของ Snapshot ในวันนั้น ทำให้สามารถสืบค้นรายการประวัติจริงย้อนหลังได้อย่างรวดเร็วและประหยัดงบคิวรี

ข้อควรระวังและวิธีแก้ (Pitfalls & Mitigations): ไฟล์สแนปช็อตเก่าและไฟล์ขยะจะสะสมขนาดใหญ่โตขึ้นเรื่อยๆ จนเต็มดิสก์โฮสต์ แก้ไขโดยใช้ระบบตั้งเวลาบดขยะตาราง (Expire Snapshots และ Remove Orphan Files) สม่ำเสมอเพื่อประหยัดค่าใช้จ่ายจัดเก็บ

3. กลไก Optimistic Concurrency Control (OCC) และ Catalog Pointer Locks

ทฤษฎีและกลไกการทำงาน (How it works): ต่างจาก RDBMS แบบดั้งเดิมที่รันล็อกตาราง (Pessimistic Lock) Iceberg ใช้ระบบ **Optimistic Concurrency Control (OCC)** ซึ่งอยู่บนสมมติฐานว่าการชนกันของคำสั่งเขียนมีโอกาสเกิดน้อย ทุกโปรเซสจะเขียนข้อมูลลงดิสก์ส่วนตัวของตัวเองโดยปราศจากการล็อก เมื่อถึงขั้นตอน Commit ระบบจะนำค่าแฮชเวอร์ชันล่าสุดมาเช็คเทียบกับ Catalog หากมีผู้อื่น Commit หน้าไปก่อนแล้ว ทรานแซกชันล่าสุดจะถูกยกเลิก (Abort) เพื่อป้องกันการเขียนทับซ้อนและหลีกเลี่ยงสถานะข้อมูลไม่นิ่ง

# Simulated retry loop script to handle OCC conflicts in Iceberg
import time

def commit_transaction_with_retry(data_payload, max_retries=3):
    retries = 0
    while retries < max_retries:
        try:
            # Attempt atomic catalog update
            # catalog.commit(data_payload)
            print("Commit successful!")
            return True
        except Exception as e: # Catch concurrent commit exceptions
            retries += 1
            print(f"Commit conflict detected. Retrying... {retries}/{max_retries}")
            time.sleep(2 ** retries) # Exponential backoff
    raise Exception("Failed to commit after maximum retries.")
                    

Use Case ในชีวิตจริง (Real-world Scenario): ระบบปรับยอดสมาชิกอัตโนมัติที่รันทำงานพร้อมกันจากสองท่อส่งข้อมูล ข้อมูลยอดใหม่จะถูกคำนวณและเขียนลงไฟล์ Parquet ล่วงหน้าได้ขนานกันโดยไม่ต้องบล็อกเวลารัน จากนั้นจึงมาทำการจัดล็อกต่อคิวสุดท้ายที่ระดับ Catalog เพียงเสี้ยววินาที

ข้อควรระวังและวิธีแก้ (Pitfalls & Mitigations): สภาวะ **Commit Conflict Storm** เกิดเมื่อมีตัวรันระบบประมวลผลขนาดเล็กจำนวนมากพยายามยิงคอมมิตเข้าหาตารางเดียวพร้อมกัน แก้ไขโดยเปลี่ยนมาใช้งานระบบ Catalogs ที่มีความมั่นคงสูงในการล็อกอะตอมิก (เช่น Apache Hive Metastore หรือ AWS Glue Catalog)

4. การวิวัฒน์พาร์ทิชันและการแบ่งพาร์ทิชันแบบซ่อน (Partition Evolution)

ทฤษฎีและกลไกการทำงาน (How it works): ในสถาปัตยกรรมข้อมูลยุคเก่า การจัดหมวดพาร์ทิชันตาราง (เช่น แยกโฟลเดอร์ตามปี `year=2026/`) หากต้องการสลับรูปแบบพาร์ทิชันใหม่ (เช่น เปลี่ยนเป็นแยกตามเดือน) วิศวกรต้องสร้างตารางใหม่และกวาดเขียนข้อมูลย้อนหลังทั้งหมด (Rewrite history) แต่ Apache Iceberg รองรับกลไก **Partition Evolution** ข้อมูลใหม่จะถูกเขียนแยกตามกฎพาร์ทิชันตัวใหม่ ส่วนข้อมูลประวัติเก่าจะคงอยู่ในโฟลเดอร์พาร์ทิชันเดิม โดยมีตัว Metadata คอยจัดการแมปช่วงคำสั่งคิวรี่ประสานงานย้อนหลังให้ผู้ใช้เองโดยอัตโนมัติ

-- Spark SQL demonstrating partition evolution in Iceberg
-- 1. Create table partitioned by month
CREATE TABLE iceberg_prod.db.user_clicks (
    user_id string, click_time timestamp, action string
) USING iceberg PARTITIONED BY (month(click_time));

-- 2. Evolve partitioning scheme to partition by day without rewriting history
ALTER TABLE iceberg_prod.db.user_clicks ADD PARTITION FIELD day(click_time);
                    

Use Case ในชีวิตจริง (Real-world Scenario): ธุรกิจสตาร์ทอัปที่มีข้อมูลผู้ใช้วิ่งเข้ามาต่อวันมีจำนวนเพิ่มขึ้น 100 เท่าตัว จากเดิมที่เคยจัดเก็บพาร์ทิชันตามระดับรายเดือน ปรับเปลี่ยนรูปแบบเป็นพาร์ทิชันรายวันได้อย่างรวดเร็วผ่านคำสั่ง `ALTER TABLE` เพื่อเร่งความเร็วในการคิวรี่ประจำวันโดยไม่ต้องทำลายตารางข้อมูลเดิม

ข้อควรระวังและวิธีแก้ (Pitfalls & Mitigations): แม้จะทำ Partition Evolution ได้ง่าย แต่ต้องระมัดระวังไม่แยกสัดส่วนย่อยเกินไป (Over-partitioning เช่น พาร์ทิชันข้อมูลรายชั่วโมงบนตารางที่มีการอัปเดตหลักสิบแถว) ซึ่งจะส่งผลให้ไฟล์มีขนาดเล็กเกินไปเป็นภาระต่อระบบ Metadata แก้ไขโดยหมั่นตรวจประเมินรูปแบบการฟิลเตอร์เฉลี่ยของทีมวิเคราะห์

5. การบำรุงรักษาตาราง Lakehouse: Compaction, Expiry, และ Orphan Cleanup

ทฤษฎีและกลไกการทำงาน (How it works): เพื่อรักษาประสิทธิภาพการอ่านและเขียนข้อมูลให้คงอยู่เสมือนเพิ่งสร้างตารางใหม่ แพลตฟอร์มต้องการการรันงานดูแลสภาพระบบอย่างสม่ำเสมอ: - **Compaction (Bin-Packing)**: ยุบรวมไฟล์ย่อยๆ ขนาดไม่กี่กิโลไบต์ที่สร้างขึ้นบ่อยจากท่อส่งข้อมูลจำลอง ให้หลอมรวมเป็นไฟล์ใหญ่ขนาดมาตรฐาน (เช่น 128MB - 256MB) - **Snapshot Expiration**: ลบรายการประวัติแฮชที่เก่าเกินข้อกำหนดเพื่อประหยัดเนื้อที่การเก็บข้อมูลในคลัง - **Orphan Files Cleanup**: ลบไฟล์ Parquet ที่ลอยเคว้งอยู่ในดิสก์ซึ่งไม่มีตัวชี้แฮชใดๆ ในไฟล์ Metadata มาจองตำแหน่งใช้งาน

# Spark SQL command schema to optimize Iceberg table structure
-- 1. Execute Bin-packing compaction (optimize file layouts)
CALL iceberg_prod.system.rewrite_data_files(
    table => 'db.user_clicks',
    strategy => 'sort',
    sort_order => 'click_time DESC'
);

-- 2. Expire old snapshots older than 7 days
CALL iceberg_prod.system.expire_snapshots(
    table => 'db.user_clicks',
    older_than => TIMESTAMP '2026-07-08 00:00:00.000'
);
                    

Use Case ในชีวิตจริง (Real-world Scenario): ท่อสตรีมข้อมูล Kafka วางข้อมูลลง Object Storage ตลอด 24 ชั่วโมง เกิดไฟล์เล็กสะสมเป็นล้านไฟล์ ทำให้คำสั่งวิเคราะห์ประมวลผลแทบหยุดทำงาน ทีมวิศวกรวิข้อมูลเปิดใช้งานระบบจ๊อบรัน Compaction ทุกคืนช่วยย่นระยะเวลาการสืบค้นข้อมูลประวัติย้อนหลังได้สูงสุด

ข้อควรระวังและวิธีแก้ (Pitfalls & Mitigations): การรันคำสั่งบำรุงรักษาตารางในช่วงเวลาเร่งด่วนที่มีผู้ใช้งานระบบงานหนาแน่น อาจแย่งพื้นที่ทรัพยากรระบบประมวลผลจนทำให้ผู้ใช้งานปกติใช้งานหน้าเว็บล่าช้า แก้ไขโดยให้ตั้งจัดตารางเวลาการรันคำสั่งกลุ่มนี้ไปอยู่ในช่วงเวลาวิกฤตที่มีผู้ใช้งานน้อยที่สุดของวัน

🛠️ Weekend Sandbox Challenge: Apache Iceberg Time Travel query

โจทย์ปฏิบัติการ: จงเขียนคิวรี Spark SQL เพื่อทำระบบอ่านประวัติย้อนหลัง (Time Travel) ของตาราง Apache Iceberg ไปยังหมายเลข Snapshot หรือช่วงวันเวลาเฉพาะเพื่อเปรียบเทียบข้อมูลก่อนการแก้ไข

-- Spark SQL query to trace table snapshot history
SELECT * FROM iceberg_local.db.orders.snapshots;

-- Querying data as of a specific historical snapshot ID
SELECT * FROM iceberg_local.db.orders FOR SYSTEM_VERSION_AS_OF 420938402934023;

-- Querying data as of a specific historical timestamp
SELECT * FROM iceberg_local.db.orders FOR SYSTEM_TIME_AS_OF TIMESTAMP '2026-07-14 00:00:00';
                    

💼 Senior Technical Interview Q&A

Q1: ไฟล์รายการแสดงผลข้อมูลในแต่ละสแนปชอต (Manifest List) และไฟล์สถิติระดับไฟล์ข้อมูล (Manifest File) ใน Apache Iceberg ทำหน้าที่ช่วยคิวรี่ทำงานเร็วขึ้นอย่างไร?

A1: Manifest List เก็บบันทึกตำแหน่ง Manifest File ของสแนปชอตตัวนั้น พร้อมค่าพารามิเตอร์ขอบเขตพาร์ทิชันเพื่อตัดไฟล์ที่ไม่อยู่ในกลุ่มคิวรี่ออก ส่วน Manifest File บันทึกสถิติระดับคอลัมน์ (เช่น Min/Max) ทำให้เอนจิ้นคิวรี่ข้ามการเปิดไฟล์ Parquet ที่ไม่มีข้อมูลตามเงื่อนไข ประหยัดเวลา I/O มหาศาล

Q2: Apache Iceberg ค้ำประกันความถูกต้องตามธุรกรรม ACID โดยไม่มีการล็อกระบบข้อมูล (Concurrency Locks) ได้อย่างไร?

A2: Iceberg อาศัยระบบ Catalogs ทำการสลับตัวชี้พอยน์เตอร์ตำแหน่งอัปเดตไฟล์ Metadata ตัวล่าสุดอย่างเป็นอะตอมิก (Atomic Swap) ร่วมกับหลักการ Optimistic Concurrency Control (OCC) โดยไม่มีการบล็อกเขียนไฟล์อื่น แต่หากพบว่าเกิดประวัติอัปเดตซ้อนจังหวะ Commit ตัวรันปัจจุบันจะถูกยกเลิกแล้วรันซ่อมตัวเองใหม่

ข้อดี / จุดเด่น & ข้อเสีย / ข้อควรระวัง

ข้อดี / จุดเด่น

  • ประหยัดค่าใช้จ่ายการบันทึกข้อมูลได้ถึง 80% เมื่อเทียบกับการจัดเก็บคลังข้อมูลดั้งเดิม
  • รองรับคำสั่ง SQL มาตรฐานระดับ ACID และการย้อนกลับดูเวอร์ชันเวลา

ข้อเสีย / ข้อควรระวัง

  • ต้องเขียนโค้ดตั้งค่าและควบคุมระบบ Catalog เสริมเพิ่มความซับซ้อนในการวางระบบ
  • ไม่เหมาะกับการเขียนข้อมูลเข้าแบบเสี้ยววินาทีละมากๆ เพราะจะสร้างไฟล์ขนาดเล็กจำนวนเกินพิกัด

ปฏิบัติการจริง (Lab Practice)

Bilingual Guide

แล็บ: จัดตั้งตารางประมวลผล Iceberg บน MinIO Object Store

💡 คำแนะนำ & ทริกเด็ด

ควรรันคำสั่ง expire_snapshots สัปดาห์ละครั้ง เพื่อลบประวัติ Snapshot ย้อนหลังที่ไม่ได้ใช้งานแล้ว ป้องกันการสะสมของไฟล์ Parquet เก่าที่ทำให้เปลืองเนื้อที่จัดเก็บ