บทที่ 8 · Part 2 — Building Blocks

Datastores

3 แกนที่อธิบายทุก datastore, ทั้ง 9 ตระกูล และของที่เป็นกระแสปี 2026 (ClickHouse, DuckDB, Iceberg, TigerBeetle) พร้อมราคาที่ต้องจ่าย

คำถาม "ควรใช้ฐานข้อมูลอะไร" ตอบไม่ได้ถ้ายังไม่มี access pattern table บทนี้ไม่ตอบคำถามนั้น — บทนี้บอกว่า แต่ละตระกูลคืออะไร เก่งอะไร เก่งเพราะกลไกอะไร และล้มแบบไหน เพื่อให้ตอนคุณกลับไปดู pattern table ในบทที่ 14 คุณเลือกได้เอง

และเพราะปี 2026 มีของใหม่ที่ทีมคุณจะโดนถามในห้องประชุมแน่ๆ ("ทำไมเราไม่ใช้ ClickHouse", "TigerBeetle มันดีจริงไหม", "Iceberg คือ database หรือเปล่า") บทนี้จึงมีหัวข้อที่ไล่ของที่กำลังเป็นกระแสทีละตัว พร้อมกลไกที่ทำให้มันเก่งและราคาที่ต้องจ่าย ไม่ใช่แค่ชื่อกับคำโฆษณา

จบบทนี้คุณจะ

  • อธิบายทุก datastore ด้วย 3 แกน เดียวกันได้ แทนที่จะท่องชื่อเป็นรายตัว
  • แยกจุดแข็ง/จุดอ่อนของ datastore ทั้ง 9 ตระกูลได้
  • รู้ว่า datastore ที่เป็นกระแสในปี 2026 แต่ละตัวเก่งเพราะ กลไกอะไร และแลกอะไรไป
  • รู้ว่า search index และ read model ไม่ใช่ source of truth เด็ดขาด
  • จัดการ object storage ให้ไม่กลายเป็นข่าวข้อมูลรั่ว
  • มี rubric ประเมิน datastore ใหม่ที่ใช้ได้กับตัวที่ยังไม่เกิดในปีหน้าด้วย

3 แกนที่อธิบายทุก datastore

ก่อนจะจำชื่อ ให้จำ 3 แกนนี้ก่อน — เกือบทุกคุณสมบัติของ datastore เป็นผลพวงจาก 3 ข้อนี้ และถ้าคุณอ่าน datastore ใหม่ที่ยังไม่เคยเห็นด้วย 3 แกนนี้ คุณจะเดาจุดแข็งกับจุดพังของมันได้เองก่อนอ่าน benchmark

แกนตัวเลือกสิ่งที่ตามมาโดยอัตโนมัติ
1. Physical layout — เก็บข้อมูลเรียงยังไงบนดิสก์Row-oriented: ค่าทุกคอลัมน์ของแถวเดียวกันอยู่ติดกันอ่านแถวเดียวครบทุกคอลัมน์ = 1 I/O · แต่ scan คอลัมน์เดียวของ 1 พันล้านแถวต้องอ่านคอลัมน์ที่ไม่ได้ใช้ไปด้วยทั้งหมด
Column-oriented: ค่าคอลัมน์เดียวกันของหลายแถวอยู่ติดกันคอลัมน์เดียวกันมีชนิดเดียวกัน → compression ratio สูงกว่ามาก และ CPU ทำงานแบบ vectorized ได้ · แต่การประกอบแถวเดียวกลับคืนต้องอ่านทุกคอลัมน์แยกกัน
2. Write path — เขียนแล้วเกิดอะไรUpdate-in-place (B-tree)อ่านเร็วและคาดเดาได้, รองรับ range scan ตาม index ตรงๆ · write amplification จากการแก้ page และต้องจัดการ page split
Append + background merge (LSM-tree)write throughput สูงมากเพราะเขียนแบบ sequential · แต่ read ต้องดูหลาย level, มี compaction ที่กิน I/O และ tombstone ที่ค้างได้
Immutable file + manifest (lakehouse)ไฟล์ไม่เคยถูกแก้ การเปลี่ยนแปลงคือ snapshot ใหม่ → time travel ได้ฟรี · แต่ต้องมี compaction job และเจอ small-file problem
3. Storage coupling — compute กับ storage ผูกกันแค่ไหนLocal disk (shared-nothing)latency ต่ำสุดและคาดเดาได้ · แต่ scale = ย้ายข้อมูล และ replication ข้าม AZ คือค่า network จริง
Shared disk / shared storageเพิ่ม read replica ได้เร็วเพราะไม่ต้อง copy ข้อมูล · แต่ storage layer กลายเป็นคอขวดและ single point ที่ต้องดูแล
Object-storage-native ("diskless")durability ยกให้ object storage, scale-to-zero ได้, ไม่มีค่า inter-AZ replication · แต่ latency พื้นคือ latency ของ object storage ต้องพึ่ง cache ทุกชั้น

รายละเอียดของ B-tree กับ LSM-tree อยู่ในบทที่ 14 — บทนี้จะใช้คำพวกนี้ต่อโดยไม่อธิบายซ้ำ

ทำไมแกนที่ 1 ทำให้ columnar เร็วแบบก้าวกระโดด

ไม่ใช่เพราะ "อ่านน้อยคอลัมน์" อย่างเดียว แต่เพราะค่าที่อยู่ติดกันเป็นชนิดเดียวกัน จึงบีบอัดด้วย codec เฉพาะทางได้ (delta encoding สำหรับ timestamp ที่เรียงกัน, dictionary encoding สำหรับ enum, run-length สำหรับค่าที่ซ้ำ) ข้อมูลที่เล็กลง 10 เท่าคือ I/O ที่น้อยลง 10 เท่า ก่อนที่ CPU จะเริ่มทำงานด้วยซ้ำ แล้ว CPU ยังทำงานทีละ batch บน array ที่ชนิดเดียวกันได้ (vectorized) แทนที่จะ dispatch ทีละแถว

ตระกูลของ datastore

