Big Data Analytics · บทที่ 7 จาก 13

CAP Theorem, ACID vs BASE, และ NoSQL

เมื่อสำเนาข้อมูลอยู่หลายเครื่อง คำว่า “ข้อมูลปัจจุบัน” ไม่ได้ง่ายเหมือนเดิม หากเครือข่ายขาด เราจะหยุดตอบเพื่อรักษาความจริงชุดเดียว หรือจะตอบต่อจากข้อมูลที่แต่ละฝั่งมี? บทนี้ไม่ได้สอนให้เลือกฐานข้อมูลจากป้าย SQL/NoSQL แต่สอนให้มองเห็นสิ่งที่ระบบยอมแลกภายใต้ความล้มเหลว

📚
ความตั้งใจของบท: แยก CAP Consistency ออกจาก ACID Consistency, แยก Availability ตามทฤษฎีออกจาก uptime และค่อยเชื่อมไปยัง ACID, BASE, NoSQL, HBase และ Cassandra ผ่าน workload กับ data model ที่ระบบต้องรองรับ

1CAP Theorem

ใจความที่ควรจำ
เมื่อเกิด Network Partition ระบบกระจายไม่สามารถรักษาทั้ง Consistency แบบที่คำสั่งดูเสมือนทำงานบนสำเนาเดียว และ Availability ที่ทุกคำขอไปยังโหนดซึ่งยังทำงานต้องได้รับคำตอบสำเร็จพร้อมกันได้ ระบบต้องยอมเสียอย่างใดอย่างหนึ่งในช่วงที่ Partition ยังอยู่

ประโยคยอดนิยมว่า “CAP เลือกได้เพียงสองในสาม” จำง่าย แต่ชวนให้คิดผิดว่า P เป็นตัวเลือกเหมือนติ๊กเมนู ในระบบที่สื่อสารผ่านเครือข่าย เราไม่สามารถสั่งว่า Partition ห้ามเกิด สิ่งที่ออกแบบได้คือ เมื่อ Partition เกิดแล้ว แต่ละ operation จะรักษา C หรือ A อย่างไร

คุณสมบัติความหมาย
Consistencyในบริบท CAP มักตีความใกล้กับ single-copy/linearizable behavior: operation ดูเหมือนเกิดทีละจุดบนสำเนาเดียว และเคารพลำดับเวลาจริงที่เกี่ยวข้อง ไม่ใช่เพียง “ทุก node เหมือนกันทุก microsecond”
Availabilityทุกคำขอที่ส่งถึงโหนดซึ่งไม่ล้มต้องจบด้วยคำตอบที่ไม่ใช่ error แม้คำตอบนั้นอาจไม่ใช่ข้อมูลรุ่นล่าสุดภายใต้โมเดล AP คำว่า timeout หรือ “ลองใหม่ทีหลัง” จึงถือว่าเสีย Availability ในความหมายเข้มของ CAP
Partition toleranceแบบจำลองยอมให้ข้อความระหว่างกลุ่มโหนดสูญหายหรือล่าช้าไม่มีกำหนด ระบบต้องนิยามพฤติกรรมในภาวะนี้ ไม่ได้หมายความว่าทุก operation ทุกฝั่งต้องสำเร็จ
เปรียบเหมือนธนาคารสองสาขาที่สายสื่อสารขาด
บัญชีมีเงิน 1,000 บาท สาขา A และ B ติดต่อกันไม่ได้ ถ้าทั้งสองสาขายอมถอน 800 บาท ระบบยังให้บริการทั้งสองฝั่ง แต่ยอดรวมอาจติดลบเมื่อเชื่อมกลับมา ถ้าต้องป้องกันเงินเกินบัญชี อย่างน้อยหนึ่งฝั่งต้องหยุดหรือจำกัดการถอน นั่นคือยอมลด Availability ของ operation นี้เพื่อรักษาข้อกำหนดด้าน Consistency

Availability ไม่ใช่แค่ “Server ตอบอะไรสักอย่าง”

ถ้าตอบ error ได้ก็นับว่า Available ระบบที่ปิดฐานข้อมูลแล้วตอบ HTTP 500 ทุกครั้งจะมี Availability 100% ซึ่งเห็นได้ชัดว่าไม่ใช่ความหมายของทฤษฎี คำตอบต้องเป็นผลที่ถูกต้องตามข้อกำหนดของโมเดลที่ระบบประกาศ แม้อาจเป็นข้อมูลเก่าในระบบที่ยอมรับ stale read

Consistency ใน CAP ใกล้ Linearizability

Linearizability ทำให้แต่ละ operation ดูเหมือนเกิดขึ้น ณ จุดหนึ่งระหว่างเวลาเริ่มกับเวลาจบ หาก write เสร็จก่อน read เริ่ม read ควรเห็น write นั้น การรับประกันนี้เข้าใจง่ายสำหรับโปรแกรมเมอร์ แต่เมื่อเครือข่ายแบ่งฝั่ง โหนดที่ติดต่อเจ้าของข้อมูลหรือ quorum ไม่ได้อาจต้องปฏิเสธคำขอ

Partition ไม่เหมือน Node Crash อย่างเดียว

โหนดอาจยังทำงานดีและตอบ client ในพื้นที่ของตน แต่สื่อสารกับอีกกลุ่มไม่ได้ ปัญหาจึงยากกว่าเครื่องดับ เพราะทั้งสองฝั่งอาจเชื่อว่าตนควรทำงานต่อ สถานการณ์นี้นำไปสู่ split brain หากไม่มี quorum, leader lease, fencing หรือกลไกป้องกันที่เหมาะสม

การเลือกไม่ได้เกิดระดับผลิตภัณฑ์อย่างเดียว

ระบบเดียวอาจให้ read concern, write concern หรือ consistency level หลายแบบ Operation อ่านหน้า feed อาจยอม stale ได้ ส่วนการตัดยอดสินค้าต้องรอ quorum การถามว่า “ฐานข้อมูลนี้เป็น CP หรือ AP” จึงเป็นจุดเริ่มต้น แต่คำถามที่ดีกว่าคือ:

อย่าติดป้ายผลิตภัณฑ์แบบตายตัว
MongoDB มี read concern, write concern และ read preference หลายระดับ Cassandra มี consistency level ต่อคำขอ ระบบบางตัวมีทั้งโหมด strong และ eventual การติดป้ายชื่อผลิตภัณฑ์ว่า CP/AP โดยไม่ระบุ topology, configuration และ operation จึงอาจทำให้นักศึกษาเข้าใจผิดมากกว่าช่วยจำ

CP: ยอมให้บางคำขอรอหรือถูกปฏิเสธ

ระบบที่รักษา linearizable write อาจกำหนดว่าต้องมี quorum หรือ leader ที่ถูกต้อง หากกลุ่มเสียงข้างมากติดต่อกันไม่ได้ ฝั่งเสียงข้างน้อยไม่รับ write ต่อ แม้เครื่องยังเปิดอยู่ จุดนี้ไม่ได้หมายความว่าทั้งระบบดับ บาง read หรือข้อมูลคนละ shard อาจยังให้บริการได้ Availability จึงควรวิเคราะห์ราย operation และ failure scope

