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)
บทที่ 1: รากฐานวิศวกรรมซอฟต์แวร์และระบบ (Foundations of Software & Systems)

ข้อมูลเบื้องต้นเกี่ยวกับวิศวกรรมข้อมูล (Introduction to Data Engineering)

1.1 ข้อมูลเบื้องต้นเกี่ยวกับวิศวกรรมข้อมูล (Introduction to Data Engineering)

ในยุคของการประมวลผลข้อมูลระดับหลายร้อยเทราไบต์ (Exabyte-scale) และความนิยมของระบบ AI เชิงรุก หน้าที่ของวิศวกรข้อมูลได้ปรับเปลี่ยนจากผู้ดูแลตารางฐานข้อมูลธรรมดา ไปสู่การเป็นผู้ออกแบบและควบคุมสถาปัตยกรรมระบบคอมพิวเตอร์และเครือข่ายแบบกระจายศูนย์ เนื้อหานี้เจาะลึก 5 หัวข้อหลักระดับระบบ เพื่อความเข้าใจกลไกและช่องว่างความรู้อย่างวิศวกรมืออาชีพ

Data Lifecycle Diagram

3D Isometric Modern Data Ingestion & Storage Lifecycle Diagram

1. การออกแบบสถาปัตยกรรม Data Lakehouse และกลไกของ Open Table Formats

ทฤษฎีและกลไกการทำงาน (How it works): Data Lakehouse คือสถาปัตยกรรมที่นำเอาความคุ้มค่าของการเก็บข้อมูลราคาประหยัดบน Object Storage (เช่น AWS S3, MinIO) มารวมเข้ากับความสามารถในการทำ ACID Transaction และการสร้างดัชนีของ Data Warehouse โดยมีเลเยอร์ Metadata หรือ Open Table Format (เช่น Apache Iceberg) ทำหน้าที่เป็นตัวเชื่อมโยงกลไกภายใน ภายใต้ฝากระโปรงของ Apache Iceberg จะมีโครงสร้างการจัดการ 3 ชั้นหลัก ได้แก่: 1. **Iceberg Catalog**: คอยชี้ตำแหน่งไฟล์ Metadata ล่าสุด (เช่น JDBC Catalog, Hive Metastore) 2. **Metadata Files**: บันทึกสกีมา (Schema), การแบ่งพาร์ทิชัน (Partition Specs) และรายการสแนปช็อต (Snapshots) ทั้งหมดของตาราง 3. **Manifest Lists & Manifest Files**: ทำหน้าที่จัดเก็บความสัมพันธ์ระดับกลุ่มข้อมูลเพื่อคัดกรองไฟล์ Parquet ที่ต้องอ่านโดยไม่ต้องกวาดอ่านข้อมูลทั้งหมด (Data File Pruning)

# Spark configuration template to catalog Iceberg with MinIO Object Storage
from pyspark.sql import SparkSession

spark = SparkSession.builder     .appName("LakehouseIcebergMinIO")     .config("spark.sql.extensions", "org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions")     .config("spark.sql.catalog.local", "org.apache.iceberg.spark.SparkCatalog")     .config("spark.sql.catalog.local.type", "hadoop")     .config("spark.sql.catalog.local.warehouse", "s3a://lakehouse-warehouse/")     .config("spark.hadoop.fs.s3a.endpoint", "http://minio:9000")     .config("spark.hadoop.fs.s3a.access.key", "minioadmin")     .config("spark.hadoop.fs.s3a.secret.key", "minioadmin")     .config("spark.hadoop.fs.s3a.path.style.access", "true")     .config("spark.hadoop.fs.s3a.impl", "org.apache.hadoop.fs.s3a.S3AFileSystem")     .getOrCreate()
                    

Use Case ในชีวิตจริง (Real-world Scenario): Netflix ประสบปัญหาระบบคลังข้อมูลเดิม (Hive-based Data Lake) ทำงานช้าเมื่อจัดเก็บตารางล็อกการรับชมความถี่สูง จึงสร้าง Apache Iceberg ขึ้นเพื่อทำระบบสแนปช็อต ทำให้สามารถอ่านข้อมูลแบบ Time Travel ย้อนดูประวัติสถิติย้อนหลัง และเขียนบันทึกพร้อมกันได้โดยไม่มีปัญหา Write-Conflict