ตระกูลเก่งอะไรไม่เก่งอะไรหยิบมาใช้เมื่อ
Relational — Postgres, MySQLACID transaction, join, ad-hoc query, เครื่องมือครบ, constraint ที่ปกป้องข้อมูลจาก bug ของคุณเองเพดานการ scale ฝั่ง write (single writer), sharding ต้องทำมือ, การแก้ schema บนตารางใหญ่ต้องระวังdefault โดยเฉพาะอะไรที่เกี่ยวกับเงิน, สิทธิ์, หรือ invariant ข้ามหลายแถว
Document — MongoDB, DynamoDBรูปร่างยืดหยุ่น, scale ตามแนวนอน, เข้าถึงด้วย key ได้เร็ว, latency คาดเดาได้transaction ข้าม document จำกัด; query ที่ไม่อยู่ในการออกแบบ key จะช้าหรือทำไม่ได้; schema drift ไปอยู่ในโค้ดคุณการเข้าถึงถูกครอบด้วย key ตัวเดียว, รูปร่างต่างกันต่อ record, และต้อง scale เกิน 1 node
Wide-column — Cassandra, ScyllaDB, HBasewrite throughput มหาศาล, scale เป็นเส้นตรง, write ได้หลาย region, เหมาะกับ time-seriesต้องรู้ query ล่วงหน้า; ไม่มี join; tombstone และ compaction เป็นทักษะ operational จริงจังข้อมูล append-mostly ปริมาณสูงมากที่มี partition ชัด: message, event, metric, feed
Key-value / cache — Redis, Valkey, Memcachedlatency ต่ำกว่า 1 ms, มีโครงสร้างรวย (sorted set, stream, HyperLogLog)ผูกกับ memory, single-thread ต่อ shard, durability เป็น config ที่คุณต้องคิดเองcache, counter, leaderboard, rate limiting, state ชั่วคราว, queue ขนาดเล็ก
Search — Elasticsearch, OpenSearch, Quickwitfull-text relevance, faceting, aggregation บนข้อความจำนวนมากnear-real-time ไม่ใช่ real-time; ไม่ใช่ source of truth; การดูแล cluster เป็นวิชาเฉพาะค้นหาข้อความอิสระ และ dashboard วิเคราะห์ — populate จาก source of truth เสมอ
Analytical / columnar — ClickHouse, BigQuery, Snowflake, DuckDBscan 1 พันล้านแถว, aggregate ราคาถูก, compress ดีไม่เหมาะกับ point lookup แถวเดียว; update/delete เป็นปฏิบัติการราคาแพงไม่ใช่เรื่องปกติ; การโหลดข้อมูลมี latencyรายงาน, BI, reconciliation, fraud analytics, observability — อะไรที่ scan ไม่ใช่ seek
Object storage — S3, GCSใหญ่ได้ไม่จำกัด, ถูก, durable มาก, มี versioning และ lifecycle rule ในตัวไม่ใช่ filesystem ไม่ใช่ database; latency ต่อ object; LIST ไม่ใช่ query engine (ราคาและเวลาโตตามจำนวน object ใน prefix) — ระดับ consistency ต่างกันตาม provider ให้ยืนยันกับเอกสารของตัวที่ใช้เอกสาร, รูป, ไฟล์ KYC, backup, data lake, archive tier
Time-series — Prometheus, Mimir, Timescale, InfluxDBเก็บ metric ได้มีประสิทธิภาพ, มี downsampling และ retention ในตัวcardinality ระเบิดแล้วตาย (ห้าม label ด้วย user_id)metric และ monitoring — ไม่ใช่ business record
Graph — Neo4j, Neptunequery แบบเดินหลาย hop ที่เขียนด้วย SQL เจ็บปวดวุฒิภาวะ operational, คนน้อย, scale write ยากfraud ring, เครือข่ายความเป็นเจ้าของ, recommendation — เมื่อความสัมพันธ์คือข้อมูล

Real case — Discord ย้ายที่เก็บ message 2 ครั้ง

Discord เผยแพร่เรื่องการย้าย 2 ครั้งห่างกันไม่กี่ปี

ครั้งแรก ย้าย message จาก MongoDB ไป Cassandra โดยเลือก partition key เป็น (channel_id, bucket) เพื่อให้ message ของ channel เดียวกันอยู่ด้วยกัน และ time bucket ทำให้ขนาด partition มีขอบเขต — เป็น wide-column model ตามตำรา ที่ถูกขับด้วย access pattern เดียวคือ "อ่าน message ล่าสุดใน channel นี้"

ครั้งที่ 2 ตอนมี message เป็น 1 ล้านล้าน พวกเขาย้ายจาก Cassandra ไป ScyllaDB และ — ที่สำคัญกว่า — เพิ่ม "data service" เขียนด้วย Rust ไว้หน้าฐานข้อมูล ที่รวม request ที่ขอ partition เดียวกันพร้อมกันให้เป็น query เดียว เพราะ channel ที่ร้อนทำให้เกิดงานซ้ำและ latency spike

บทเรียน 3 ข้อ: partition key คือ design, การ bucket ทำให้ขนาด partition มีขอบเขตตามเวลา, และการวาง service ที่ฉลาดไว้หน้าฐานข้อมูลมักถูกกว่าการเปลี่ยนฐานข้อมูล — How Discord Stores Billions of Messages · …Trillions of Messages

Datastore ที่เป็นกระแสในปี 2026

หัวข้อนี้ไม่ใช่รายการ "ของที่ควรใช้" — เป็นรายการ "ของที่คุณจะโดนถามถึง และควรตอบได้ว่ามันเก่งเพราะอะไร" สังเกตว่าเกือบทุกตัวในตารางนี้ไม่ได้ชนะด้วยการเป็นฐานข้อมูลที่ดีกว่าในทุกด้าน แต่ชนะด้วยการตัดความสามารถบางอย่างทิ้งอย่างจงใจ เพื่อแลกกับความเก่งเฉพาะทางที่ชัดมาก นั่นคือสิ่งที่คุณควรมองหาเวลาประเมินของใหม่

ตัวหมวดจุดแข็งที่จับต้องได้ราคาที่จ่ายลิงก์
ClickHousereal-time OLAPsparse primary index + columnar + vectorized → scan 1 พันล้านแถวได้ในเวลาระดับวินาที บนฮาร์ดแวร์ที่ไม่ต้องมหาศาลupdate/delete แพงและไม่ใช่ปฏิบัติการปกติ; ไม่มี transaction แบบ OLTPclickhouse.com/docs
DuckDBembedded OLAPanalytics engine ที่รันในโปรเซสเดียวกับแอป ไม่มี server ให้ดูแล อ่าน Parquet/Iceberg บน S3 ตรงๆโปรเซสเดียว ไม่ได้ออกแบบมาเป็น multi-user serverduckdb.org
Apache Icebergopen table formatตารางเดียวกันถูกอ่านด้วย engine หลายตัว, snapshot ทำให้ time travel และ rollback เป็นของที่มีอยู่แล้วต้องมี catalog + งาน compaction; ไม่ใช่ database มันคือ "รูปแบบตาราง"iceberg.apache.org/spec
TigerBeetleOLTP เฉพาะทางเรื่องเงินdebit/credit อยู่ในฐานข้อมูล ไม่ต้องถือ lock ข้าม network; batch ได้ถึง 8,190 transfer ต่อ requestschema ตายตัว (มีแค่ account/transfer); single leader; ต้องมี Postgres คู่ไปด้วยdocs.tigerbeetle.com
Aurora DSQLdistributed SQLactive-active หลาย region พร้อม strong consistency โดยไม่ต้องดูแล nodeไม่มี foreign key/trigger/PL-pgSQL, จำกัด 3,000 แถวต่อ transaction, ต้องเขียน retry เองaws.amazon.com/rds/aurora/dsql
Valkeykey-valuefork ของ Redis 7.2.4 ภายใต้ Linux Foundation ด้วย BSD-3 — เป็น default ของ managed service หลายเจ้าแล้วecosystem ของ module แยกทางกับ Redis ตั้งแต่จุด forkvalkey.io
Postgres + extensionrelational ที่กลืนงานคนอื่นpgvector, PostGIS, TimescaleDB ทำให้ store เดียวครอบ 3–4 use case ได้จริงทุก extension คือ dependency ที่ต้องรอดตอน major upgradepostgresql.org
turbopuffer / SlateDBobject-storage-nativeยก durability ให้ object storage → ไม่มีค่า replication ข้าม AZ และ scale ลงเหลือเกือบศูนย์ได้latency พื้นคือ latency ของ object storage ต้องพึ่ง cache ทุกชั้นturbopuffer.com · slatedb.io
ScyllaDBwide-columnshard-per-core เขียนด้วย C++ ไม่มี GC pause → tail latency นิ่งกว่า Cassandra ที่โหลดเท่ากันยังเป็น wide-column อยู่ดี — partition key ผิดก็จบเหมือนกันscylladb.com
CockroachDB / TiDBdistributed SQLSQL แบบกระจายที่ shard/rebalance ให้เอง, survive การเสีย node หรือ AZ ได้โดยไม่ต้อง failover ด้วยมือlatency ต่อ transaction สูงกว่า single-node เพราะต้อง coordinate; ราคาและความซับซ้อนของ opscockroachlabs.com · pingcap.com
StarRocks / Apache Dorisreal-time OLAPคู่แข่งสาย MPP ของ ClickHouse ที่เน้น join หลายตารางและ query แบบ BI มากกว่าecosystem และคนในตลาดยังเล็กกว่าstarrocks.io · doris.apache.org

