Database Internals

โครงสร้างพื้นฐานภายใน database ที่ต้องเข้าใจก่อนคุยเรื่อง index/query optimization — ทุกอย่างเริ่มจาก Pages และ IO

Page (หน่วยพื้นฐานของ I/O)

กฎเหล็ก: Database ไม่อ่านทีละ row — มันอ่านทีละ page (block ขนาดคงที่)

DatabasePage Size
PostgreSQL8 KB
MySQL (InnoDB)16 KB
MongoDB (WiredTiger)32 KB
SQL Server8 KB
Oracle8 KB
  • 1 page เก็บได้หลาย rows (depends on row size)
  • ขอ row 1 row → DB อ่าน 1 page ทั้ง page (row อื่นในเดียวกันได้ “ฟรี”)
  • ตาราง row เล็ก = density ดี = อ่าน page น้อย = เร็วกว่า

IO (Input/Output Operation)

IO = สกุลเงินของ database — ทุกอย่างที่เราทำคือพยายามลด IO

  • 1 IO = อ่าน 1 page (หรือหลาย pages)
  • IO ไม่สามารถอ่าน “1 row” แยก — อ่าน “1 page” เสมอ
  • บาง IO ไป OS cache (เร็ว ~microsec) บางอันลง disk จริง (ช้ากว่า 100-1000x)
StorageRandom Read Latency
HDD~10 ms
SSD~0.1 ms
RAM~0.0001 ms

Row ID (Tuple ID)

DatabaseRow ID
MySQL (InnoDB)Primary key = row id (clustered index)
PostgreSQLctid (tuple id) แยกจาก PK

ผลกระทบ:

  • Postgres: UPDATE row → สร้าง tuple ใหม่ → ctid เปลี่ยน → secondary index ทุกอัน update (write amplification)
  • MySQL: UPDATE non-PK column → secondary index ไม่ต้องเปลี่ยน

Heap (ที่อยู่จริงของข้อมูล)

Heap = data structure ที่เก็บ table ทั้งหมด page ต่อ page เรียงกัน

  • ข้อมูลจริง ๆ (ทุก column) อยู่ใน heap
  • Traverse heap แพง — ต้องอ่านหลาย pages
  • จึงต้องมี index มาบอกว่า page ไหนต้องดึง

Index (ทางลัดไปยัง heap)

Index = data structure แยกจาก heap ที่มี pointers ชี้กลับไป heap

  • เก็บ subset ของ data (เฉพาะ indexed columns + pointer)
  • index เองก็เก็บเป็น pages → ต้องใช้ IO เพื่ออ่าน
  • ยิ่ง index เล็ก → ยิ่ง fit ใน memory → search เร็ว
  • Data structure ที่นิยมที่สุด: B+Tree

Flow การ query แบบใช้ index:

  1. IO ที่ 1: อ่าน index page → หา key → ได้ pointer
  2. IO ที่ 2: อ่าน heap page ตาม pointer → ได้ row จริง

ถ้าไม่มี index → full table scan = อ่านทุก page ของ heap

index ไม่ใช่ “ของฟรี” — ทุก INSERT/UPDATE/DELETE ต้อง update index ด้วย → write ช้าลง

Buffer Pool / Shared Buffers

[Application] <-> [DB Process] <-> [Buffer Pool (RAM)] <-> [Disk]
  • DB เก็บ pages ที่ใช้บ่อยใน RAM (“buffer pool”)
  • READ → check buffer pool ก่อน → cache miss → อ่าน disk → load เข้า buffer pool
  • WRITE → modify ใน buffer pool → mark dirty → flush ลง disk แบบ async (with WAL)
DatabaseParameterค่าแนะนำ
PostgreSQLshared_buffers25% ของ RAM
MySQL InnoDBinnodb_buffer_pool_size50-70% ของ RAM

Pitfall: ตั้ง buffer pool ใหญ่เกิน → swap → ช้ายิ่งกว่า disk จริง

Key Points

  • Database อ่านทีละ page ไม่ใช่ row — Postgres 8KB, MySQL 16KB, MongoDB 32KB
  • IO = สกุลเงินของ DB — ทุกการ optimize = ลด IO
  • Heap เก็บข้อมูลจริง, Index เป็นทางลัดชี้ไป heap
  • MySQL ใช้ PK เป็น row id (clustered), Postgres ใช้ ctid แยก
  • Buffer Pool = cache pages ใน RAM — ตั้งค่าให้เหมาะ ไม่ใหญ่เกิน
  • Database Indexing — B+Tree, EXPLAIN, scan types
  • PostgreSQL — heap-organized, 8KB pages, MVCC
  • MySQL — clustered index, 16KB pages
  • WAL — durability ของ dirty pages ใน buffer pool
  • Database Engines — engine ต่าง ๆ จัดการ internals ต่างกัน