PipeCraft

Roadmap Progress

ข้อมูลเบื้องต้นเกี่ยวกับวิศวกรรมข้อมูล (Introduction to Data Engineering) Python สำหรับ Data Engineering พื้นฐาน Linux & CLI สำหรับวิศวกรข้อมูล Git สำหรับระบบงานข้อมูลและทีมพัฒนา พื้นฐานเครือข่าย เว็บเทคโนโลยี และระบบกระจายศูนย์ (Web Fundamentals, 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) รากฐานของ AWS VPC การจัดการเส้นทางขั้นสูงและ NAT Gateway การเชื่อมต่อหลาย VPC และ Hybrid Cloud VPC Endpoints (PrivateLink) ระบบรักษาความปลอดภัยขอบเขตเครือข่าย สถาปัตยกรรมเครือข่ายสำหรับ EKS IPv6 และการจัดการ IP Address สถาปัตยกรรมเครือข่ายระดับโลก แก่นแท้ของสถาปัตยกรรม Kubernetes สถาปัตยกรรมเครื่องและคอมโพเนนต์ เลเยอร์ที่เชื่อมต่อได้ (Pluggable Layers) การรันบนโปรดักชันระดับองค์กร Apache Flink Stateful Stream Processing Real-Time CDC & Event Sourcing at Scale Low-Latency Stream-Table Joins & Windowing Data Lineage & Metadata Graph Engineering Statistical Anomaly Detection & Data Drift Data Incident Management & Automated DLQ Remediation Cloud Data FinOps & Cost Optimization Mechanics Data Mesh & Multi-Tenant Platform Architecture Vector Databases & AI-Ready Data Infrastructure API Fundamentals, Architectures & Core Components API Versioning, Docs, Real-Time & Microservices Production Reliability, Security & Resiliency World-Class Master Architecture & Traffic Management
บทที่ 3: คลังข้อมูลคลาวด์และสถาปัตยกรรมยุคใหม่ (Modern Warehouse & Lakehouse)

Local-first Data Engineering ด้วย DuckDB

3.2 Local-first Data Engineering ด้วย DuckDB

แนวคิด Local-first Data Engineering คือการลดต้นทุนระบบคลาวด์โดยประมวลผลและทดสอบโค้ดบนเครื่องส่วนตัว ซึ่งในปัจจุบันประสิทธิภาพ CPU และหน่วยความจำมีขีดจำกัดความเร็วสูงมากพอในการประมวลผลข้อมูลขนาดกิกะไบต์

Technical Architecture Diagram

Architecture Diagram
Click to zoom
Step-by-Step Breakdown: (1) Data Source Ingestion -> (2) Processing Engine -> (3) Storage Layer -> (4) Serving API

1. โครงสร้างข้อมูลรายคอลัมน์ vs. ระบบจัดเก็บแบบรายแถว

Learning Progression

  • [BASIC] ปูพื้นฐานภาษาเข้าใจง่าย - เข้าใจคอนเซปต์ภาพรวมและการแก้ปัญหาเบื้องต้น
  • [INTERMEDIATE] โค้ด/คอนฟิกไวยากรณ์จริง - การเขียนโค้ดเพื่อใช้งานจริงในระบบ
  • [PROFESSIONAL] Under-the-hood & Performance/FinOps - กลไกเบื้องลึกและการรีดประสิทธิภาพ

Real-World Enterprise Scenario

เคสระบบการเงิน/Big Tech: การรองรับ Transaction จำนวนมหาศาลต่อวินาทีพร้อมประกัน Data Integrity สูงสุด โดยใช้สถาปัตยกรรมที่ยืดหยุ่นและการมอนิเตอร์ระดับสูง

ทฤษฎีและกลไกการทำงาน (How it works): ฐานข้อมูลแบบรายแถว (Row-oriented - RDBMS) บันทึกค่าทุกคอลัมน์ในแถวติดกันบนแผ่นดิสก์ เหมาะกับงานเขียนเป็นธุรกรรมรายตัว (OLTP) ในขณะที่ฐานข้อมูลรายคอลัมน์ (Columnar database เช่น DuckDB, ClickHouse) จัดเก็บค่าข้อมูลในแต่ละคอลัมน์แยกกันออกจากกันอย่างสิ้นเชิง เหมาะอย่างยิ่งสำหรับงานวิเคราะห์สรุปผล (OLAP) เนื่องจากคิวรีจะอ่านเฉพาะข้อมูลคอลัมน์ที่ระบุในเงื่อนไขการคำนวณเท่านั้น และข้ามคอลัมน์อื่นได้โดยตรง ส่งผลให้ประหยัดพื้นที่ดิสก์ I/O มหาศาล