AP: ตอบต่อ แล้วรับภาระ Reconciliation

ระบบที่ยอมให้หลายฝั่งรับ write ระหว่าง Partition ต้องมีวิธีจัดการ version เมื่อเครือข่ายกลับมา เช่น last-write-wins, vector clock, CRDT หรือ application-specific merge ไม่มีวิธีใดเหมาะทุกข้อมูล การเลือก AP ไม่ได้ทำให้ความขัดแย้งหายไป เพียงย้ายเวลาตัดสินจากตอนเขียนไปตอนรวมข้อมูลภายหลัง

CA มีประโยชน์เป็นภาพ แต่ไม่ใช่มุมของระบบกระจายเมื่อเกิด Partition

บนเครื่องเดียวไม่มี network partition ระหว่าง replicas จึงไม่อยู่ในเงื่อนไขที่ CAP บังคับให้เลือกระหว่าง C กับ A แต่เครื่องยังล้ม ดิสก์ยังเสีย และ transaction ยัง block ได้ คำว่า CA ไม่ได้แปลว่าฐานข้อมูลเครื่องเดียว Available ตลอดเวลา เพียงแต่ CAP trade-off ไม่ถูกกระตุ้นด้วย network partition ระหว่างสำเนา

เมื่อ Partition เกิดสิ่งที่รักษาสิ่งที่ยอมเสีย
แนว CPOperation ที่ยอมรับต้องยังรักษา consistency model ที่ประกาศบางคำขออาจ timeout, error หรือรอจน quorum กลับมา
แนว APโหนดที่ยังทำงานตอบ operation ต่อผลอาจ stale หรือเกิด concurrent versions ที่ต้อง reconcile

PACELC: แม้เครือข่ายปกติก็ยังมีสิ่งที่ต้องแลก

PACELC ขยายมุมมองว่า ถ้าเกิด Partition (P) เลือกระหว่าง Availability กับ Consistency; Else (E) เมื่อระบบปกติ ยังมักเลือกระหว่าง Latency กับ Consistency เช่นรอ replica ข้าม region มากขึ้นช่วยเพิ่มความมั่นใจแต่ response ช้าขึ้น ทำให้เห็นว่าการแลกไม่ได้เกิดเฉพาะวันระบบพัง

หยุดคิดสักครู่
ถ้าหน้าแสดงจำนวน Like อ่านช้ากว่าไม่กี่วินาที ผู้ใช้อาจยอมรับได้ แต่ถ้า coupon ใบเดียวถูกใช้สองครั้ง ความเสียหายต่างกัน ดังนั้นคำถามไม่ใช่ “องค์กรนี้เลือก AP หรือ CP” แต่คือ “ข้อมูลและ operation นี้ยอมรับ anomaly แบบใดได้บ้าง”

2ACID vs BASE: สองปรัชญาการออกแบบ Consistency

หัวข้อนี้มักวาง ACID กับ BASE ไว้คนละฝั่งราวกับต้องเลือกศาสนา แต่จริง ๆ แล้ว ACID อธิบายคุณสมบัติของ transaction ส่วน BASE เป็นคำย่อเชิงแนวคิดที่เน้น availability และ eventual convergence ระบบหนึ่งอาจมี transaction แบบ ACID ในขอบเขตหนึ่ง และใช้ asynchronous replication แบบ eventual ในอีกขอบเขตหนึ่งได้

ACID (ฐานข้อมูล relational แบบดั้งเดิม)
  • Atomicity — ทั้งหมดหรือไม่เลย
  • Consistency — ข้อมูลถูกต้องตามกฎเสมอ
  • Isolation — ธุรกรรมไม่รบกวนกัน
  • Durability — สำเร็จแล้วไม่หายแม้ระบบล่ม
เหมาะกับ: งานที่ invariant และขอบ transaction ชัด เช่นการโอนเงิน การจอง หรือการตัด stock แต่ NoSQL หลายระบบก็รองรับ transaction บางระดับเช่นกัน
BASE (แนวทางที่ผ่อน consistency บางส่วน)
  • Basically Available — ระบบพร้อมให้บริการเกือบตลอดเวลา
  • Soft state — สถานะข้อมูลอาจเปลี่ยนแปลงได้แม้ไม่มี input ใหม่ (เพราะกำลัง sync กันอยู่)
  • Eventual consistency — ข้อมูลจะตรงกันในที่สุด ถ้าไม่มี input ใหม่เข้ามาอีก
เหมาะกับ: ข้อมูลที่ยอมให้สำเนาตามกันภายหลังได้ เช่น metrics, cache หรือ activity feed บางชนิด แต่ต้องนิยามว่า “ในที่สุด” และวิธีแก้ conflict อย่างไร

Consistency ใน ACID ไม่ใช่ C ใน CAP

คำเดียวกันความหมายตัวอย่าง
ACID ConsistencyTransaction พาฐานข้อมูลจากสถานะที่ไม่ละเมิด invariant ไปยังอีกสถานะที่ไม่ละเมิด invariantยอดห้ามติดลบ, foreign key ต้องอ้างแถวที่มีอยู่
CAP Consistencyพฤติกรรมของสำเนาหลายชุดใกล้ single-copy/linearizable semantics ภายใต้ concurrent operations และ failureread หลัง write ที่จบแล้วเห็นค่ารุ่นนั้นตาม real-time order

ฐานข้อมูลอาจให้ transaction atomic แต่ replication read ไม่ linearizable หรือให้ linearizable register แต่ไม่มี multi-row invariant คำว่า Consistency จึงต้องตามด้วยคำถามว่า consistency model ใด และอยู่ในขอบเขต object, partition หรือ transaction ใด

Atomicity: ทั้งชุดสำเร็จหรือไม่มีผล

สมมุติโอนเงิน 100 บาทจาก A ไป B เราไม่ต้องการให้หัก A แล้วเครื่องล้มก่อนเพิ่ม B Atomicity ทำให้ transaction ทั้งชุด commit หรือ abort แต่ไม่ได้รับประกันเองว่าธุรกิจเขียนกฎถูก เช่นโปรแกรมอาจโอนไปบัญชีผิดอย่าง atomic มากก็ได้

Isolation มีหลายระดับ

คำว่า “Transaction ไม่รบกวนกัน” เป็นภาพเริ่มต้น แต่ระบบจริงมี isolation levels เช่น Read Committed, Snapshot Isolation และ Serializable ซึ่งยอม anomaly ต่างกัน ระดับสูงขึ้นมักเพิ่มการรอ การตรวจ conflict หรือการ abort การบอกเพียงว่า “รองรับ ACID” จึงยังไม่พอ ต้องดู isolation level และข้อจำกัดจริง

ปรากฏการณ์ภาพง่าย ๆผลกระทบ
Dirty Readอ่านค่าที่ยังไม่ commitค่าที่เห็นอาจถูก rollback หายไป
Non-repeatable Readอ่านแถวเดิมสองครั้งได้คนละค่ารายงานใน transaction เดียวไม่คงที่
Phantomquery เงื่อนไขเดิมพบจำนวนแถวใหม่ชุดข้อมูลเปลี่ยนระหว่าง transaction
Write Skewสอง transaction อ่าน snapshot เดียวแล้วเขียนคนละแถวinvariant ข้ามแถวอาจถูกละเมิดแม้ไม่มี write conflict ตรง ๆ