ข้อควรระวังและวิธีแก้ (Pitfalls & Mitigations): ปัญหา **Small File Problem** เกิดจากงานเขียนข้อมูลขนาดเล็กบ่อย ๆ จนไฟล์ Parquet ย่อยบวมเต็มโฟลเดอร์ ส่งผลให้ I/O อ่านช้าลงอย่างมหาศาล แก้ไขโดยการตั้งระบบทำ **Auto-Compaction** ด้วยการรันสปาร์กจ๊อบเพื่อจัดกลุ่มไฟล์ใหม่: `CALL local.system.rewrite_data_files(table => 'db.events', options => map('max-file-size-bytes', '536870912'))` (ยุบรวมให้ได้ขนาด 512MB ต่อไฟล์)

2. ระบบประมวลผลธุรกรรมออนไลน์ (OLTP) และการประมวลผลเชิงวิเคราะห์ออนไลน์ (OLAP)

ทฤษฎีและกลไกการทำงาน (How it works): ฐานข้อมูล OLTP (เช่น PostgreSQL, MySQL) จัดเก็บข้อมูลแบบรายแถว (Row-oriented / Slotted Page) ออกแบบมาสำหรับการเข้าถึงและปรับปรุงข้อมูลทีละเรเบียนอย่างรวดเร็ว ทำงานร่วมกับดัชนี B-Tree และระบบบันทึก Log การเขียนก่อนลุยจริง (Write-Ahead Log - WAL) เพื่อการันตี ACID ขณะที่ฐานข้อมูล OLAP (เช่น ClickHouse, Snowflake, BigQuery) เก็บข้อมูลเชิงคอลัมน์ (Column-oriented) ข้อมูลของคอลัมน์เดียวกันจะถูกจัดกลุ่มและจัดเก็บเรียงลำดับอยู่ด้วยกันบนดิสก์ ช่วยให้คอมพิวเตอร์สแกนหาผลรวมของฟิลด์ตัวเลขล้านแถวได้โดยไม่ต้องโหลดคอลัมน์ที่ไม่เกี่ยวเข้ามาในแรม

-- [OLTP - PostgreSQL DDL]
CREATE TABLE customer_orders (
    order_id UUID PRIMARY KEY,
    customer_id UUID NOT NULL,
    order_amount DECIMAL(10,2) NOT NULL,
    created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW()
);
CREATE INDEX idx_customer ON customer_orders(customer_id);

-- [OLAP - ClickHouse MergeTree DDL]
CREATE TABLE analytics.customer_orders (
    order_id UUID,
    customer_id UUID,
    order_amount Decimal(10,2),
    created_at DateTime
) ENGINE = MergeTree()
PARTITION BY toYYYYMM(created_at)
ORDER BY (customer_id, created_at);
                    

Use Case ในชีวิตจริง (Real-world Scenario): ระบบธนาคารจัดเก็บยอดชำระเงินของลูกค้าลงในระบบฐานข้อมูล Relational OLTP เพื่อความปลอดภัยในการทำรายการเงิน จากนั้นมีบริการ CDC (Change Data Capture) ส่งข้อมูลธุรกรรมเหล่านั้นไปประกอบสถิติการทุจริตในระบบ OLAP อย่าง ClickHouse เพื่อออกรายงานแดชบอร์ดความเร็วสูง

ข้อควรระวังและวิธีแก้ (Pitfalls & Mitigations): การรันคำสั่ง `UPDATE` หรือ `DELETE` ถี่ ๆ บนระบบ OLAP จะส่งผลกระทบต่อความเร็วระบบเนื่องจากสถาปัตยกรรมจัดเก็บเชิงคอลัมน์ไม่ได้ออกแบบมารับงานนี้ แก้ไขโดยดีไซน์ตารางเป็นแบบ **Append-Only** (เน้นการใส่ข้อมูลต่อท้าย) และใช้คำสั่ง `SUM` หรือการ Join ร่วมกับวิวจดจำสถานะ (Materialized Views) ในการคำนวณหายอดปัจจุบัน

