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)

Local-first Data Engineering ด้วย DuckDB

3.2 Local-first Data Engineering ด้วย DuckDB

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

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

ทฤษฎีและกลไกการทำงาน (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) และการปรับแต่งขอบเขตทรัพยากรแรม

ทฤษฎีและกลไกการทำงาน (How it works): DuckDB เป็นฐานข้อมูลแบบ **Embedded** ซึ่งรันอยู่ในโปรเซสหน่วยความจำเดียวกันกับแอปพลิเคชันหลัก (เสมือนเป็นส่วนเสริมของ Python) ไม่มีเซิร์ฟเวอร์แยกต่างหาก เพื่อความเสถียรและหลีกเลี่ยงข้อจำกัดการแย่งแรมจนระบบปฏิบัติการสั่งปิดกระบวนการทำงาน เราต้องทำการตั้งค่าขีดจำกัดหน่วยความจำแรมสูงสุด (Memory Limit) และระบุจำนวนแกนประมวลผล CPU ที่อนุญาตให้ใช้สอยล่วงหน้า

-- Configuration variables to cap resource footprint in DuckDB
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): การรันสคริปต์ ETL ทำงานวิเคราะห์เบื้องหลังบนคอนเทนเนอร์ขนาดเล็ก (เช่น บน AWS ECS Fargate ที่มีแรมเพียง 8GB) การจำกัดขอบเขตช่วยให้ DuckDB ไม่ใช้งานเกินขนาดที่จองไว้ ทำให้โปรเซสอื่นรันงานต่อได้อย่างเสถียร

ข้อควรระวังและวิธีแก้ (Pitfalls & Mitigations): ปัญหาระบบหลุดล่มจากข้อผิดพลาดหน่วยความจำล้น (Out of Memory) เมื่อคิวรี่ข้อมูลขนาดใหญ่เกินแรมที่กำหนด แก้ไขโดยกำหนดพื้นที่เก็บข้อมูลสำรองนอกแรมชั่วคราวผ่านการตั้งค่า `temp_directory` ซึ่งจะสั่งบันทึกส่วนต่างลงดิสก์อัตโนมัติเมื่อค่าคำนวณเกินขีดจำกัด

🛠️ 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

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

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