Durability ต้องถามว่าทนต่ออะไร

เมื่อระบบตอบว่า commit แล้ว ข้อมูลควรรอดจาก failure ตามขอบเขตที่ประกาศ แต่การเขียนลง memory, OS buffer, local journal, replica หนึ่งตัว หรือ quorum ข้าม region ให้ระดับความทนทานต่างกัน Write concern และ fsync policy จึงเป็นส่วนหนึ่งของความหมายคำว่า “สำเร็จ”

BASE ไม่ใช่ข้ออ้างให้ข้อมูลผิดโดยไม่มีเส้นตาย

Eventual consistency กล่าวว่าเมื่อหยุดมี update ใหม่ สำเนาจะ converge ในที่สุดภายใต้สมมุติฐานที่ระบบซ่อมแซมทำงานได้ แต่ไม่ได้บอกว่ากี่วินาที ไม่บอกว่าผู้อ่านหนึ่งคนจะเห็นค่าถอยหลังหรือไม่ และไม่บอกว่าจะรวม concurrent writes แบบใด Application ต้องกำหนด SLO, repair process และ user-visible semantics เพิ่มเติม

แนวทางปฏิบัติเมื่อใช้ Asynchronous Operation
อาจตอบผู้ใช้หลังรับคำขอเข้าคิว โดยแยกสถานะ accepted ออกจาก completed ให้ชัด ไม่ควรแสดงว่า “สำเร็จแล้ว” หากงานยังมีโอกาสล้ม จากนั้นต้องมี idempotency key, retry, dead-letter handling และหน้าสถานะให้ตรวจสอบได้ การซ่อนความหน่วงไม่ควรกลายเป็นการซ่อนความจริง

Eventual Consistency มีหลายระดับประสบการณ์ผู้ใช้

คุณสมบัติเหล่านี้อยู่ระหว่าง eventual แบบอ่อนกับ linearizability แบบเข้ม ระบบอาจให้ session guarantee เพื่อประสบการณ์ที่สมเหตุสมผลโดยไม่ต้องจ่ายค่าความเข้มสูงสุดทุก operation

Compensation แทน Rollback ข้ามบริการ

ใน Microservices การครอบทุกบริการด้วย distributed transaction อาจแพงหรือเปราะ จึงมี Saga ที่แต่ละขั้น commit ใน local transaction และมี compensating action หากขั้นหลังล้ม เช่นคืน stock หรือคืนเงิน Compensation ไม่ใช่การย้อนเวลา เพราะโลกภายนอกอาจเห็นเหตุการณ์ไปแล้ว จึงต้องออกแบบ business semantics ให้รับสถานะระหว่างทางได้

การจองห้องพัก
ระบบอาจรับคำขอจอง ตัดวงเงิน และส่งอีเมลเป็นคนละขั้น หากส่งอีเมลสำเร็จแล้วขั้นตัด stock ล้ม เรา “rollback อีเมล” ไม่ได้ ได้เพียงส่งอีเมลแก้ไขหรือยกเลิก นี่เป็นเหตุผลว่าทำไมการขยาย transaction ข้ามระบบไม่ได้แก้ด้วยคำว่า ACID/BASE เพียงคำเดียว

ACID กับ BASE ไม่ได้ผูกกับ SQL/NoSQL แบบหนึ่งต่อหนึ่ง

ฐานข้อมูล SQL แบบกระจายหลายระบบให้ ACID transactions ส่วน document หรือ wide-column store บางระบบมี transaction หรือ compare-and-set ในขอบเขตที่กำหนด ในทางกลับกัน SQL database ที่ใช้ asynchronous replica ก็อาจให้ stale read ได้ การเลือกควรดู guarantee จริง ไม่ใช่ตัดสินจาก query language

3NoSQL: สี่ประเภทหลัก

NoSQL เดิมสื่อถึงระบบที่ไม่ได้ใช้ relational model/SQL แบบดั้งเดิม ต่อมามักตีความว่า Not Only SQL แต่ไม่ว่าขยายคำอย่างไร NoSQL ไม่ใช่คุณสมบัติด้าน performance หรือ consistency โดยอัตโนมัติ มันเป็นกลุ่มระบบที่ data model และ query model ต่างกันมาก

ประเภทโมเดลข้อมูลเหมาะกับ Access Patternข้อจำกัดที่มักพบ
Key-Value Storeค้น value ด้วย key ระบบอาจไม่เข้าใจโครงสร้างภายในsession, cache, lookup ที่รู้ keyquery ภายใน value หรือ join จำกัด
Wide-Column / Column Familyrow ที่มีคอลัมน์ sparse จัดกลุ่มเป็น family และกระจายด้วย row/partition keyข้อมูลปริมาณมากที่ query ตาม key/range ที่ออกแบบไว้schema ต้องเริ่มจาก query; ad-hoc query ไม่ใช่จุดเด่น
Document Storeเก็บ document เช่น BSON/JSON และ index field ภายในได้aggregate ที่อ่านเขียนร่วมกันและ schema เปลี่ยนตามรุ่นdocument โตไม่จำกัด, cross-document join/transaction มีต้นทุน
Graph Databasenode, edge และ properties เป็นหน่วยหลักtraversal ความสัมพันธ์หลายทอดและ pattern matchingการกระจาย graph ข้ามเครื่องและ supernode อาจยาก

NoSQL ไม่ได้แปลว่า Schemaless

แม้ฐานข้อมูลไม่บังคับ schema แบบตารางสัมพันธ์ Application ยังมี schema อยู่ใน code, validation, index และความคาดหวังของผู้อ่าน หากเอกสารรุ่นเก่ากับรุ่นใหม่มี field ต่างกัน เราต้อง version และ migrate logic การไม่มี DDL เข้มงวดไม่ได้ทำให้ความรับผิดชอบเรื่อง schema หายไป เพียงย้ายจาก database ไป application มากขึ้น

เริ่มออกแบบจาก Query ไม่ใช่เริ่มจาก Entity Diagram อย่างเดียว

ใน relational design เรามัก normalize เพื่อลดความซ้ำแล้วใช้ join ตอนอ่าน ในระบบที่กระจายข้อมูลตาม partition key การ join ข้าม partition แพง จึงอาจ denormalize และสร้างตารางตาม query เช่นตาราง “คำสั่งซื้อตามลูกค้า” กับ “คำสั่งซื้อตามวัน” เก็บข้อมูลซ้ำคนละมุม

เปรียบเหมือนจัดแฟ้มตามคำถาม
ถ้าคนมาขอเอกสารด้วยเลขบัตรเสมอ เราจัดตู้ตามเลขบัตร หากอีกฝ่ายถามตามวันที่ทุกวัน การมีตู้สำเนาเรียงตามวันที่อาจคุ้มกว่ารื้อทุกแฟ้มทุกครั้ง NoSQL schema หลายแบบจึงดูซ้ำ แต่ความซ้ำนั้นซื้อความเร็วในการอ่าน แลกกับภาระทำให้สำเนาสอดคล้องกัน