อ่านตารางนี้ให้ถูกวิธี

คอลัมน์ "ราคาที่จ่าย" สำคัญกว่าคอลัมน์ "จุดแข็ง" เพราะจุดแข็งคือสิ่งที่ vendor พูดให้ฟรีอยู่แล้ว แต่ราคาคือสิ่งที่คุณจะเจอในเดือนที่ 6 เวลานำเสนอในห้องประชุม ให้พูดราคาก่อน — คนจะเชื่อจุดแข็งที่คุณพูดต่อจากนั้นมากขึ้น

ClickHouse — OLAP ที่ออกแบบให้ scan ไม่ใช่ seek

กลไกที่ทำให้มันเก่ง — ไม่ใช่เวทมนตร์ มันคือ 3 อย่างที่ทำงานทับกัน

  1. Sparse primary index — ClickHouse ไม่ทำ index ทีละแถว มันแบ่งข้อมูลเป็น granule ขนาด 8,192 แถวโดยค่าเริ่มต้น แล้วเก็บ index 1 entry ต่อ granule ผลคือ index ของตารางระดับหลายล้านแถวมีขนาดเล็กพอที่จะโหลดเข้า memory ได้ทั้งก้อน query จึงทำ binary search บน index ใน memory เพื่อตัด granule ที่ไม่เกี่ยวออกก่อนแตะดิสก์ — เอกสารของ ClickHouse ยกตัวอย่างตาราง 8.87 ล้านแถวที่ได้ประมาณ 1,083 granule และ primary index ขนาดระดับร้อยกิโลไบต์
  2. Columnar + compression codec เฉพาะชนิด — คอลัมน์ timestamp ที่เรียงกันใช้ delta encoding, คอลัมน์ enum ใช้ dictionary, แล้วทับด้วย LZ4 หรือ ZSTD อีกชั้น
  3. MergeTree — write เข้ามาเป็น part ที่ immutable แล้วมี background merge รวม part (เป็น LSM ในรูปแบบของ column store) → write path เป็น sequential ล้วน

ราคาที่จ่าย และนี่คือส่วนที่คนข้ามบ่อยที่สุด

  • ALTER TABLE ... UPDATE/DELETE คือ mutation ไม่ใช่ UPDATE แบบ OLTP — มันเขียน part ใหม่ทั้งก้อน และห้ามแก้คอลัมน์ที่อยู่ใน primary key หรือ sorting key เอกสารของ ClickHouse เองแนะนำให้ใช้ deduplication หรือ lightweight delete แทนการ mutate บ่อยๆ
  • ไม่มี transaction ข้ามตารางแบบที่คุณใช้ป้องกัน invariant เรื่องเงิน
  • ORDER BY key ของตารางคือ design เหมือน partition key ของ Cassandra — เลือกผิดแล้ว query ที่ไม่ตรงกับมันจะกลาย full scan

ในบริบท fintech ควรวางมันไว้ตรงไหน — fraud analytics, product analytics, observability, และงาน reconciliation ที่ต้อง scan ธุรกรรมทั้งวัน ไม่ใช่ ledger และไม่ใช่ที่ที่คุณตัดสินใจหักเงิน — ตรงนั้นดูบทที่ 16

ClickHouse ไม่ทดแทน Postgres และ Postgres ไม่ทดแทน ClickHouse

ข้อผิดพลาดที่เห็นบ่อยคือทีมเอา ClickHouse มาเป็น operational database เพราะ benchmark สวย แล้วไปเจอกำแพงตอนต้องแก้แถวเดียวหรือต้องการ unique constraint ทางกลับกันก็มี: ทีมยัด analytical query ลง read replica ของ Postgres จนกิน I/O จนกระทบ production 2 ระบบนี้อยู่คนละแกนของ "physical layout" ที่อธิบายไว้ข้างบน วิธีเชื่อมที่ถูกคือ CDC จาก Postgres ไป ClickHouse ไม่ใช่เลือกอันใดอันหนึ่ง

DuckDB และ DuckLake — analytics ที่ไม่ต้องมี cluster

กลไก — DuckDB เป็น analytical database ที่รัน ในโปรเซสเดียวกับแอปของคุณ (คนมักเรียกว่า "SQLite ของฝั่ง analytics") ไม่มี server ไม่มี port ไม่มี daemon แต่ข้างในเป็น columnar + vectorized execution เต็มรูปแบบ และมันอ่าน Parquet, CSV, JSON รวมถึงตาราง Iceberg บน object storage ได้โดยตรง โดยไม่ต้อง import เข้ามาก่อน

จุดแข็งที่จับต้องได้

  • งาน reconciliation ปลายวันที่เคยต้องยก cluster ขึ้นมา กลายเป็น job เดียวในคอนเทนเนอร์เดียว
  • Data engineer ทดสอบ query บนข้อมูลจริงบนเครื่องตัวเองได้ ไม่ต้องรอ warehouse quota
  • ใช้เป็น query engine ฝังใน service เพื่อทำ aggregation บนไฟล์ที่ดึงมาจาก S3

ราคาที่จ่าย — โปรเซสเดียว ไม่ใช่ multi-user database server, ไม่มี concurrency control สำหรับผู้เขียนหลายคนพร้อมกันแบบ Postgres, และขนาดข้อมูลที่ทำงานได้ผูกกับเครื่อง 1 เครื่อง (แม้จะไปได้ไกลกว่าที่คนคิดมาก)

DuckLake คือส่วนขยายที่เก็บ metadata ของ lakehouse ไว้ใน SQL database แทนที่จะเป็น ไฟล์ catalog บน object storage — ผลคือ metadata lookup และ partition pruning เร็วขึ้น และได้ multi-table transaction กับ time travel มาในอินเทอร์เฟซ SQL เดียว — ducklake.select · DuckDB Iceberg extension

Apache Iceberg — table format ที่แยก storage ออกจาก engine