3. กลไกการนำเข้าข้อมูลแบบ Batch และ Streaming Ingestion

ทฤษฎีและกลไกการทำงาน (How it works): การดึงนำข้อมูล (Ingestion) เป็นการย้ายข้อมูลจากระบบต้นทางมาจัดเก็บในดิสก์หลัก โดยแบ่งเป็นสองประเภทตาม Latency: - **Batch Ingestion**: ทำงานเป็นรอบเวลา (เช่น รายชั่วโมง/รายวัน) คิวรีอ่านข้อมูลจากฐานต้นทางโดยการระบุช่วงเวลาพาร์ทิชัน เช่น `WHERE update_date >= CURRENT_DATE - INTERVAL '1 day'` ช่วยรักษาทรัพยากรคอมพิวเตอร์และคุ้มทุนคอส - **Streaming Ingestion**: ข้อมูลจะไหลต่อเนื่องทันทีที่เกิดเหตุการณ์ (Events) ผ่านระบบ Message Queue เช่น Apache Kafka ข้อมูลจะถูกเก็บในรูปแบบคิวบันทึกขนาดยาว (Distributed Commit Log) โดนแบ่งพาร์ทิชันและชี้ตำแหน่งอ่านด้วยออฟเซต (Offset) เพื่อความแม่นยำในการรันซ้ำ

# PySpark Structured Streaming configuration example from Kafka broker
from pyspark.sql.functions import col, from_json
from pyspark.sql.types import StructType, StringType

schema = StructType().add("order_id", StringType()).add("status", StringType())

kafka_stream = spark.readStream     .format("kafka")     .option("kafka.bootstrap.servers", "kafka-broker:9092")     .option("subscribe", "orders-topic")     .option("startingOffsets", "latest")     .load()

parsed_stream = kafka_stream     .selectExpr("CAST(value AS STRING) as json_payload")     .select(from_json(col("json_payload"), schema).alias("data"))     .select("data.*")
                    

Use Case ในชีวิตจริง (Real-world Scenario): แพลตฟอร์มเรียกรถโดยสาร (Grab/Uber) ใช้ Streaming Ingestion ดึงค่าพิกัด GPS ของผู้ขับเรียลไทม์เพื่อคำนวณราคาตามระยะทางจริง ส่วนข้อมูลสรุปบัญชีรอบวันจะใช้ระบบ Batch ในการดึงรวบรวมช่วงเที่ยงคืนเพื่อประหยัดคอมพิวต์ในการคิดเงิน

ข้อควรระวังและวิธีแก้ (Pitfalls & Mitigations): ปัญหา **Consumer Lag** ในระบบ Streaming คือการประมวลผลข้อมูลไม่ทันปริมาณการเขียนจนเกิดการสะสมค้าง แก้ไขโดยตั้งระบบตรวจวัดผ่าน Prometheus และทำการขยายพาร์ทิชันของ Kafka (Scale Out) เพิ่มจำนวนโปรเซสผู้ดึงข้อมูลตามสัดส่วนที่เหมาะสม

4. การออกแบบสัญญาข้อมูล (Data Contracts) และสกีมาอินทิเกรชัน

ทฤษฎีและกลไกการทำงาน (How it works): Data Contract คือข้อตกลงและเงื่อนไขโครงสร้างข้อมูลรวมถึงค่าจำกัด (SLA) ระหว่างผู้ผลิตต้นทาง (Software Engineer) และผู้บริโภคข้อมูลปลายทาง (Data Engineer) โดยการนำเอากรอบการตรวจวัดประเภทข้อมูล (Schema Validation) มาคุมที่เลเยอร์การส่งออก ข้อมูลที่ไม่ผ่านข้อกำหนดจะถูกปฏิเสธตั้งแต่ต้นสาย เพื่อป้องการเกิดเหตุการณ์ฐานข้อมูลล่มเนื่องจากทีมระบบเปลี่ยนสกีมาแบบกะทันหัน