# Concept of row storage vs column storage
Row-oriented (PostgreSQL):
[Row 1: ID, Name, Age] -> [Row 2: ID, Name, Age]

Column-oriented (DuckDB):
[Column ID: 1, 2] -> [Column Name: 'A', 'B'] -> [Column Age: 25, 30]
                    

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

ข้อควรระวังและวิธีแก้ (Pitfalls & Mitigations): ฐานข้อมูลแบบคอลัมน์ไม่เหมาะกับงานเขียนแทรกทีละแถวถี่ๆ (High-frequency single-row insert) เพราะมีภาระในการจัดเก็บข้อมูลแยกคอลัมน์สูงมาก แก้ไขโดยสะสมข้อมูลเป็นชุด (Micro-batch) แล้วเขียนลงดิสก์พร้อมกันทีเดียว

2. Vectorized Execution Engine และระบบคำนวณความเร็วสูง SIMD

ทฤษฎีและกลไกการทำงาน (How it works): ระบบประมวลผลทั่วไปจะทำการวนลูปประมวลผลทีละแถวข้อมูลซึ่งเกิดความล่าช้าจาก Instruction loop overhead แต่ DuckDB ใช้เครื่องยนต์รันมติแบบ **Vectorized Execution** โดยจัดเก็บข้อมูลแยกเป็นกลุ่มอาร์เรย์ (Vectors ขนาด 1024 ค่า) และประมวลผลพร้อมกันในหน่วยความจำ L1/L2 Cache ของ CPU ซึ่งประสานร่วมกับคำสั่งระดับฮาร์ดแวร์ **SIMD (Single Instruction Multiple Data)** ทำให้ CPU คำนวณขนานกันในระดับสัญญาณนาฬิกา

# Python demonstration comparing loop vs vectorized calculations
import numpy as np

# Simulate 1 million order amounts
amounts = np.random.rand(1000000)

# 1. Slow loop computation
# total = [x * 1.07 for x in amounts]

# 2. Fast Vectorized SIMD computation (DuckDB model)
total_vectorized = amounts * 1.07
                    

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

ข้อควรระวังและวิธีแก้ (Pitfalls & Mitigations): ประสิทธิภาพ Vectorized จะลดลงหากประมวลผลคอลัมน์ประเภท String ขนาดใหญ่ที่มีสถิติผันแปรสูง แก้ไขโดยแปลงประเภทข้อมูลให้เป็นหมวดหมู่ (Enum/Dictionary encoding) เพื่อลดขนาดบิตของอาร์เรย์ในแรม

3. DuckDB HTTPFS Extension และกลไกคิวรีไฟล์ Parquet จาก AWS S3

ทฤษฎีและกลไกการทำงาน (How it works): ส่วนต่อขยาย **httpfs** ของ DuckDB ช่วยให้ผู้พัฒนาสามารถรันคำสั่งคิวรีข้อมูล SQL บนไฟล์ Parquet/CSV ที่เก็บอยู่บนคลาวด์ภายนอกได้โดยตรง ผ่านโปรโตคอล HTTP range requests กลไกนี้จะส่งคำขออ่านพิกัดเฉพาะฟิลด์ Metadata ของไฟล์ Parquet เพื่อหาพิกัดระดับบิตของคอลัมน์ที่ต้องการ จากนั้นยิงขอดึงเฉพาะส่วนต่างข้อมูลระดับบล็อกบิตมาประมวลผลข้ามท่อเครือข่าย โดยข้ามการดาวน์โหลดไฟล์ขนาดใหญ่ทั้งไฟล์มาเก็บไว้ในเครื่อง

-- DuckDB setup script to query remote S3 parquet files directly
INSTALL httpfs;
LOAD httpfs;

-- Configure cloud storage access parameters
SET s3_region = 'ap-southeast-1';
SET s3_access_key_id = 'AWS_ACCESS_KEY';
SET s3_secret_access_key = 'AWS_SECRET_KEY';