นี่คือของที่คนสับสนมากที่สุดในตารางนี้ Iceberg ไม่ใช่ database มันคือ สเปกว่าไฟล์ Parquet กองหนึ่งบน object storage จะถูกเรียกว่า "ตาราง" ได้ยังไง — มี manifest บอกว่าไฟล์ไหนอยู่ในตาราง, มี snapshot บอกว่าสถานะ ณ เวลาหนึ่งคืออะไร, และมี schema evolution ที่ไม่ต้องเขียนข้อมูลใหม่

จุดแข็งที่จับต้องได้ — เป็นเรื่อง lock-in มากกว่าเรื่องความเร็ว

  • ตารางเดียวกันถูกอ่าน/เขียนได้จากหลาย engine (Spark, Trino, Flink, DuckDB, ClickHouse, Snowflake, Databricks) → คุณเปลี่ยน query engine ได้โดยไม่ต้องย้ายข้อมูล ซึ่งเป็น exit path ที่ประเมินค่าเป็นตัวเงินได้ในการเจรจากับ vendor
  • Snapshot ทำให้ time travel และ rollback เป็นของที่มีอยู่แล้ว ไม่ต้องสร้างเอง — มีค่ามากตอนตรวจสอบย้อนหลังและตอน pipeline เขียนข้อมูลผิด
  • Optimistic concurrency + serializable isolation ตามสเปก โดย reader ไม่ต้องถือ lock

สถานะสเปก (ยืนยันจากสเปกโดยตรง) — เวอร์ชัน 1, 2 และ 3 เสร็จและถูกรับรองโดยชุมชนแล้ว ส่วนเวอร์ชัน 4 ยังอยู่ระหว่างพัฒนาและยังไม่ถูกรับรองอย่างเป็นทางการ

Spec versionเพิ่มอะไร
v1ตาราง analytic บนไฟล์ immutable (Parquet, Avro, ORC)
v2row-level delete ผ่าน delete file — ลบ/แก้ทีละแถวได้โดยไม่ต้องเขียนไฟล์ใหม่ทั้งไฟล์
v3ชนิดข้อมูลใหม่ (nanosecond timestamp, variant, geometry, geography, unknown), default value ของคอลัมน์, multi-argument transform, row lineage, binary deletion vector, table encryption key
v4(กำลังพัฒนา) ปรับโครงสร้าง metadata และรองรับ relative location

ราคาที่จ่าย — ต้องมี catalog (ตัวที่บอกว่าตารางล่าสุดอยู่ที่ metadata ไฟล์ไหน), ต้องมี maintenance job สำหรับ compaction และลบ snapshot เก่า และถ้า pipeline เขียนไฟล์เล็กๆ ถี่ๆ คุณจะเจอ small-file problem ที่ทำให้ query ช้าลงเรื่อยๆ

ฝั่ง AWS มี S3 Tables ที่ทำ Iceberg ให้เป็น storage primitive ที่จัดการ compaction ให้ ซึ่งลดงาน maintenance ข้างต้นไปได้ส่วนหนึ่ง — แลกกับการผูกกับ managed service ของเจ้าเดียว

TigerBeetle — OLTP ที่ตัดทุกอย่างทิ้งเพื่อ debit/credit

ตัวนี้เกี่ยวกับงานที่พวกเราทำโดยตรงที่สุดในรายการทั้งหมด จึงควรเข้าใจให้ละเอียดกว่าตัวอื่น

กลไกที่ทำให้มันเก่ง — จุดสำคัญคือเรื่อง interface ไม่ใช่เรื่อง engine

ฐานข้อมูลทั่วไปให้ business logic อยู่ที่แอป แล้วแอปต้องถือ lock ข้ามเน็ตเวิร์ก ระหว่างที่คิดว่าจะหักเงินเท่าไหร่ — พอ contention สูง (บัญชีร้อนไม่กี่บัญชีอยู่ในธุรกรรมส่วนใหญ่) ทั้ง latency และ throughput พังพร้อมกันตาม Little's Law TigerBeetle ย้าย debit/credit เข้าไปอยู่ในฐานข้อมูลเอง จึงไม่ต้องมี lock ข้ามเน็ตเวิร์กเลย

เมื่อ interface ถูกแล้ว วิศวกรรมที่เหลือถึงจะมีผล — และของพวกนี้เป็นรูปธรรมมาก

การตัดสินใจผลที่จับต้องได้
ทุก request คือ batch สูงสุด 8,190 transferต้นทุนของ consensus จ่ายครั้งเดียวต่อ batch ไม่ใช่ต่อ transfer · และตอนโหลดเบา batch จะเล็กลงเองเพื่อแลกกลับเป็น latency ที่ดีขึ้น
Transfer มีขนาด 128 ไบต์ และ cache-line alignedการประมวลผล 1 batch กลายเป็น CPU loop เดียวที่แน่น
จองหน่วยความจำแบบ static ทั้งหมดไม่มี GC pause, ไม่มี memory fragmentation, ไม่มีเคส out-of-memory กลางทาง
เขียนด้วย Zig, ไม่มี dependency ภายนอก, ออกแบบรอบ io_uringทุกชั้นถูกออกแบบร่วมกันเพื่อ workload เดียว
Viewstamped Replication + strict serializabilityมีรายงานวิเคราะห์จาก Jepsen ให้อ่านเป็นหลักฐานอิสระ
การสร้าง account/transfer เป็น idempotent ตาม idสร้างซ้ำด้วย id เดิมได้ผลลัพธ์ exists ไม่ใช่ยอดซ้ำ — ตรงกับ idempotency key ในบทที่ 4

ราคาที่จ่าย และมันแพงจริง

  • Schema ตายตัว — เก็บได้แค่ Account กับ Transfer ไม่มีชื่อลูกค้า ไม่มีสถานะ KYC ไม่มี metadata ของธุรกรรม แปลว่าคุณยังต้องมี Postgres คู่ไปด้วยเสมอ และคุณเพิ่งสร้างปัญหา dual-write ให้ตัวเอง (วิธีแก้อยู่ในบทที่ 16)
  • Single-threaded และ single leader โดยตั้งใจ — เอกสารระบุตรงๆ ว่าการเพิ่ม node เพิ่มความน่าเชื่อถือ ไม่ใช่ throughput เหตุผลคือการ shard ฐานข้อมูลการเงินทำได้ยาก เพราะบัญชีร้อนไม่กี่บัญชีจะทำให้ shard นั้นเป็นคอขวดอยู่ดี
  • ไม่มี SQL, ไม่มี ad-hoc query, เครื่องมือ ops รอบตัวยังน้อยกว่า Postgres มาก
  • ทีมต้องเรียนรู้ data model แบบ debit/credit ให้แน่นก่อน ไม่ใช่หลัง

เกณฑ์ตัดสินใจที่ตรงไปตรงมา

ถ้า ledger ของคุณยังอยู่ในระดับที่ Postgres รับไหวโดย p99 ยังอยู่ในงบ ใช้ Postgres ต่อไป — double-entry ledger ที่ถูกต้องใน Postgres มีค่ามากกว่า ledger ที่เร็วในฐานข้อมูลที่ทีมยังไม่เคย operate จริง