Polyglot Persistence: ระบบเดียวอาจใช้หลายฐานข้อมูล

ตะกร้าสินค้าอาจใช้ key-value, รายการสั่งซื้อใช้ relational transaction, recommendation ใช้ graph และ event history อยู่ object storage การใช้ฐานข้อมูลเดียวลด operational complexity แต่บังคับ workload ต่างชนิดให้เข้ากับเครื่องมือเดียว การใช้หลายระบบยืดหยุ่นกว่าแต่เพิ่มงานด้าน replication, security, observability และความรู้ของทีม

เกณฑ์เลือกที่ควรถามก่อนชื่อผลิตภัณฑ์

HBase: ตัวอย่าง Column Family ในทางปฏิบัติ

อย่าถูกภาพลวงตาของตาราง
HBase มองเป็น sorted sparse multidimensional map ได้ แถวเดียวไม่จำเป็นต้องมี qualifier เหมือนแถวอื่น แต่ Column Family เป็นส่วนของ physical design และกำหนดตอนสร้าง/แก้ schema ไม่ควรสร้าง family จำนวนมากแบบ field อิสระ เพราะสมาชิกใน family แชร์คุณสมบัติการจัดเก็บหลายอย่าง
// ภาพเชิงแนวคิดของข้อมูล HBase
Table People (Name, Home, Office) {
  101: {
    Name: {First="Florian", Last="Krepsbach"},
    Home: {Phone="555-1212"},
    Office: {Phone="666-1212"}
  }
}
GET People:101:Name:First  →  "Florian"

Cell มี Version ตาม Timestamp

ตำแหน่งหนึ่งอธิบายด้วย row key, column family, qualifier และ version/timestamp HBase เก็บได้หลาย versions ตาม configuration คุณสมบัตินี้ช่วยงาน time-oriented และ rollback บางแบบ แต่เพิ่มพื้นที่และ compaction burden จึงควรกำหนดจำนวน version/TTL ให้ตรงงาน

Region: ช่วงของ Row Key ที่ถูกกระจาย

ตารางถูกแบ่งเป็น Regions ตามช่วง row key แล้ว RegionServer รับผิดชอบแต่ละช่วง เมื่อ region โตจะแตกเป็นช่วงย่อย การเรียงตาม row key ทำให้ scan ช่วงต่อเนื่องมีประสิทธิภาพ แต่ทำให้ key ที่เพิ่มตามเวลาส่ง write ใหม่ไปปลายช่วงเดียวจน hotspot ได้

Row key: จุดที่การออกแบบข้อมูลกลายเป็นการออกแบบการคำนวณ

HBase เรียงข้อมูลตาม row key และแบ่งข้อมูลเป็นช่วง ไม่ได้ hash ทุก key โดยอัตโนมัติ ถ้าใช้ key ที่เพิ่มขึ้นเรื่อย ๆ เช่น timestamp งานเขียนใหม่อาจไหลไปกองอยู่ Region เดียว เกิด hotspot ทั้งที่เครื่องอื่นยังว่าง

Hash Prefix หรือ Salting
เราอาจเติม bucket ที่คำนวณจาก hash ไว้หน้า key เช่น hash(userId)%8 | timestamp | userId เพื่อกระจาย write ไปแปดช่วง วิธีนี้ช่วย write concurrency แต่การอ่านตามช่วงเวลาทั้งหมดต้อง query แปดช่วงแล้ว merge ผล จึงไม่มี row key ที่ดีที่สุดโดยไม่รู้ access pattern

Salting แบบสุ่มกับ Hash Prefix ต่างกันเล็กน้อย

Random salt ช่วยกระจาย hot pattern แต่ถ้าจะอ่าน row เดิมต้องรู้ salt หรือค้นทุก bucket ส่วน deterministic hash prefix คำนวณ bucket จากส่วนของ key ได้ จึง point lookup ง่ายกว่า การเลือกจำนวน buckets น้อยเกินไปยังกระจายไม่พอ มากเกินไปทำให้ fan-out read สูง

Reverse Timestamp

บางงานต้องการรายการล่าสุดก่อน อาจใช้ค่า timestamp กลับทิศภายในส่วน key หลัง prefix เพื่อให้ scan ได้ตามลำดับที่ต้องการ แต่ถ้าเอา timestamp ไว้หน้าสุดก็ยัง hotspot อยู่ เทคนิคหนึ่งแก้ query order ไม่ได้แก้ distribution ทุกด้าน จึงต้องจัดองค์ประกอบ key เป็นลำดับตามความต้องการ

HBase Write Path แบบย่อ

Write ถูกบันทึกใน Write-Ahead Log เพื่อความทนทานและใส่ MemStore ในหน่วยความจำ เมื่อถึงเงื่อนไข MemStore flush เป็น HFile บน storage ต่อมามี compaction รวมไฟล์เพื่อลดจำนวนชิ้นและจัดการข้อมูลเก่า เส้นทางนี้ทำให้ write เป็นลำดับมากกว่าการแก้ตำแหน่งกระจัดกระจาย แต่ต้นทุนถูกเลื่อนไป compaction ภายหลัง

HBase Read Path แบบย่อ

Read อาจตรวจ BlockCache, MemStore และ HFiles หลายชุด Bloom Filter ช่วยตัดไฟล์ที่แน่ใจว่าไม่มี key แต่ถ้า Bloom Filter บอกว่า “อาจมี” ยังต้องตรวจจริง เพราะมี false positive ได้ การมี HFiles มากเกินไปเพิ่ม read amplification จึงต้องให้ compaction ทำงานอย่างสมดุล

ความสัมพันธ์กับ HDFS

HBase ใช้ distributed storage อย่าง HDFS เป็นฐาน แต่เพิ่มการเข้าถึง record แบบสุ่มและระบบจัด region เหนือไฟล์ก้อนใหญ่ HDFS เหมาะกับ streaming file access ส่วน HBase จัด index และ write path สำหรับ row-level access การเข้าใจ HDFS จากบทก่อนจึงช่วยเห็นว่า HBase ไม่ได้แทน HDFS แต่สร้างบริการข้อมูลอีกระดับบนมัน

อย่าใช้ HBase เพราะข้อมูล “ใหญ่” อย่างเดียว
ถ้างานอ่านไฟล์เป็น batch แล้วเขียนผลครั้งเดียว Parquet บน distributed storage อาจง่ายและคุ้มกว่า HBase เหมาะเมื่อจำเป็นต้อง point lookup หรือ range access ตาม row key ด้วย latency ที่ต่างจาก batch scan การเลือกจาก Volume เพียง V เดียวมักพาไปผิดระบบ

4Cassandra: กรณีศึกษาระบบ Wide-Column แบบกระจาย