-- Query only 2 target columns from a 5GB remote parquet file
SELECT customer_id, SUM(purchase_amount)
FROM read_parquet('s3://my-prod-datalake/gold/sales/**/*.parquet')
GROUP BY customer_id;
                    

Use Case ในชีวิตจริง (Real-world Scenario): วิศวกรข้อมูลวิเคราะห์แนวโน้มยอดขายสินค้าผ่านไฟล์บันทึกประวัติขนาด 50GB บน Amazon S3 โดยรันสคริปต์ DuckDB คอนฟิก HTTPFS บนแล็ปท็อป ดึงข้อมูลผ่านเน็ตเวิร์กเพียง 2MB ประมวลผลและตอบคำถามคิวรีได้ทันควันในเวลาครึ่งวินาที

ข้อควรระวังและวิธีแก้ (Pitfalls & Mitigations): การรันคิวรีลักษณะนี้บ่อยครั้งอาจมีค่าใช้จ่ายแฝงจากการส่งคำขอเรียกเก็บไฟล์จาก Object Store (API Get/List request charges) แก้ไขโดยดาวน์โหลดส่วนต่างที่กรองพาร์ทิชันแล้วลงฮาร์ดดิสก์เพื่อรันวิเคราะห์แบบ Offline

4. การผสานระบบส่งข้อมูลแบบ Zero-Copy ร่วมกับ Pandas, Polars และ PyArrow

ทฤษฎีและกลไกการทำงาน (How it works): อุปสรรคหนึ่งของการแลกเปลี่ยนข้อมูลระหว่างฐานข้อมูลและหน่วยความจำของโค้ดโปรแกรมคือขั้นตอนการแปลงรูปแบบตัวแปล (Serialization/Deserialization) DuckDB แก้ปัญหานี้โดยสนับสนุนการทำงานบนมาตรฐานความปลอดภัยหน่วยความจำ **Apache Arrow** ทำให้เราสามารถแลกเปลี่ยนตารางข้อมูลขนาดใหญ่ระหว่าง DuckDB, Pandas, Polars และ PyArrow ใน RAM วงเดียวกันได้ทันทีแบบ **Zero-Copy** โดยไม่มีการก๊อปปี้บล็อกข้อมูลหรือเขียนทับในหน่วยความจำ

# Zero-Copy integration demo using DuckDB and Polars
import polars as pl
import duckdb

# 1. Create Polars DataFrame in RAM
df_polars = pl.DataFrame({"id": [1, 2, 3], "score": [95.5, 88.0, 92.4]})

# 2. DuckDB queries Polars DataFrame directly in memory without copy
result_df = duckdb.query("SELECT id, score * 1.1 AS new_score FROM df_polars").pl()
print(result_df)
                    

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

ข้อควรระวังและวิธีแก้ (Pitfalls & Mitigations): ความเข้ากันไม่ได้ของบางสเปคฟิลด์วันเวลา (Timestamp timezone offset) ระหว่าง Pandas และ DuckDB อาจทำให้ประเภทข้อมูลเคลื่อนแก้ไขดักจับยาก แก้ไขโดยการระบุการจัดรูปแบบประเภทให้อยู่ในมาตรฐานกลาง UTC (Timezone-naive) ก่อนประมวลผล

5. สถาปัตยกรรมแบบฝังตัว (Embedded DB) และกลไก Out-of-Core Processing

ทฤษฎีและกลไกการทำงาน (How it works): DuckDB เป็นฐานข้อมูลแบบ **Embedded** ซึ่งรันอยู่ในโปรเซสหน่วยความจำเดียวกันกับแอปพลิเคชันหลัก เมื่อต้องประมวลผลข้อมูลที่มีขนาดใหญ่กว่า RAM (Larger-than-RAM) DuckDB จะใช้กลไก **Out-of-Core Processing** โดยโหลดข้อมูลเข้ามาทำใน RAM ทีละบล็อก (Chunking) และหาก RAM ใกล้เต็ม จะทำการพักข้อมูล (Spill) ลงบนฮาร์ดดิสก์อัตโนมัติ ทำให้รันคำนวณผ่านไฟล์ใหญ่ได้โดยไม่เกิด Out-of-Memory (OOM)