TigerBeetle เริ่มคุ้มเมื่อคุณวัดได้แล้วว่า contention บนบัญชีร้อน (บัญชี settlement, บัญชี merchant รายใหญ่, บัญชีตัวกลางของ scheme) เป็นคอขวดจริง ไม่ใช่แค่กลัวว่าจะเป็น — และการตัดสินใจย้าย ledger core ต้องมีเจ้าของที่รับผิดชอบเซ็นชื่อพร้อมวันที่ ไม่ใช่การตัดสินใจของ engineer คนเดียว

Aurora DSQL และตระกูล distributed SQL

สัญญาของหมวดนี้ — SQL ที่ scale ตามแนวนอนและทน node/AZ ตายได้ โดยที่คุณยังเขียน SQL เหมือนเดิม Aurora DSQL (GA เมื่อ 27 พ.ค. 2025) เป็นตัวที่ผลักไปไกลสุดฝั่ง serverless: active-active หลาย region พร้อม strong consistency โดยไม่มี node ให้คุณดูแลเลย

แต่ "PostgreSQL-compatible" ไม่ได้แปลว่า drop-in และนี่คือบทเรียนที่ใช้ได้กับทั้งหมวด ข้อจำกัดต่อไปนี้มาจากเอกสารของ AWS โดยตรง

ข้อจำกัดผลต่อโค้ดที่คุณมีอยู่
ไม่มี foreign key — ต้องตรวจ referential integrity ที่ชั้นแอปinvariant ที่คุณเคยฝากไว้กับ DB ย้ายมาอยู่ในโค้ดที่มี bug ได้
ไม่มี trigger และไม่มี PL/pgSQL (มีแต่ SQL function)logic ที่อยู่ใน trigger ต้องย้ายไป application หรือ event-driven
Optimistic concurrency control — ไม่มี lock, ชนกันแล้วได้ serialization error ตอน commitทุก transaction ต้องมี retry loop และต้อง idempotent
Isolation คงที่ที่ Repeatable Read เท่านั้นปรับ isolation ต่อ transaction ไม่ได้
1 transaction แก้ได้ไม่เกิน 3,000 แถว (นับรวมทุก DML)batch job ที่เคยอัปเดต 10,000 แถวใน transaction เดียวต้องเขียนใหม่
DDL กับ DML ต้องแยก transaction และ 1 DDL ต่อ 1 transactionmigration script ส่วนใหญ่ต้องรื้อ
1 database ต่อ cluster, ไม่มี temp table, collation C, timezone UTC, connection หมดอายุใน 1 ชั่วโมงconnection pool และ query ที่พึ่ง collation ต้องตรวจใหม่

วิธีอ่านข้อจำกัดพวกนี้ให้เป็นประโยชน์ — มันไม่ใช่ "ข้อบกพร่อง" แต่คือราคาของ active-active ข้าม region ที่ต้องมีคนจ่าย ระบบกระจายที่ไม่ต้องการ coordinator กลางจะไม่มีทางให้ FK แบบที่ตรวจข้ามทั้งคลัสเตอร์ได้ฟรี ทางเลือกอื่นในหมวดนี้ (CockroachDB, TiDB, Spanner) เลือกจ่ายราคาคนละจุดกัน — ตัวที่ให้ FK มักจ่ายเป็น latency ต่อ transaction แทน

คำถามที่ต้องถามก่อนเลือก distributed SQL

"เราต้องการ write หลาย region พร้อมกัน จริงหรือ หรือแค่ต้องการ survive การเสีย 1 region" คำตอบ 2 ข้อนี้นำไปสู่ architecture คนละแบบและราคาคนละระดับ — ส่วนใหญ่ต้องการอย่างหลัง ซึ่ง Postgres แบบ multi-AZ + DR ที่ซ้อมแล้วตอบได้ ดูบทที่ 13

Valkey กับ Redis — เรื่อง license ที่กลายเป็นเรื่อง architecture

ลำดับเหตุการณ์ที่ควรรู้ เพราะมันกระทบว่า managed service ที่คุณใช้จะเป็นตัวไหนในอีก 3 ปี

  • มี.ค. 2024 — Redis เปลี่ยนจาก BSD ไปเป็น license แบบ source-available (RSALv2 / SSPLv1)
  • มี.ค. 2024 — Linux Foundation ประกาศ Valkey ซึ่ง fork มาจาก Redis 7.2.4 ภายใต้ BSD-3 โดยมีผู้ร่วมก่อตั้งเป็นผู้ให้บริการคลาวด์รายใหญ่หลายเจ้า
  • พ.ค. 2025 — Redis เพิ่มทางเลือก AGPLv3 ให้ Redis 8 (ประกาศจาก Redis)

ทำไม engineer ต้องสนใจ — เพราะ managed service คือสิ่งที่คุณใช้จริง ไม่ใช่ซอร์สโค้ด เมื่อผู้ให้บริการคลาวด์ย้ายค่าเริ่มต้นไปทาง Valkey สิ่งที่ตามมาคือ roadmap ของฟีเจอร์, โครงสร้างราคา, และชุด module ที่ใช้ได้ เดินแยกทางกันตั้งแต่จุด fork

สิ่งที่ต้องตรวจก่อนเลือกข้าง (เป็นรูปธรรม)

  • คุณใช้ module อะไรอยู่ (search, JSON, time-series, bloom) และมันมีในฝั่งที่คุณจะไปไหม
  • client library ที่ทีมใช้ ประกาศรองรับเวอร์ชันไหนของฝั่งไหน
  • ถ้าเป็นงานที่ regulated ทีม legal ต้องรู้ว่าคุณใช้ AGPLv3 หรือ BSD-3 — นี่เป็นคำถามที่ต้องส่งให้เจ้าของด้าน legal ตอบ ไม่ใช่ engineer ตัดสินเอง

Postgres ที่กลืนงานของ store อื่นไปเรื่อยๆ

กระแสที่แรงที่สุดในปี 2026 อาจไม่ใช่ฐานข้อมูลใหม่เลย แต่คือการที่ Postgres ทำงานที่เคยต้องใช้ store แยกได้ "ดีพอ" มากขึ้นทุกปี จนเกณฑ์ที่จะเพิ่ม store ใหม่สูงขึ้นเรื่อยๆ

งานเคยต้องใช้ตอนนี้ทำใน Postgres ได้ด้วยเพดานที่ควรรู้
semantic searchvector database แยกpgvector (HNSW / IVFFlat)ระดับหลายสิบล้าน vector ขึ้นไปเริ่มคุ้มที่จะแยก
spatial queryระบบ GIS แยกPostGISpolygon จริงและ index เชิงพื้นที่ครบ
time-seriesInfluxDBTimescaleDB / partition ตามเวลาcardinality ยังเป็นข้อจำกัดเหมือนเดิม
queue เบาๆRedis / SQSSELECT ... FOR UPDATE SKIP LOCKEDthroughput สูงมากๆ ยังควรใช้ broker จริง
JSON documentMongoDBjsonb + GIN indexschema drift ยังเป็นปัญหาเดิม

ฝั่งตัวเครื่องยนต์เองก็ขยับ — PostgreSQL 18 เพิ่มระบบ asynchronous I/O (เลือก method ได้ทั้งแบบ worker และ io_uring), uuidv7() ที่ให้ id เรียงตามเวลาจึงเป็นมิตรกับ B-tree index มากกว่า UUIDv4, virtual generated column, skip scan สำหรับ B-tree และ OAuth 2.0 authentication