คำว่า “AP แบบเต็มรูปแบบ” ในหัวข้อเดิมทำให้ดูเหมือน Cassandra เลือก Availability โดยไม่สน Consistency ความจริง Cassandra ให้ผู้ใช้เลือก consistency level ต่อคำขอ มีทั้ง ONE, QUORUM, ALL และระดับเฉพาะ datacenter อีกทั้งมี Lightweight Transaction ที่ใช้ Paxos สำหรับ compare-and-set แบบ linearizable ในขอบเขตที่กำหนด จึงควรมองว่าเป็นระบบที่ tunable มากกว่าป้าย AP คำเดียว

Data Model ปัจจุบัน
Cassandra เป็น distributed wide-column database ที่ออกแบบ table จาก query โดย Primary Key แบ่งเป็น Partition Key และ Clustering Columns; Partition Key กำหนดการกระจายข้อมูล ส่วน Clustering Columns กำหนดลำดับภายใน partition

Primary Key มีสองหน้าที่ในบรรทัดเดียว

CREATE TABLE events_by_user (
  user_id text,
  event_time timestamp,
  event_type text,
  payload text,
  PRIMARY KEY ((user_id), event_time)
) WITH CLUSTERING ORDER BY (event_time DESC);

user_id เป็น Partition Key ทำให้ event ของผู้ใช้เดียวกันอยู่ partition เดียว ส่วน event_time เป็น Clustering Column ทำให้ query รายการล่าสุดของผู้ใช้ได้ตามลำดับ แต่ถ้าผู้ใช้คนหนึ่งมี events ไม่จำกัด partition จะโตเกินไป อาจต้อง bucket ตามเดือน เช่น ((user_id, month), event_time)

Partition เล็กเกินไปกับใหญ่เกินไปก็มีปัญหา

การออกแบบผลดีปัญหา
Partition เล็กมากจำนวนมหาศาลกระจายได้ละเอียดmetadata, index และ request overhead สูง
Partition พอดีกับ queryอ่านต่อเนื่องในโหนดไม่กี่ตัวต้องคาด access pattern ล่วงหน้า
Partition ใหญ่มากquery ช่วงกว้างใน partition เดียวhot partition, compaction/read/repair หนัก

ไม่มี Master สำหรับ Data Path แต่ไม่ได้แปลว่าไม่มี Coordination

Client ส่งคำขอไปยังโหนดใดก็ได้ โหนดนั้นทำหน้าที่ coordinator ของคำขอนั้น คำนวณว่า replica ใดรับผิดชอบ partition และรอคำตอบตาม consistency level ไม่มี master กลางที่รับทุก read/write แต่ยังมี gossip, failure detector, schema coordination และ repair process หลายชนิด

กลไกรายละเอียด
PartitioningPartition Key ถูก hash เป็น token แล้ว token ranges ถูกกระจายให้โหนด/virtual nodes รับผิดชอบ
Gossipโหนดแลกข้อมูลสถานะคลัสเตอร์แบบ peer-to-peer เป็นระยะ แต่ repair ข้อมูล replica เป็นกระบวนการอีกส่วน ไม่ควรเรียกทุกอย่างรวมว่า Gossip
Failure Detectionใช้ค่า Φ (phi) accrual failure detector — ค่า suspicion เพิ่มขึ้นตามเวลาถ้าโหนดไม่ตอบสนอง แทนที่จะตัดสิน "ตายหรือไม่ตาย" แบบ binary ทันที
Write Pathเขียน Commit Log และ Memtable ก่อน จากนั้น flush เป็น SSTable ซึ่ง immutable แล้วใช้ Compaction จัดการไฟล์ภายหลัง
Read Pathรวมข้อมูลจาก Memtable และ SSTables ที่เกี่ยวข้อง ใช้ Bloom Filter, partition index, cache และ timestamp/tombstone เพื่อ reconcile

แก่นของ Cassandra โดยไม่ต้องจำรายละเอียดทั้งระบบ

Cassandra นำ partition key ไปผ่าน hash เพื่อหา token แล้วกระจายข้อมูลไปยังโหนดต่าง ๆ เปรียบเหมือนการแบ่งถนนวงแหวนให้หลายสาขารับผิดชอบ เมื่อเพิ่มหรือลดสาขา ข้อมูลที่ต้องย้ายมีเพียงบางช่วง ไม่ต้องสับไพ่ทั้งสำรับใหม่ นี่คือแนวคิดของ consistent hashing

Virtual Nodes ช่วยกระจาย Token Ranges

แทนให้โหนดหนึ่งรับช่วงต่อเนื่องเพียงช่วงเดียว Virtual Nodes ทำให้โหนดรับผิดชอบหลาย token ranges กระจายบนวงแหวน ช่วย balance และย้ายข้อมูลเมื่อเพิ่ม/ลบโหนดได้ละเอียดขึ้น แต่จำนวน vnodes และ topology ยังต้องวางแผนตามรุ่นและ workload ไม่ใช่ค่ามากที่สุดเสมอไป

Replication Factor บอกจำนวนสำเนา

ถ้า RF=3 partition หนึ่งมี replicas สามชุดตาม strategy ที่กำหนด การวางข้าม rack/datacenter ช่วยทน failure domain แต่เพิ่ม latency และ bandwidth หาก write ต้องรอ replica ระยะไกล จึงมี consistency levels เช่น LOCAL_QUORUM เพื่อให้ quorum ภายใน datacenter

Tunable Consistency: R + W > RF

ในแบบจำลองง่าย หากจำนวน replica ที่รออ่าน (R) บวกจำนวนที่รอเขียน (W) มากกว่า Replication Factor ชุดอ่านกับชุดเขียนต้อง overlap อย่างน้อยหนึ่ง replica เช่น RF=3, QUORUM read=2 และ QUORUM write=2

R + W > RF

เงื่อนไข overlap ช่วยเรื่อง freshness ภายใต้สมมุติฐานของระบบ แต่ไม่ควรถูกยกเป็นคำรับประกัน transaction ทุกชนิด Clock skew, concurrent writes, failed writes, tombstones และ consistency level ข้าม datacenter ยังมีรายละเอียด การอ่าน QUORUM ไม่เท่ากับ serializable transaction โดยอัตโนมัติ

Write Path: เร็วตอนเขียน เพราะเลื่อนงานบางส่วนไปทีหลัง

  1. Coordinator ส่ง mutation ไป replicas ตาม topology
  2. Replica บันทึก Commit Log เพื่อ recovery
  3. อัปเดต Memtable ที่จัดลำดับใน memory
  4. เมื่อถึงเกณฑ์ Memtable flush เป็น SSTable ซึ่งไม่แก้ทับ
  5. Compaction รวม SSTables, กำจัดข้อมูลเก่า และจัดการ tombstone ตามเงื่อนไข

การเขียนแบบ append/immutable SSTable ลด random write แต่ไม่ได้ลบงาน มันย้ายงานไป compaction ภายหลัง หาก write rate สูงเกิน compaction capacity จะเกิด backlog, read amplification และพื้นที่ชั่วคราวสูง นี่เชื่อมกับแนวคิด Immutability ในบท 5: ลด write lock ได้ แต่ต้องจ่ายด้วย version และการเก็บกวาด

Read Path: ต้องรวมความจริงจากหลายชั้น