# Example of Data Contract Configuration YAML Schema
dataset: user_events
version: 1.0.0
owner: auth_team
schema:
  type: object
  properties:
    event_id: { type: string, format: uuid }
    user_id: { type: integer }
    email: { type: string, format: email }
    action: { type: string, enum: [signup, login, logout] }
  required: [event_id, user_id, email, action]
  additionalProperties: false
                    

Use Case ในชีวิตจริง (Real-world Scenario): ระบบชำระเงินของสถาบันการเงินใช้ Data Contract คุมการส่ง API บัญชีของลูกค้า หากทีมพัฒนาซอฟต์แวร์เปลี่ยนชนิดข้อมูล ID จากตัวเลขเป็นตัวหนังสือ ตัว Validate จะกั้นไม่ให้ส่งข้อมูลขึ้น Pipeline และส่งสคริปต์แจ้งเตือนกลับทีมโปรแกรมเมอร์ทันที

ข้อควรระวังและวิธีแก้ (Pitfalls & Mitigations): การจำกัดที่เข้มงวดเกินไปอาจทำให้ระบบหลักหยุดทำงานเมื่อต้องการอัปเกรดระบบ แก้ไขโดยการทำระบบสกีมาแบบยืดหยุ่น (Schema Compatibility) เช่น การเพิ่มฟิลด์ทางเลือกได้ (Optional Fields) และสร้างสปาร์กบัฟเฟอร์แยกข้อมูลผิดปกติออกไปจัดใน Dead Letter Queue (DLQ) เพื่อสืบค้นภายหลังโดยไม่บล็อกท่อส่ง

5. การกำหนดและออกแบบ SLA, SLO และ SLI ในระบบงานข้อมูล

ทฤษฎีและกลไกการทำงาน (How it works): การวัดผลประสิทธิภาพท่อส่งข้อมูลต้องกำหนดเกณฑ์วัดเชิงเทคนิคอย่างเป็นระบบ: - **SLA (Service Level Agreement)**: สัญญากับฝ่ายธุรกิจ (เช่น ข้อมูลรายรับต้องอัปเดตพร้อมใช้งานภายในเวลา 08:00 น. ของวันทำการ) - **SLO (Service Level Objective)**: เป้าหมายความน่าเชื่อถือที่ทีมวิศวกรกำหนด (เช่น ความล่าช้าของข้อมูลหรือ Data Freshness ต้องไม่เกิน 15 นาที และครอบคลุมจำนวนข้อมูลสมบูรณ์ไม่น้อยกว่า 99.9%) - **SLI (Service Level Indicator)**: วิธีตรวจวัดจริง (เช่น สคริปต์คิวรีเปรียบเทียบค่าเวลาปัจจุบันลบเวลาแก้ไขล่าสุดของข้อมูลในปลายทาง)

-- Prometheus metric rule to alert on data ingestion freshness lag (in seconds)
# Alert if the maximum timestamp delay in database ingest exceeds 1 hour
ALERT DataPipelineFreshnessSpike
  IF (time() - last_successful_run_timestamp_seconds{pipeline="orders_elt"}) > 3600
  FOR 5m
  LABELS { severity = "critical" }
  ANNOTATIONS {
    summary = "Data Ingestion Pipeline Latency is too high",
    description = "The pipeline orders_elt has not updated for over 1 hour."
  }
                    

Use Case ในชีวิตจริง (Real-world Scenario): บริษัทขายของออนไลน์กำหนด SLO ว่ายอดจำหน่ายสินค้าบนหน้าแดชบอร์ดห้ามช้าเกิน 5 นาที (Freshness SLI) หากหน้าเว็บค้างมีจ๊อบค้างใน Airflow ระบบ Prometheus จะส่งสายตรวจเช็คส่งแจ้งเตือนเข้า Slack และ PagerDuty เพื่อให้ทีมวิศวกรเข้าดีบักก่อนเวลา SLA ที่สัญญากับฝ่ายบริหาร