`uuidv7()` แก้ปัญหาที่เจอบ่อยในระบบเงิน

UUIDv4 สุ่มทั้งก้อน → การ insert กระจายทั่ว B-tree ทำให้ page ร้อนกระจายและ index โตเร็ว UUIDv7 มี timestamp เป็น prefix → insert ไปตกใกล้ๆ กันตามเวลา ได้ locality ที่ดีกว่าโดยยังไม่เดา id ถัดไปได้ง่ายเท่า sequential integer รายละเอียดเรื่องการเลือก ID อยู่ในบทที่ 10

[!WARNING] ทุก extension คือหนี้ตอน major upgrade "ใช้ Postgres ตัวเดียวจบ" ฟังดูเรียบง่าย แต่ Postgres ที่มี 6 extension คือ 6 โครงการที่ต้องออกเวอร์ชันรองรับ major version ใหม่ ก่อน ที่คุณจะอัปเกรดได้ ก่อนเพิ่ม extension ให้ถามว่า "ถ้าตัวนี้ตามไม่ทัน Postgres เวอร์ชันหน้า เราจะทำยังไง"

Object-storage-native — เมื่อ S3 กลายเป็นดิสก์หลัก

แนวคิด — แทนที่จะเก็บ state บนดิสก์ของ node แล้ว replicate ข้าม AZ เอง ระบบกลุ่มนี้ยก durability ทั้งหมดให้ object storage แล้วใช้ NVMe กับ RAM เป็น cache เท่านั้น node จึงกลายเป็นของที่ทิ้งได้จริง ไม่มี state ที่ต้องกู้

จุดแข็งที่จับต้องได้

  • ไม่มีค่า replication ข้าม AZ เพราะ object storage จัดการ durability ให้แล้วในราคาของมัน
  • scale ลงเหลือเกือบศูนย์ได้ — workload ที่ query เป็นช่วงๆ ไม่ต้องจ่ายค่า cluster ที่เปิดค้างไว้
  • ไม่มี state ที่ต้อง rebalance ตอนเพิ่ม/ลด node ทำให้ operational model ง่ายลงมาก
  • ต้นทุนต่อ GB ของ object storage ต่ำกว่า memory มาก — เหมาะกับข้อมูล cold ที่ต้องค้นได้เป็นครั้งคราว (ตัวเลขเปรียบเทียบที่ vendor เผยแพร่เป็น illustrative ให้ยืนยันกับ pricing ของ region คุณเอง)

ราคาที่จ่าย — latency พื้นของ object storage อยู่ระดับ 10 มิลลิวินาทีขึ้นไปต่อ request ระบบกลุ่มนี้จึงต้องมี cache ทุกชั้นและ p99 จะแย่ลงชัดเจนตอน cache miss ไม่เหมาะกับ write ที่ต้อง ack ในระดับมิลลิวินาทีเดียว หรือ read ที่ต้องนิ่งทุก request

ตัวอย่างที่อ่านได้จริง — turbopuffer (search + vector), SlateDB (LSM ที่เขียนลง object storage ตรง), Quickwit (log search) และฝั่ง streaming ก็มี WarpStream ที่ทำ Kafka protocol บน object storage ล้วน (Confluent ประกาศเข้าซื้อ) — รูปแบบเดียวกันโผล่ในหลายหมวดพร้อมกัน นั่นคือสัญญาณว่ามันเป็นแนวสถาปัตยกรรม ไม่ใช่ผลิตภัณฑ์เดี่ยว

วิธีประเมิน datastore ที่กำลังมาแรง

ของในหัวข้อที่แล้วจะเปลี่ยนไปในอีก 2 ปี แต่วิธีประเมินไม่เปลี่ยน ใช้ตารางนี้กับ datastore ตัวไหนก็ได้ รวมถึงตัวที่ยังไม่เกิด

คำถามหลักฐานที่ยอมรับได้ธงแดง
มันเก่งเพราะกลไกอะไรอธิบายได้ด้วย 3 แกนข้างบน และมีเอกสารสถาปัตยกรรมของตัวเองตอบได้แค่ "เร็วกว่า X เท่า" จาก benchmark ของ vendor เอง
ความถูกต้องถูกตรวจโดยคนนอกหรือยังรายงาน Jepsen, formal verification, deterministic simulation testing ที่เผยแพร่"เรามี test ครบ" โดยไม่มีอะไรให้อ่าน
ตอนพังมันพังยังไงเอกสารเรื่อง failure mode, การ recover, พฤติกรรมตอน network partitionเอกสารมีแต่ happy path
License และ governancelicense ที่ทีม legal อนุมัติแล้ว + governance ที่ไม่ได้ขึ้นกับบริษัทเดียวlicense ที่เพิ่งเปลี่ยน หรือเปลี่ยนได้ฝ่ายเดียว
ทางออกคืออะไรexport ออกเป็น format มาตรฐานได้ (Parquet, Iceberg, logical dump)ข้อมูลออกได้ทางเดียวคือ API ของเจ้าของ
ใครดูแลตอนตีสามมีคนในทีมอย่างน้อย 2 คนที่เคย operate จริง หรือมี managed service ที่มี SLA"เดี๋ยวเราเรียนรู้ระหว่างทาง" กับระบบที่มีเงินอยู่ในนั้น
backup กู้ได้จริงไหมเคยซ้อม restore ลงระบบเปล่าและจับเวลามี backup แต่ไม่เคยกู้
มันแทน store เดิมหรือมาเพิ่มมีแผนปลดของเดิมพร้อมวันที่คำตอบคือ "เพิ่ม" ทุกครั้ง

Deterministic simulation testing — คำที่จะได้ยินบ่อยขึ้น

ฐานข้อมูลรุ่นใหม่หลายตัวโฆษณาว่าใช้ deterministic simulation testing: รันทั้งระบบในโลกจำลองที่เวลา, network, และดิสก์ถูกควบคุมด้วย seed แล้วยิงความผิดพลาด (packet หาย, ดิสก์คืนข้อมูลเพี้ยน, node ตายกลาง commit) เป็นล้านรอบ จุดสำคัญคือเมื่อเจอ bug มัน reproduce ได้ด้วย seed เดิมเสมอ

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

Search index — derived เสมอ ไม่เคยเป็น authoritative

  • Populate จากฐานข้อมูลผ่าน CDC หรือ outbox เพื่อให้สร้างใหม่จากศูนย์ได้เสมอ
  • ตั้งงบเวลาสำหรับ full reindex — คุณจะต้องทำทุกครั้งที่แก้ mapping
  • คาดหวัง near-real-time (~1 s refresh) และออกแบบ UX ให้รองรับ ("เอกสารของคุณกำลังถูกจัดทำดัชนี")
  • คืนแค่ id จาก search แล้วไปดึงรายละเอียดจาก source of truth ป้องกันการแสดงข้อมูลเก่าและป้องกัน PII ค้างใน index