-- Configuration variables to cap resource footprint and enable out-of-core spilling
SET memory_limit = '4GB';   -- Cap RAM usage to 4 gigabytes
SET threads = 4;            -- Limit execution threads to 4 cores
SET temp_directory = '/tmp/duckdb_temp/'; -- Spill excess data to specific disk directory
                    

Use Case ในชีวิตจริง (Real-world Scenario): การรันสคริปต์ทำ JOIN ตารางข้อมูลขนาด 50GB บนแล็ปท็อปที่มีแรมเพียง 16GB กลไก Out-of-Core จะสลับการอ่านเขียนลงดิสก์ NVMe ทำให้งานคำนวณเสร็จสิ้นได้โดยเครื่องไม่แฮงก์

ข้อควรระวังและวิธีแก้ (Pitfalls & Mitigations): การพักข้อมูล (Spill to disk) บนฮาร์ดดิสก์แบบเก่า (HDD) จะทำให้ระบบทำงานช้าลงมหาศาล แก้ไขโดยรันงานบนดิสก์ SSD/NVMe และขยาย memory_limit ให้มากที่สุดเท่าที่จะเป็นไปได้

6. Polars Lazy Frame, Streaming Engine และกลไกบีบอัด Parquet

ทฤษฎีและกลไกการทำงาน (How it works): เครื่องมือทางเลือกอย่าง Polars ใช้ Lazy Frame เพื่อจัดเตรียม Query Plan ก่อนเริ่มประมวลผลจริง ทำให้สามารถตัดคอลัมน์และกรองแถวล่วงหน้า (Filter Pushdown) อีกทั้งยังมี Streaming Engine ที่ประมวลผลเป็นชุดๆ (Batches) ช่วยให้จัดการไฟล์ใหญ่กว่า RAM ได้ เมื่อทำงานร่วมกับ Parquet ที่มีการบีบอัดแบบ Dictionary Encoding (แทนที่คำซ้ำด้วยตัวเลข) และ ZSTD ยิ่งทำให้การอ่านข้อมูลมหาศาลผ่าน I/O ทำได้รวดเร็วระดับวินาที

# Python Polars: Lazy execution with streaming and Parquet ZSTD
import polars as pl

# scan_parquet creates a LazyFrame (no data loaded yet)
q = (
    pl.scan_parquet("large_sales_data.parquet")
    .filter(pl.col("region") == "APAC")
    .group_by("product_category")
    .agg(pl.col("revenue").sum())
)

# collect(streaming=True) executes processing in chunks to save RAM
result = q.collect(streaming=True)
                    

Use Case ในชีวิตจริง (Real-world Scenario): วิศวกรข้อมูลต้องการหาผลรวมรายได้จากไฟล์ Parquet ขนาด 100GB ระบบ Polars ร่วมกับ Parquet จะอ่านเฉพาะ Dictionary Block ของคอลัมน์ที่ใช้งาน และสตรีมมิ่งคำนวณโดยใช้แรมเพียง 2GB

ข้อควรระวังและวิธีแก้ (Pitfalls & Mitigations): คำสั่งบางประเภทใน Polars (เช่น การเรียงลำดับ หรือ Group By ที่คีย์เยอะมากๆ) อาจยังไม่รองรับ Streaming 100% และทำให้เกิด Out-of-Memory ได้ แก้ไขโดยพิจารณาทำพาร์ทิชันไฟล์ล่วงหน้า หรือใช้ DuckDB ช่วยประมวลผลแทนในท่าที่ซับซ้อน

Weekend Sandbox Challenge: Remote query with DuckDB

โจทย์ปฏิบัติการ: จงเขียนสคริปต์ DuckDB เพื่อวิเคราะห์และดึงสรุปไฟล์ข้อมูลดิบ Parquet ขนาดใหญ่จากเซิร์ฟเวอร์จำลองระยะไกล (HTTP) โดยกรองฟิลด์วิเคราะห์เฉพาะเจาะจง

# local_duckdb_analysis.py
import duckdb

# Connect to in-memory DuckDB database
con = duckdb.connect(database=':memory:')