ข้อมูลรุ่นล่าสุดอาจอยู่ Memtable รุ่นเก่าอยู่ใน SSTables หลายไฟล์ และบางค่าถูกลบด้วย tombstone Read จึงต้องค้น candidate files, อ่านข้อมูลและ reconcile ตาม key/timestamp Bloom Filter ช่วยตอบว่าไฟล์ “ไม่มีแน่นอน” ได้ แต่คำตอบว่า “อาจมี” ยังต้องเปิดดู

Bloom Filter ไม่คืน Record และมี False Positive

Bloom Filter เป็น probabilistic membership structure ถ้าตอบว่าไม่มี เราตัดไฟล์ได้อย่างปลอดภัย ถ้าตอบว่าอาจมี อาจเป็น false positive แต่ไม่มี false negative ภายใต้การใช้งานถูกต้อง การเพิ่ม bit ต่อ key ลด false positive แลกกับ memory

เปรียบเหมือนเจ้าหน้าที่หน้าห้องเอกสาร
เจ้าหน้าที่บอกได้แน่ว่า “แฟ้มนี้ไม่มีชื่อคุณ” ทำให้ไม่ต้องเปิดค้น แต่ถ้าบอกว่า “น่าจะมี” เรายังต้องเปิดแฟ้มจริง เขาไม่ได้ส่งเอกสารให้เรา เพียงช่วยตัดแฟ้มที่ไม่ต้องค้นออก

Tombstone: การลบที่ต้องเดินทางไปทุกสำเนา

ในระบบที่มี SSTable immutable และ replica อาจ offline เราไม่สามารถลบ byte เก่าทันทีแล้วจบ จึงเขียน marker ว่าข้อมูลถูกลบ เรียก tombstone เพื่อป้องกันค่ารุ่นเก่าฟื้นกลับจาก replica ที่ยังไม่เห็นการลบ Tombstone มากเกินไปทำให้ read ต้องสแกนและ compaction หนัก การใช้ TTL หรือ delete จำนวนมากจึงต้องออกแบบ

Hinted Handoff, Read Repair และ Anti-Entropy Repair

กลไกทำงานเมื่อใดข้อจำกัด
Hinted Handoffเก็บ hint ชั่วคราวเมื่อ replica ปลายทางไม่พร้อม แล้วส่งภายหลังเป็น best effort และไม่แทน full repair
Read Repairเมื่อ read ที่เกี่ยวข้องพบ replicas ไม่ตรงกัน อาจ reconcile/repair ตาม settingซ่อมเฉพาะข้อมูลและ replicas ที่ read แตะ ไม่ได้สแกนทั้งคลัสเตอร์
Anti-Entropy Repairกระบวนการดูแลเชิงรุกเปรียบเทียบและซ่อมช่วงข้อมูลใช้ I/O/network และต้องมี operational schedule

Timestamp และ Last-Write-Wins

Cassandra ใช้ timestamp เพื่อ reconcile cell versions หลายกรณี ทำให้ clock discipline สำคัญ Last-write-wins ใช้ง่ายแต่ concurrent update หนึ่งอาจหายจากมุมมอง application หาก timestamp ชนะ ไม่ได้ merge ความหมายทางธุรกิจให้ การนับยอดด้วย read-modify-write จึงเสี่ยง lost update และควรใช้ data type/pattern ที่เหมาะ

Lightweight Transactions ไม่ใช่ Transaction ทั่วไปที่ฟรี

คำสั่งเงื่อนไข เช่น IF NOT EXISTS ใช้ consensus/Paxos เพื่อ linearizable compare-and-set มี round trips และ latency สูงกว่า write ปกติ ควรใช้เมื่อ invariant ต้องการจริง ไม่ควรเปลี่ยนทุก write ให้เป็น LWT เพราะต้องการ “ความปลอดภัยเผื่อไว้”

Query-First Modeling และ Denormalization

Cassandra ไม่เหมาะกับการสร้าง table กลางหนึ่งชุดแล้วค่อย query ได้ทุกแบบ มักสร้าง table ต่อ access pattern เช่น events_by_user และ events_by_day Application หรือ pipeline ต้องเขียนข้อมูลซ้ำหลายตาราง การอ่านเร็วขึ้น แต่ write path และ consistency ระหว่าง views ซับซ้อนขึ้น

สิ่งที่ควรเข้าใจ
Cassandra ไม่ได้เร็วเพราะมีคำว่า NoSQL แต่เพราะออกแบบ write path แบบ log-structured, กระจาย partition ด้วย hash, ทำสำเนาข้าม failure domains และให้เลือกระดับคำตอบที่ต้องรอ ต้นทุนที่ตามมาคือ Compaction, Repair, Tombstone, Denormalization และการออกแบบ Partition Key อย่างระมัดระวัง
ทำไมไม่ควรใช้ Benchmark เก่าเป็นคำตัดสิน
MySQLCassandra
Write เฉลี่ย~300 ms0.12 ms
Read เฉลี่ย~350 ms15 ms
ตัวเลขจากเอกสารประวัติศาสตร์ช่วยให้เห็นแรงผลักดันในยุคแรก แต่ไม่ควรนำไปสรุปว่า Cassandra เร็วกว่า MySQL หลายพันเท่าสำหรับทุกงาน หากไม่รู้ hardware, durability setting, query, dataset, concurrency, index และวิธีวัด การเปรียบเทียบที่ยุติธรรมต้องใช้ workload และ guarantee ที่ใกล้กันบนระบบปัจจุบัน

5เมื่อยุคเปลี่ยน: CAP และ NoSQL ในปี 2026

แล้ว · ~2015-2018
ตอนนี้ · 2026
CAP ถูกมองเป็นกฎเหล็กที่ต้องเลือก 2 ใน 3 อย่างเด็ดขาด
วงการเริ่มพูดถึง PACELC theorem มากขึ้น — ขยาย CAP ว่า "ถ้าเกิด Partition ต้องเลือก A หรือ C, แต่ถ้าไม่มี Partition (Else) ก็ยังต้องเลือกระหว่าง Latency กับ Consistency อยู่ดี" สะท้อนว่าการแลกเปลี่ยนไม่ได้เกิดแค่ตอน partition เท่านั้น
ต้องเลือกระหว่างฐานข้อมูล SQL (ACID) กับ NoSQL (BASE) แบบขาดจากกัน
Distributed SQL ให้ relational model และ distributed transaction บนหลายโหนดได้มากขึ้น แต่ยังจ่ายด้วย consensus, coordination, latency ข้าม region และ operational complexity ไม่ได้ทำให้ CAP หรือ Network Delay หายไป
ระบบถูกจัดเป็น CP/AP จากชื่อผลิตภัณฑ์และใช้ป้ายนี้ตัดสินใจ
ระบบสมัยใหม่มีหลาย consistency levels, transaction scopes และ deployment modes การประเมินจึงลงมาที่ operation, concern/level, topology และ failure domain มากขึ้น

Consensus ไม่ได้ทำให้ได้ C+A ระหว่าง Partition