ตัวเลือกในหมวดนี้ปี 2026 กว้างขึ้นกว่าเดิม: Elasticsearch กับ OpenSearch เป็นสาย cluster เต็มรูปแบบ, ส่วน Quickwit และ turbopuffer เป็นสาย object-storage-native ที่แลก latency พื้น กับต้นทุนที่ต่ำลงมากสำหรับข้อมูล cold — เลือกจากรูปร่างของ query ไม่ใช่จากความใหม่

อย่าใส่ข้อมูลที่ต้องลบได้ลง search index โดยไม่มีแผน

ภายใต้ PDPA คำขอลบข้อมูลต้องไปถึงทุกที่ที่มีสำเนา ถ้า search index มีชื่อ-นามสกุล-เบอร์โทร คุณต้องมีกลไกลบที่นั่นด้วย และต้องพิสูจน์ได้ว่าลบแล้ว ทางที่ง่ายกว่าคือ index เฉพาะ id กับ field ที่ต้องใช้ค้นจริง

ข้อนี้ใช้กับ derived store ทุกตัว ไม่ใช่แค่ search — ClickHouse ที่รับ CDC มาจาก Postgres ก็เป็นสำเนาที่ต้องลบได้เหมือนกัน และการลบใน columnar store แพงกว่าที่คิดมาก ให้ออกแบบตั้งแต่วันแรกว่า จะไม่ส่ง field ที่ระบุตัวบุคคลเข้าไป แทนที่จะไปหาวิธีลบทีหลัง

Object storage — ที่ที่ข้อมูลรั่วบ่อยที่สุด

กฎเหตุผล
ใช้ pre-signed URL เสมอbyte ไม่ต้องผ่าน application server เลย — ประหยัด bandwidth และ CPU
Validate content type และขนาดที่ server หลัง uploadcontent type ที่ client ประกาศมาคือคำโกหกที่รอวันเกิด
Scan malware ถ้าผู้ใช้ดาวน์โหลดไฟล์ของกันได้ไม่อย่างนั้นคุณกลายเป็น CDN แจกมัลแวร์
เปิด versioning และ lifecycle rule วันแรกundelete ได้ และย้ายไป cold tier อัตโนมัติ
เปิด block public access และตรวจสอบว่ามันเปิดจริงbucket ที่ config ผิดยังเป็นสาเหตุข้อมูลรั่วที่พบบ่อยที่สุดในโลกจริง
แยก bucket ตาม data classificationไฟล์ KYC ไม่ควรอยู่ bucket เดียวกับ asset ของหน้าเว็บ
เปิด server-side encryption ด้วย key ที่คุณควบคุม สำหรับ bucket ที่มีข้อมูลลูกค้าทำให้การเพิกถอน key เป็นกลไกควบคุมได้จริง ไม่ใช่แค่บรรทัดในนโยบาย
Upload flow ที่ถูก

1. Client -> API:  POST /v1/documents  (ประกาศ type, ขนาด, purpose)
2. API:            ตรวจสิทธิ์, สร้าง record (status = AWAITING_UPLOAD),
                   คืน pre-signed PUT URL ที่ผูก key + content-length + หมดอายุ 5 นาที
3. Client -> S3:   PUT byte ตรงเข้า object storage
4. S3 -> API:      event notification ว่า object มาถึง
5. API:            อ่าน metadata จริง (magic bytes, ขนาด), scan, แล้ว
                   set status = READY หรือ REJECTED
--> ห้ามเชื่อขั้นที่ 1 เลย ทุกอย่างต้องยืนยันที่ขั้นที่ 5

Pre-signed URL ที่ผูกไม่แน่น

pre-signed URL ที่ไม่จำกัด Content-Length ทำให้ใครอัปโหลดไฟล์ 50 GB ก็ได้ และที่ไม่จำกัด key prefix ทำให้เขียนทับ object ของคนอื่นได้ ผูก key ที่แน่นอน + content-length range + expiry สั้น ทุกครั้ง

Geo / spatial

เทคนิคแนวคิดเหมาะกับ
Geohashencode พิกัดเป็น string — prefix ที่ตรงกัน = พื้นที่เดียวกันindex ได้ในทุก store แม้ไม่มีความสามารถ spatial
S2 cell (Google)แบ่งโลกเป็น cell บนลูกบาศก์แล้ว map ลงเส้น Hilbertต้องการความแม่นของพื้นที่และการ cover region
H3 hexagon (Uber)hexagon แบบลำดับชั้น — ระยะถึงเพื่อนบ้านสม่ำเสมอกว่า grid สี่เหลี่ยม · ข้อจำกัดที่ต้องรู้: grid สร้างบน icosahedron จึงมี pentagon 12 เซลล์ และ cell ลูกไม่ซ้อนทับ cell แม่แบบพอดีจับคู่ผู้ใช้-คนขับ, การรวมสถิติเชิงพื้นที่
PostGISspatial extension บน Postgres รองรับ polygon จริงครอบคลุมความต้องการส่วนใหญ่ได้ และคุณมี Postgres อยู่แล้ว

ข่าวดีของปี 2026 คือฝั่ง analytics ตามมาแล้ว — Iceberg spec v3 เพิ่มชนิด geometry และ geography เข้ามาในตัว แปลว่าข้อมูลเชิงพื้นที่บน lakehouse ไม่ต้องเก็บเป็น WKT string แล้วแปลงเองอีกต่อไป

รายละเอียดการออกแบบ nearby search อยู่ในบทที่ 19 · อ้างอิง: H3 — Uber's hexagonal hierarchical spatial index

Time-series — ระวัง cardinality

Label ที่ทำให้ Prometheus ตาย

ห้าม:  http_requests_total{user_id="u_918273", path="/v1/transfers/trf_01J9XYZ"}
       --> 1 ล้านผู้ใช้ x 500,000 transfer = time series หลายพันล้านเส้น

ควร:  http_requests_total{route="/v1/transfers/:id", method="POST", status="201"}
       --> route ที่ normalize แล้ว x method x status = หลักร้อยเส้น

กฎ: label ต้องมีค่าที่เป็นไปได้จำกัดและรู้ล่วงหน้า ถ้าต้องการสืบย้อนถึง request รายตัว นั่นคืองานของ trace และ log ไม่ใช่ metric — Prometheus metric and label naming

ทำไม cardinality ถึงฆ่าระบบได้ — แต่ละชุด label ที่ไม่ซ้ำกันคือ time series 1 เส้น ที่ต้องมี index, มี memory ค้างไว้สำหรับ chunk ล่าสุด และมีต้นทุนตอน query ที่ต้องรวมเส้นเหล่านี้ การเพิ่ม label 1 ตัวที่มี 10,000 ค่าเป็นการคูณ จำนวนเส้นทั้งหมดด้วย 10,000 ไม่ใช่บวก นี่คือเหตุผลที่มันพังแบบฉับพลันไม่ใช่ค่อยๆ ช้าลง

ถ้าต้องเก็บ metric ระยะยาวหลายทีมรวมกัน ฝั่ง long-term storage อย่าง Grafana Mimir หรือ Thanos ช่วยเรื่อง retention และ multi-tenant ได้ — แต่ไม่ได้แก้ปัญหา cardinality ให้ ปัญหานั้นแก้ได้ที่ตอนตั้งชื่อ label เท่านั้น

Vector store — pgvector ก่อน แล้วค่อยคิดเรื่องอื่น