# Query directly from a remote public parquet file
con.execute("INSTALL httpfs; LOAD httpfs;")
query = """
    SELECT 
        passenger_count, 
        AVG(trip_distance) AS avg_distance
    FROM read_parquet('https://d37ci6vzurychx.cloudfront.net/trip-data/yellow_tripdata_2023-01.parquet')
    GROUP BY passenger_count
    LIMIT 5;
"""
print(con.execute(query).fetchall())
                    

Senior Technical Interview Q&A

Q1: เหตุใดโครงสร้างข้อมูลเชิงคอลัมน์ (Columnar Layout) จึงได้รับความเร็วสูงกว่าระบบเชิงแถวในการวิเคราะห์ข้อมูล (OLAP)?

A1: เนื่องจากงานวิเคราะห์ OLAP สนใจเฉพาะตัวเลขผลรวมของคอลัมน์เฉพาะเจาะจง (เช่น หายอดรวมรายได้) การเก็บแบบคอลัมน์ช่วยให้เอนจิ้นคิวรี่กวาดสายตาเฉพาะฟิลด์คอลัมน์นั้นบนแผ่นดิสก์ได้ทันที โดยไม่ต้องโหลดรายละเอียดคอลัมน์ที่ไม่จำเป็นอื่นขึ้นมาเป็นภาระในแรม

Q2: การประมวลผลแบบ Vectorized ใน DuckDB ช่วยให้ระบบคำนวณเอาชนะ PostgreSQL สำหรับงาน OLAP ได้อย่างไร?

A2: PostgreSQL ใช้วิธีวนลูปประมวลผลข้อมูลทีละแถว (Volcano model) ซึ่งเกิด Instruction Loop Overhead สูงมาก ในขณะที่ DuckDB จัดกลุ่มเก็บข้อมูลแยกเป็นอาร์เรย์ (Vectors ขนาด 1024 ค่า) และส่งไปคำนวณใน L1/L2 Cache ของ CPU พร้อมกันร่วมกับคำสั่งระดับฮาร์ดแวร์ SIMD ทำให้ประมวลผลเร็วกว่าหลายเท่าตัว

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

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

  • คิวรีประมวลผลไฟล์ Parquet ในเครื่องได้เร็วอย่างมหาศาลโดยไม่มีค่าใช้จ่าย
  • ไม่ต้องตั้งค่าเซิร์ฟเวอร์หรือโครงสร้างพื้นฐานใดๆ เพียงแค่รันภายในตัวสคริปต์

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

  • ความสามารถในการจัดเก็บและแรมจำกัดอยู่ที่ทรัพยากรของเครื่องเครื่องเดียว
  • ไม่รองรับการทำงานคิวรีพร้อมกันปริมาณมากๆ จากผู้ใช้งานที่แตกต่างกันได้ดี

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

Bilingual Guide

แล็บ: ติดตั้งและเขียนคำสั่ง SQL วิเคราะห์ไฟล์ระยะไกลผ่าน DuckDB

วิธีรันแล็บปฏิบัติการบนเครื่องจริง (Local Terminal Execution Blueprint)

เนื่องจากปฏิบัติการ Data Engineering / DevOps ระดับสูงต้องรันบน Environment จริง ขั้นตอนด้านล่างนี้คือคำสั่งสำหรับนำไปรันบน Terminal / Docker ในเครื่องของคุณ:

1. สร้างโฟลเดอร์ปฏิบัติการและเตรียมไฟล์ Environment
mkdir -p pipecraft-lab && cd pipecraft-lab
python3 -m venv venv && source venv/bin/activate
pip install --upgrade pip pandas polars pytest requests
2. จำลองการสร้างและรันระบบประมวลผล (Run Pipeline Command)
# Run execution pipeline test
python3 -c "
import polars as pl
print(' [PipeCraft Lab] Running local pipeline engine...')
df = pl.DataFrame({'id': [1, 2, 3], 'status': ['SUCCESS', 'SUCCESS', 'AUDITED']})
print(df)
" 
3. ตรวจสอบการผ่านเกณฑ์และการทำงาน (Data Quality Assertions)
# Verify clean execution exit status
echo " PipeCraft Local Lab Execution Completed Successfully!"

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

คุณสามารถแปลงไฟล์ CSV ขนาดใหญ่เป็น Parquet ได้ในพริบตาผ่าน DuckDB ด้วยคำสั่ง: COPY (SELECT * FROM 'data.csv') TO 'data.parquet' (FORMAT 'parquet')