Raft หรือ Paxos ช่วยให้กลุ่มโหนดตกลงลำดับและเลือกค่าที่ปลอดภัยเมื่อยังมี quorum แต่ฝั่งที่ไม่ถึง quorum ต้องหยุดรับ operation บางชนิด นี่คือการรักษา Consistency ด้วยการยอมลด Availability ของฝั่งเสียงข้างน้อย ไม่ใช่ทางลัดผ่าน CAP

Multi-Region ทำให้ Latency กลายเป็นส่วนของ Semantics

หาก transaction ต้องรอ quorum ข้ามทวีป ความเร็วแสงและ network path สร้าง latency ขั้นต่ำ การวาง leader ใกล้ผู้ใช้บางกลุ่มช่วยกลุ่มนั้นแต่ทำให้อีกกลุ่มไกลขึ้น บางระบบให้ follower read ที่เร็วแต่ stale บางระบบให้ bounded staleness หรือ exact timestamp read ผู้ใช้จึงเลือกความสดกับ latency ตาม operation

Serverless/Managed ไม่ได้ตัด Trade-off ออก เพียงย้ายผู้ดูแล

บริการ managed ลดภาระติดตั้ง patch, backup และ scaling บางส่วน แต่ schema, hot key, consistency setting, cost model และ failure semantics ยังเป็นหน้าที่ของผู้ออกแบบ Application การกดเลือก “multi-region” ไม่ได้ตอบว่า concurrent writes จะรวมอย่างไร

CRDT: ออกแบบ State ให้ Merge ได้

Conflict-free Replicated Data Type ใช้โครงสร้างทาง Algebra ทำให้ replica merge concurrent updates แล้ว converge โดยไม่ต้องเลือกว่า write ใดหาย เช่น grow-only counter หรือ set บางชนิด แต่ CRDT ไม่ได้ใช้ได้กับ invariant ทุกแบบ การถอนเงินจากยอดจำกัดยังต้อง coordination หรือแบ่งสิทธิ์อย่างระมัดระวัง

จุดเชื่อมกับบท Functional Programming คือ merge operation ต้องมีคุณสมบัติ เช่น associative, commutative และ idempotent ตามแบบของ state-based CRDT หลายชนิด โครงสร้างทางคณิตศาสตร์จึงช่วยลด coordination ได้จริงเมื่อความหมายของข้อมูลรองรับ

Consistency เป็น Spectrum ไม่ใช่สวิตช์

ระหว่าง Eventual กับ Linearizable มี Sequential, Causal, Session Guarantees, Bounded Staleness และ Snapshot semantics หลายแบบ การบอกว่า “Strong” หรือ “Weak” โดยไม่ระบุ model มักไม่พอสำหรับวิเคราะห์ anomaly

6กรอบตัดสินใจเลือกฐานข้อมูลจาก Workload

การเริ่มจากรายชื่อผลิตภัณฑ์ทำให้เราเถียงเรื่องคุณสมบัติที่ยังไม่รู้ว่าจำเป็นหรือไม่ วิธีที่เป็นระบบกว่าคือเริ่มจากข้อมูล Operation และ Failure แล้วค่อยหา engine ที่ให้ guarantee นั้นด้วยต้นทุนที่ทีมรับได้

ขั้นที่ 1: เขียน Invariant ก่อน Schema

Invariant คือกฎที่ห้ามละเมิด เช่น coupon ใช้ได้ครั้งเดียว ยอดรวม debit เท่ากับ credit หรือ seat หนึ่งที่นั่งขายได้คนเดียว หาก invariant ข้ามหลาย records/partitions เราต้องรู้ว่าระบบรองรับ transaction ขอบเขตนั้นหรือ Application จะชดเชยอย่างไร

ขั้นที่ 2: ระบุ Access Pattern

คำถามผลต่อการออกแบบ
อ่านด้วย key หรือ scan ช่วงกำหนด partition/shard key และ index
อ่านหลายความสัมพันธ์อาจต้อง graph traversal, join หรือ denormalized views
write ต่อ key สูงหรือไม่ตรวจ hot key และ contention
query ad hoc หรือคงที่Relational/columnar engine อาจเหมาะกับ ad hoc กว่า query-first store
ข้อมูลโตตามเวลาต้อง bucket, TTL, archive และ compaction policy

ขั้นที่ 3: เขียน Anomaly Budget

แทนถามว่า “ต้อง Strong Consistency ไหม” ให้ระบุว่ายอมรับอะไรได้ เช่นอ่านช้าไม่เกิน 5 วินาที, ห้าม lost update, ยอม duplicate event แต่ consumer ต้อง idempotent, ผู้ใช้ต้อง read-your-writes การเขียนข้อกำหนดเป็นพฤติกรรมช่วยเลือก model ได้ตรงกว่า adjective

ขั้นที่ 4: ระบุ Failure Domain

ต้องทน process crash, node crash, rack outage, availability zone หรือ region outage? Replication Factor สามชุดใน rack เดียวไม่ได้ทน rack failure ส่วนสาม regions เพิ่มความทนทานแต่มี cost และ latency สูงขึ้น ความทนทานต้องวางสำเนาข้ามขอบที่ต้องการจริง

ขั้นที่ 5: ดู Operational Burden

ระบบที่ benchmark เร็วแต่ต้อง repair, compact และ tune ซับซ้อนอาจไม่คุ้มกับทีมเล็ก ต้องประเมิน backup/restore, schema evolution, monitoring, upgrade, incident response และความสามารถของทีม Database ที่ดีบนกระดาษอาจเป็นระบบที่แย่หากไม่มีใครดูแลเป็น

ขั้นที่ 6: Benchmark Guarantee เดียวกัน

อย่าเปรียบเทียบ write ที่รอ fsync+quorum กับ write ที่ตอบหลังใส่ memory ของ replica เดียว แล้วสรุปว่าตัวหลังเร็วกว่าร้อยเท่า ควรตั้ง durability, consistency, payload, concurrency และ failure behavior ให้ใกล้กัน รวม p95/p99 latency ไม่ใช่ดู average อย่างเดียว

ตัวอย่าง Mapping แบบไม่ผูกชื่อผลิตภัณฑ์

Workloadสิ่งที่สำคัญโมเดลที่ควรพิจารณา
Ledger การเงินInvariant, audit, serializable transactionRelational/Distributed SQL ที่ guarantee ชัด
Session CacheKey lookup, TTL, latency ต่ำKey-value store
IoT Events ตาม Device/เวลาwrite สูง, range ภายใน device bucketWide-column หรือ time-series store
Product Catalogdocument fields ต่างตามประเภท, index หลาย fieldDocument store หรือ relational JSON ตาม query
Fraud Relationshipเดินความสัมพันธ์หลายทอดGraph database/graph processing
Analytics ระยะยาวscan คอลัมน์, aggregate ปริมาณมากColumnar files/warehouse/lakehouse
ถ้า Relational Database ธรรมดารองรับได้ ก็ไม่ต้องหนีเพราะคำว่า Big Data
การเพิ่มระบบใหม่สร้างต้นทุนด้านทีมและข้อมูลหลายสำเนา Scale ที่คาดว่าจะเกิดในอีกห้าปีไม่ควรบังคับให้รับ complexity วันนี้โดยไม่มีหลักฐาน เริ่มจากระบบที่ง่ายที่สุดซึ่งตอบ SLO และ invariant ได้ แล้ววัดก่อนขยาย