ใช้เมื่อทำ semantic search หรือ retrieval สำหรับฟีเจอร์ AI เท่านั้น ถ้า query ของคุณเป็นการหาคำที่ตรงกัน search index ธรรมดายังเป็นคำตอบที่ถูกกว่าและอธิบายผลลัพธ์ได้ง่ายกว่า

ลำดับการตัดสินใจที่เสียหายน้อยที่สุด

  1. เริ่มที่ pgvector ใน Postgres ที่คุณมีอยู่แล้ว — ได้ transaction, backup, สิทธิ์ และ join กับข้อมูลจริงมาฟรีทั้งหมด
  2. เลือก index ให้ตรงกับงาน — HNSW ให้ recall กับ latency ที่ดีกว่าแต่กิน memory และสร้างช้ากว่า ส่วน IVFFlat สร้างเร็วและกิน memory น้อยกว่าแต่ต้องมีข้อมูลก่อนถึงจะ train ได้
  3. วัด recall ไม่ใช่แค่ latency — vector index เป็น approximate ระบบที่ตอบเร็วแต่พลาดผลลัพธ์ที่ถูกต้องคือระบบที่ผิด และตัวเลขนี้ไม่โผล่ใน dashboard latency
  4. แยกออกเป็น vector database ต่างหากเมื่อวัดแล้วว่าไม่ไหว — ปกติคือเรื่องจำนวน vector ระดับหลายสิบล้านขึ้นไป, ความต้องการ filter ซับซ้อนพร้อม ANN, หรือการกระจายหลาย region ตัวเลือกในหมวดนี้เช่น Qdrant, Milvus, turbopuffer

Vector store กับ PDPA

embedding ที่สร้างจากข้อความของลูกค้า ยังเป็นข้อมูลที่มาจากบุคคล อย่าคิดว่าการแปลงเป็นตัวเลขทำให้มันหลุดจากขอบเขตการคุ้มครอง ต้องมีเส้นทางลบและต้องผูกกับ id ที่ตามไปลบต้นทางได้ ให้ตัดสินใจเรื่องนี้กับเจ้าของด้าน data protection พร้อมชื่อและวันที่ ไม่ใช่ให้ทีม engineer สรุปเอง

ตารางสรุป: จับคู่ pattern กับ store

Access patternStore ที่เหมาะ
point lookup ด้วย primary key + transactionrelational
invariant เรื่องยอดเงินที่ contention สูงมากrelational ก่อน · ledger เฉพาะทาง (TigerBeetle) เมื่อวัดแล้วว่าไม่ไหว
point lookup ด้วย key เดียว, ปริมาณมหาศาล, ไม่ต้อง joindocument / key-value
append หนักมาก อ่านตามช่วงเวลาใน partitionwide-column
full-text + facetingsearch (derived)
scan + aggregate หลายพันล้านแถว แบบ real-timeClickHouse / StarRocks / Doris
scan + aggregate ที่รอได้เป็นชั่วโมง หลาย engine ต้องอ่านร่วมกันlakehouse บน Iceberg
analytics บนไฟล์ที่มีอยู่แล้ว โดยไม่อยากตั้ง clusterDuckDB
SQL ที่ต้องรอดการเสียทั้ง region และ write ได้หลาย regiondistributed SQL (Aurora DSQL, CockroachDB, TiDB)
byte ก้อนใหญ่, immutableobject storage
ค้นข้อมูล cold ปริมาณมากเป็นครั้งคราว จ่ายตามการใช้จริงobject-storage-native (turbopuffer, Quickwit)
metric ที่ label จำกัดtime-series
เดินความสัมพันธ์หลาย hopgraph
ค้นความคล้ายเชิงความหมายpgvector ก่อน แล้วค่อยคิดเรื่องอื่น

Polyglot persistence มีต้นทุนที่คนไม่นับ

ทุก store ที่เพิ่มคือ: backup อีกชุด, การกู้คืนที่ต้องทดสอบอีกแบบ, monitoring อีกชุด, ทักษะที่ทีมต้องมีอีกอย่าง, เส้นทาง upgrade อีกเส้น, และผิวสัมผัสด้าน security อีก 1 ชุด (credential, network policy, การเข้ารหัส, audit log) พร้อมกับปัญหาใหม่คือ ข้อมูลชุดเดียวกันไม่ตรงกันระหว่าง store

เกณฑ์ที่ควรผ่านก่อนเพิ่ม store: มี access pattern ที่ store ที่มีอยู่ทำไม่ได้จริง และคุณวัดแล้วว่ามันทำไม่ได้ ไม่ใช่แค่คิดว่าทำไม่ได้ และคุณเขียนได้ว่าใครดูแลมันตอนตีสาม

Checklist ก่อนตัดสินใจเพิ่มหรือเปลี่ยน datastore

  • มี access pattern table จากบทที่ 14 แล้ว ไม่ใช่เริ่มจากชื่อผลิตภัณฑ์
  • อธิบายได้ว่าตัวที่เลือกอยู่ตรงไหนของ 3 แกน และทำไมแกนนั้นตรงกับ pattern ของเรา
  • เขียน ราคาที่ต้องจ่าย ของตัวเลือกนั้นไว้ใน design doc อย่างน้อย 3 ข้อ
  • วัดแล้วว่า store ที่มีอยู่ทำไม่ได้จริง พร้อมตัวเลขและวันที่ที่วัด
  • ถ้าเป็น derived store: มีเส้นทาง rebuild จาก source of truth และเคยทดสอบ rebuild แล้ว
  • ถ้าเป็น derived store: มีเส้นทางลบข้อมูลส่วนบุคคล และพิสูจน์ได้ว่าลบแล้ว
  • มีคนในทีมอย่างน้อย 2 คนที่ operate ตัวนี้ได้ หรือมี managed service พร้อม SLA ที่อ่านแล้ว
  • เคยซ้อม restore จาก backup ลงระบบเปล่าและจับเวลาไว้
  • license ผ่านการตรวจจากเจ้าของด้าน legal แล้ว พร้อมชื่อและวันที่
  • มีทางออก — export เป็น format มาตรฐานได้ และประเมินเวลาไว้แล้ว
  • มีแผนว่าจะปลด store เดิมเมื่อไหร่ หรือระบุชัดว่าจะอยู่คู่กันถาวรและใครดูแล

สรุปบทนี้

อธิบายทุก datastore ด้วย 3 แกน: physical layout, write path, storage coupling · Relational เป็น default โดยเฉพาะที่เกี่ยวกับเงิน · partition key คือ design ของ wide-column · ของที่เป็นกระแสในปี 2026 เกือบทุกตัวเก่งเพราะ ตัดความสามารถบางอย่างทิ้งอย่างจงใจ — จุดแข็งคือสิ่งที่ vendor บอกฟรี ราคาคือสิ่งที่คุณต้องไปหาเอง · search index และ read model เป็น derived ที่ต้อง rebuild ได้ · object storage ต้องปิด public access และ validate ทุกอย่างฝั่ง server · ห้าม label metric ด้วยค่าที่ cardinality ไม่จำกัด · และทุก store ที่เพิ่มคือ backup, monitoring, security surface, ทักษะ และปัญหาข้อมูลไม่ตรงกันอีก 1 ชุด