ข้อควรระวังและวิธีแก้ (Pitfalls & Mitigations): ปัญหา **Alert Fatigue** หรือเสียงแจ้งเตือนที่ดังตลอดจนวิศวกรเริ่มละเลยและมองข้าม แก้ไขโดยการแยกความสำคัญของ SLO: ตั้งเกณฑ์เตือน Warning แบบไม่ต้องปลุกตื่นนอนหากท่อรองช้าลงเล็กน้อย และตั้ง Critical ปลุกเฉพาะเมื่อกระทบ SLA หลักเท่านั้น

🛠️ Weekend Sandbox Challenge: Raw-to-Silver Validation Blueprint

โจทย์ปฏิบัติการ: จงเขียนโครงร่างระบบการจัดการข้อมูลดิบ (Raw Data) และตรวจสอบคุณภาพของข้อมูลเพื่อย้ายเข้าสู่เลเยอร์กึ่งสำเร็จรูป (Silver Layer) โดยสร้างไฟล์ทดสอบ CSV และตรวจสอบข้อมูลประเภทอายุ (Age) ต้องอยู่ระหว่าง 1-120 ปี และยอดเงิน (Amount) ห้ามเป็นลบ

# local_pipeline_validation.py
import csv

def validate_record(row):
    try:
        age = int(row['age'])
        amount = float(row['amount'])
        if not (1 <= age <= 120):
            return False, "Age out of range"
        if amount < 0:
            return False, "Negative amount"
        return True, "Valid"
    except ValueError as e:
        return False, f"Type mismatch: {str(e)}"

# Simulating raw CSV reading
raw_data = [
    {"user_id": "1", "age": "25", "amount": "150.50"},
    {"user_id": "2", "age": "150", "amount": "20.00"}, # Invalid age
    {"user_id": "3", "age": "thirty", "amount": "99.00"} # Invalid type
]

for record in raw_data:
    is_valid, msg = validate_record(record)
    print(f"Record {record['user_id']}: {'PASSED' if is_valid else 'FAILED'} -> {msg}")
                    

💼 Senior Technical Interview Q&A

Q1: จงอธิบายความแตกต่างและการเลือกใช้ระหว่างสถาปัตยกรรมแบบ Batch Processing และ Streaming ในระบบข้อมูลระดับองค์กร?

A1: Batch Processing เหมาะกับงานคำนวณข้อมูลขนาดใหญ่ที่สะสมมาตามช่วงเวลา (เช่น การสรุปยอดขายรายวัน) ซึ่งต้องการความแม่นยำสูงและการควบคุมทรัพยากรที่มีประสิทธิภาพ ในขณะที่ Streaming ประมวลผลข้อมูลเกือบทันทีเมื่อเหตุการณ์เกิดขึ้น (เช่น ตรวจจับการทุจริตธุรกรรมการเงิน) การเลือกใช้พิจารณาจากข้อกำหนดเรื่องความล่าช้า (Latency), ปริมาณข้อมูล (Volume) และค่าใช้จ่ายของกำลังไฟประมวลผล (Compute Cost)

Q2: การย้ายข้อมูลจากระบบจัดเก็บแบบรายแถว (Row-oriented) ไปเป็นแบบรายคอลัมน์ (Columnar) ช่วยเพิ่มความคุ้มค่าของการประมวลผล (Compute Cost-Efficiency) อย่างไร?

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

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

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

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

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

  • ใช้เวลาในการจัดเตรียมระบบโครงสร้างพื้นฐานยาวนานกว่างานทำโมเดลขนาดเล็ก
  • ต้องการความรู้ที่กว้างขวางในเทคโนโลยี Cloud, Networking, และ Distributed Systems

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

Bilingual Guide

แล็บ: ประกอบชิ้นส่วนของวงจรชีวิตข้อมูล

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

ก่อนตัดสินใจใช้ระบบประมวลผลข้อมูลราคาแพง ให้ถามธุรกิจว่าต้องการข้อมูลสดใหม่แค่ไหน หากต้องการแค่สัปดาห์ละครั้ง การประมวลผลแบบ Batch ด้วยสคริปต์ Python ในเซิร์ฟเวอร์เดียวจะช่วยเซฟงบไปได้มากกว่าเปิดคลัสเตอร์ Kafka/Spark ตลอดเวลา