7กรณีศึกษาเชิงความคิด

กรณีที่ 1: จำนวน Like

จำนวน Like อาจยอมให้แสดงคลาดเล็กน้อยและ converge ภายหลังได้ แต่การกด Like ของผู้ใช้เองควร read-your-writes ไม่เช่นนั้นผู้ใช้กดแล้วหัวใจดับทันทีจนคิดว่าระบบเสีย เราอาจใช้ counter ที่ merge ได้และ session guarantee ที่ขอบหน้าจอ

กรณีที่ 2: Stock สินค้าชิ้นสุดท้าย

ถ้าสินค้าเหลือหนึ่งชิ้นและสอง region ขายพร้อมกัน การตอบทั้งคู่รักษา Availability แต่ละเมิด invariant การแก้อาจใช้ leader/quorum, reservation token, escrow แบ่ง stock ต่อ region หรือยอม oversell แล้ว compensate แต่ละวิธีเปลี่ยน business semantics และประสบการณ์ลูกค้า

กรณีที่ 3: Timeline ของ Social Media

Timeline อาจสร้างแบบ fan-out on write หรือ fan-out on read คนดังมีผู้ติดตามมหาศาลทำให้ fan-out on write เกิด hot workload ระบบอาจผสมสองวิธี ยอมให้โพสต์ปรากฏช้าบางส่วน และจัดอันดับจากหลาย source Consistency ที่ต้องการจึงไม่ใช่ “ทุก timeline เหมือนกันทันที”

กรณีที่ 4: Profile กับ Permission

ชื่อเล่นหรือรูป profile stale ไม่กี่วินาทีอาจยอมรับได้ แต่ permission ที่ถูกถอนแล้ว stale อาจเปิดข้อมูลลับ ระบบเดียวกันจึงใช้ cache policy และ consistency ต่างกันตาม field/operation ไม่ควรใช้กฎเดียวกับทั้ง document เพราะจัดเก็บร่วมกัน

กรณีที่ 5: Event Processing อย่างน้อยหนึ่งครั้ง

Broker อาจส่ง event ซ้ำเมื่อไม่แน่ใจว่า consumer ทำสำเร็จหรือยัง หาก handler ใช้ idempotency key การรับซ้ำไม่สร้างผลซ้ำ นี่เป็นตัวอย่างที่ Application design ช่วยอยู่กับ Availability โดยไม่เรียกร้อง exactly-once จากทุกชั้น แต่ต้องกำหนด key และขอบ deduplication ให้ถูก

กรณีที่ 6: DNS

DNS ใช้ caching และ TTL ทำให้ข้อมูลเปลี่ยนแล้วไม่ได้กระจายทันที แต่ให้ระบบทนและตอบได้กว้าง การลด TTL ช่วยให้เปลี่ยนเร็วขึ้นแต่เพิ่ม query load การเพิ่ม TTL ลด load แต่ stale นานขึ้น เป็นตัวอย่าง Latency/Availability/Freshness trade-off ที่ผู้ใช้อินเทอร์เน็ตพบทุกวัน

8สรุปและขั้นตอนถัดไป

CAP ไม่ได้สอนว่าโลกมีฐานข้อมูลสามกล่องให้เลือก แต่สอนว่าขณะเครือข่ายแบ่ง เราไม่สามารถรับทุก operation ต่อและรักษา single-copy semantics พร้อมกันได้ การตัดสินใจจึงต้องลงไปถึง operation และ invariant

ACID กับ BASE ไม่ใช่คู่ตรงข้ามแบบ SQL กับ NoSQL ACID อธิบาย transaction properties ส่วน BASE ชี้แนวทางที่ยอมให้ state เปลี่ยนและ converge ภายหลัง ระบบสมัยใหม่มักผสม guarantee หลายระดับตามขอบข้อมูล

สิ่งที่ควรติดตัวไปมีดังนี้:

บทถัดไปจะพูดถึง Variety และ Data Integration เมื่อข้อมูลมาจากหลายระบบ ปัญหาไม่ได้มีเพียงสำเนาไม่ตรงกัน แต่ schema, หน่วย, ความหมาย และคุณภาพก็อาจไม่ตรงกันด้วย เราจะขยับจาก “หลายสำเนาของข้อมูลเดียวกัน” ไปสู่ “ข้อมูลหลายชนิดที่ดูเหมือนพูดเรื่องเดียวกัน แต่ใช้ภาษาคนละแบบ”

คำถามซ้อมสอบ
  1. แก้ประโยค “CAP เลือกได้เพียงสองในสาม” ให้แม่นยำขึ้น และอธิบายว่าทำไม Partition Tolerance ไม่ใช่ช่องที่ระบบกระจายจะไม่เลือก
  2. อธิบายว่าทำไมการตอบ error ทุกคำขอจึงไม่นับเป็น Availability ตามความหมายของ CAP
  3. เปรียบเทียบ ACID Consistency กับ CAP Consistency พร้อมยกตัวอย่างระบบที่มีอย่างหนึ่งแต่ไม่ได้หมายความว่ามีอีกอย่างโดยอัตโนมัติ
  4. อธิบาย Isolation Levels และยกตัวอย่าง Write Skew ที่ Snapshot Isolation อาจยอมให้เกิด
  5. Eventual Consistency ไม่ได้บอกอะไรบ้างเกี่ยวกับเวลา ลำดับ และการแก้ Concurrent Writes
  6. อธิบายว่า Row Key แบบเพิ่มตามเวลาทำให้ HBase เกิด Hotspot ได้อย่างไร และ Hash Prefix แลก Write Distribution กับ Range Scan อย่างไร
  7. เปรียบเทียบ HBase Region กับ Cassandra Partition ว่าทั้งคู่ใช้ key กำหนดตำแหน่งข้อมูลต่างกันอย่างไร
  8. ออกแบบ Cassandra Primary Key สำหรับ Event ตามผู้ใช้และเวลา พร้อมอธิบายว่าทำไมอาจต้องทำ Time Bucket
  9. อธิบาย Cassandra Write Path ตั้งแต่ Commit Log, Memtable, SSTable ถึง Compaction และชี้ว่าต้นทุนไม่ได้หายไปแต่ถูกย้ายไปไหน
  10. ใช้ RF=3 อธิบาย R + W > RF พร้อมบอกว่าทำไม Quorum Read/Write จึงยังไม่เท่ากับ Serializable Transaction
  11. เปรียบเทียบ Hinted Handoff, Read Repair และ Anti-Entropy Repair ว่าช่วยให้ Replica กลับมาตรงกันคนละช่วงอย่างไร
  12. เลือกฐานข้อมูลให้กรณี Stock ชิ้นสุดท้าย โดยเริ่มจาก Invariant, Access Pattern, Anomaly Budget และ Failure Domain แทนการเริ่มจากชื่อผลิตภัณฑ์