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

พื้นฐานที่ควรรู้ก่อนเรียน Big Data

Big Data ไม่ได้สร้างกฎของคอมพิวเตอร์ขึ้นมาใหม่ แต่ขยายปัญหาเดิมจนต้นทุนที่เคยมองข้ามกลายเป็นคอขวด บทนี้จึงไม่ได้ทบทวนทุกวิชาตั้งแต่ต้น แต่เลือกพื้นฐานที่ต้องหยิบกลับมาใช้เมื่อต้องจัดการข้อมูลขนาดใหญ่จริง ๆ

📚
เรียบเรียงใหม่จากเอกสาร “บทที่ 0: เรื่องที่ควรตระหนักรู้ก่อนมาเรียน Big Data” ของผู้สอน โดยเชื่อม Computer Architecture, Data Structures, Algorithms, Networks, Databases, Operating Systems และ Distributed Systems ให้เป็นแผนที่พื้นฐานชุดเดียว
บทนี้ไม่ใช่ด่านตรวจ prerequisite

ถ้าพื้นฐานบางเรื่องยังไม่แน่น ไม่จำเป็นต้องหยุดอ่านทั้งหมด ให้ใช้บทนี้เป็นแผนที่ เมื่อบทต่อไปพูดถึง cache, hash, partition, Join, lock หรือ network cost แล้วรู้สึกว่าภาพไม่ชัด ค่อยย้อนกลับมายังส่วนที่เกี่ยวข้อง เป้าหมายคือเชื่อมความรู้ ไม่ใช่ทดสอบว่าใครจำรายวิชาเดิมได้ครบกว่าใคร

1สถาปัตยกรรมคอมพิวเตอร์: ทำไม Big Data ถึงแคร์เรื่อง Cache

เมื่อเราพูดว่า CPU เร็วขึ้น มักนึกถึงจำนวนคำสั่งที่ประมวลผลได้ต่อวินาที แต่ในงานข้อมูล CPU จำนวนมากไม่ได้ติดอยู่ที่การบวกหรือเปรียบเทียบ มันกำลังรอข้อมูลเดินทางมาถึงจาก cache, RAM, SSD, ดิสก์ หรือเครื่องอื่น

Memory Hierarchy
หน่วยความจำแต่ละระดับแลกความเร็วกับความจุและราคา Register กับ cache มีขนาดเล็กแต่เร็ว RAM ใหญ่ขึ้นแต่ช้าลง Storage เก็บได้มากกว่าแต่มี latency สูงขึ้น ส่วนข้อมูลบนเครื่องอื่นต้องผ่านทั้ง software stack และเครือข่าย การออกแบบ Big Data จึงเป็นการจัดวางข้อมูลให้การคำนวณพบข้อมูลในระดับที่ใกล้และต่อเนื่องที่สุดเท่าที่ทำได้
ระดับจุดเด่นสิ่งที่ต้องระวัง
Register / CPU Cacheตอบสนองเร็วที่สุด เหมาะกับ working set ขนาดเล็กและการเข้าถึงซ้ำความจุน้อย หากเข้าถึงข้อมูลกระจัดกระจาย cache line ที่โหลดมาอาจถูกใช้เพียงส่วนน้อย
RAMเก็บโครงสร้างข้อมูลและผลระหว่างทางได้มากกว่า cacheMemory bandwidth มีเพดาน หลาย core อาจแย่งช่องทางเดียวกันจนเพิ่ม core แล้วไม่เร็วขึ้น
SSD / HDDเก็บข้อมูลถาวรและมีความจุสูงการอ่านแบบสุ่มมี overhead สูงกว่าการอ่านต่อเนื่อง โดยเฉพาะ HDD
Remote Memory / Storageช่วยแบ่งข้อมูลและขยายความจุออกไปหลายเครื่องต้องจ่ายค่า serialization, network latency, bandwidth และความล้มเหลวระหว่างทาง

Latency กับ Bandwidth เป็นคนละเรื่อง

Latency คือเวลารอก่อนข้อมูลเริ่มมาถึง ส่วน Bandwidth คือปริมาณข้อมูลที่ส่งได้ต่อหน่วยเวลา การส่งข้อมูลชิ้นเล็กจำนวนมากมักแพ้ latency ซ้ำ ๆ ขณะที่การอ่านข้อมูลก้อนใหญ่มักติด bandwidth

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

Spatial และ Temporal Locality

Spatial locality หมายถึงเมื่ออ่านข้อมูลตำแหน่งหนึ่ง เรามีแนวโน้มใช้ข้อมูลข้างเคียงต่อ การเก็บข้อมูลต่อเนื่องจึงใช้ cache line และการอ่าน block ได้คุ้มกว่า ส่วน temporal locality หมายถึงข้อมูลที่เพิ่งใช้มีโอกาสถูกใช้อีก การ cache จึงช่วยได้เมื่อการใช้ซ้ำเกิดก่อนข้อมูลถูกแทนที่

Array มักมี spatial locality ดีกว่าโครงสร้างที่แต่ละ node กระจายอยู่ทั่ว heap แต่ไม่ได้หมายความว่า array ชนะทุกงาน หากต้องแทรกกลางข้อมูลบ่อยหรือไม่รู้ขนาดล่วงหน้า โครงสร้างอื่นอาจเหมาะกว่า ประเด็นคือเลือก layout จาก access pattern ไม่ใช่เลือกจาก Big-O เพียงช่องเดียวในตาราง

Row-oriented กับ Column-oriented Layout

ถ้างานอ่านข้อมูลลูกค้าหนึ่งคนครบทุกคอลัมน์ การวางข้อมูลเป็นแถวช่วยให้ค่าที่ต้องใช้มาอยู่ใกล้กัน แต่ถ้างานหาค่าเฉลี่ยรายได้ของลูกค้าทุกคน การอ่านเฉพาะคอลัมน์รายได้ช่วยลดข้อมูลที่ต้องดึงอย่างมาก Columnar format เช่น Parquet จึงเหมาะกับ analytical workload และเปิดทางให้ compression กับ vectorized execution ทำงานได้ดี

อ่านแฟ้มกับอ่านบัญชีรวม
งานบริการลูกค้าเหมือนหยิบแฟ้มของคนหนึ่งแล้วอ่านทุกช่อง ส่วนงานวิเคราะห์เหมือนดึงเฉพาะช่อง “รายได้” จากแฟ้มทุกคน การจัดตู้แบบเดียวไม่สะดวกที่สุดสำหรับทั้งสองงาน นี่คือเหตุผลที่ระบบ operational กับ analytical มักใช้รูปแบบจัดเก็บต่างกัน

Pipeline, Superscalar, และ Out-of-Order Execution

Pipeline แบ่งการทำคำสั่งเป็นช่วง เช่น fetch, decode และ execute เพื่อให้คำสั่งหลายตัวอยู่คนละช่วงพร้อมกัน Superscalar ทำให้ CPU ออกคำสั่งได้มากกว่าหนึ่งคำสั่งต่อรอบ และ Out-of-order execution อนุญาตให้คำสั่งที่พร้อมข้ามคำสั่งซึ่งกำลังรอข้อมูล ตราบใดที่ผลลัพธ์สุดท้ายยังเทียบเท่าลำดับเดิม

ความสามารถเหล่านี้ช่วยซ่อน latency ได้บางส่วน แต่มีขอบเขต หากคำสั่งทุกตัวรอ memory หรือมี dependency ต่อกันยาว CPU ก็ไม่มีงานอิสระพอให้สลับขึ้นมาทำ การออกแบบ data layout และ algorithm จึงยังสำคัญ แม้ฮาร์ดแวร์จะฉลาดขึ้นมาก

Vectorization และ Branch Prediction

SIMD instruction ประมวลผลค่าหลายตัวด้วยคำสั่งเดียว เหมาะกับข้อมูลชนิดเดียวกันที่วางต่อเนื่อง เช่น บวก vector หรือกรองคอลัมน์ แต่ถ้าแต่ละ record แยกไปคนละ branch CPU อาจทำนายทางผิดและต้องทิ้งงานใน pipeline ระบบ analytical สมัยใหม่จึงพยายามประมวลผลข้อมูลเป็น batch และใช้รูปแบบที่เอื้อต่อ vectorization

Immutability, Lazy Evaluation และ Cache เป็นคนละแนวคิด

สิ่งที่มักอธิบายปนกัน
Immutability หมายถึงข้อมูลเดิมไม่ถูกแก้ ทำให้ติดตาม dependency และคำนวณใหม่ได้ง่ายขึ้น · Lazy evaluation หมายถึงยังไม่คำนวณจนกว่าจะต้องใช้ผล ทำให้ระบบเห็นสายงานทั้งหมดก่อนวางแผน · Caching หมายถึงเก็บผลที่คาดว่าจะใช้ซ้ำ ทั้งสามเรื่องทำงานร่วมกันได้ แต่เรื่องหนึ่งไม่ได้เกิดขึ้นเพราะอีกเรื่องโดยอัตโนมัติ

RDD ของ Spark เป็น immutable และเก็บ lineage จึงสร้าง partition ที่หายขึ้นใหม่ได้ Transformation เป็น lazy ทำให้ Spark รวมงานเป็น stage ก่อนรัน ส่วน cache() เป็นการตัดสินใจเก็บผลไว้ใช้ซ้ำ ผู้เรียนควรแยกสามชั้นนี้ออกจากกัน เพื่ออธิบายได้ว่าความเร็วหรือการกู้คืนเกิดจากกลไกใด

แม้เป็นคนละแนวคิด แต่เชื่อมต่อกันเป็นสายเหตุผลเดียวได้

จุดเชื่อมสำคัญอยู่ที่ dependency หากงานหนึ่งแก้ข้อมูลก้อนเดิมตลอดเวลา ความหมายของข้อมูลจะขึ้นกับว่าใครเขียนก่อน ใครอ่านทีหลัง และอ่านระหว่างการเขียนหรือไม่ Dependency จึงซ่อนอยู่ในลำดับเวลาและ shared state ระบบต้องใช้ lock หรือกลไก concurrency control เพื่อป้องกันผลลัพธ์ที่ขัดแย้งกัน

เมื่อข้อมูลเป็น immutable การแปลงแต่ละครั้งสร้างผลลัพธ์เชิงตรรกะก้อนใหม่ ข้อมูลเดิมยังคงเดิม ความสัมพันธ์จึงเขียนออกมาเป็นสายได้ชัดเจน:

rawData
  └── filter(valid)
        └── map(toFeature)
              └── groupByKey
                    └── aggregate

สายนี้บอกได้ว่าผลแต่ละก้อนมาจากไหน หากยังไม่มีใครต้องการผล ระบบไม่จำเป็นต้องรีบคำนวณ นี่คือจุดที่ Lazy Evaluation เข้ามาใช้ประโยชน์จาก dependency ที่ชัดเจน เมื่อมี Action ระบบจึงพิจารณาสายงานทั้งหมด จัด stage และเลือกว่าจะเริ่มคำนวณตรงไหน

ความสัมพันธ์แบบย่อ
Immutability ทำให้ข้อมูลเดิมไม่เปลี่ยนและ dependency ติดตามได้ → Lazy Evaluation เก็บ dependency เป็นแผนก่อนรัน → Optimizer จัดรูปหรือตัดงานที่ไม่จำเป็น → Cache เลือกเก็บผลบางจุด เมื่อการคำนวณจุดนั้นแพงและต้องใช้ซ้ำ

ลด Write Lock ได้อย่างไร

หาก thread หรือ worker หลายตัวอ่านข้อมูล immutable ก้อนเดียวกัน ทุกคนรู้ว่าค่าจะไม่ถูกเปลี่ยนระหว่างอ่าน จึงไม่ต้องใช้ write lock เพื่อป้องกันข้อมูลก้อนนั้น และ reader ไม่ต้องรอ writer ที่กำลังแก้ค่าภายในก้อนเดียวกัน

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

อย่างไรก็ตาม Immutability ไม่ได้ทำให้ lock หายจากระบบทั้งหมด Scheduler ยังต้องจัดคิว Cache manager ยังต้องปรับ metadata และ storage ยังต้องประกาศว่าผลลัพธ์ชุดใหม่พร้อมใช้งานหรือไม่ จุดที่ลดลงคือการแย่งแก้ เนื้อหาของข้อมูลก้อนเดิม ไม่ใช่การประสานงานทุกชนิดในระบบ

ลด Dependency แบบใด

Immutability ไม่ได้ลบ dependency ทางคณิตศาสตร์ หาก C ต้องใช้ผลจาก B มันยังต้องรอ B เหมือนเดิม สิ่งที่ลดลงคือ write-after-read, read-after-write และ write-after-write dependency ที่เกิดจากหลายงานใช้ตำแหน่งเก็บข้อมูลร่วมกันโดยไม่จำเป็น

Dependencyเมื่อแก้ข้อมูลร่วมกันเมื่อสร้างผลลัพธ์ใหม่
Read after WriteReader ต้องรู้ว่า writer เขียนเสร็จหรือยังผลใหม่จะถูกส่งต่อเมื่อ transformation ที่สร้างมันเสร็จ
Write after ReadWriter อาจต้องรอ reader อ่านค่าเดิมให้เสร็จWriter สร้าง version ใหม่ Reader เดิมยังอ่าน version เก่าได้
Write after WriteWriter หลายตัวแย่งตำแหน่งเดียวกัน ต้องกำหนดลำดับแต่ละงานเขียน output แยกกัน แล้วค่อยเลือกหรือรวมผลตามกฎ
True data dependencyต้องรอผลก่อนหน้ายังต้องรอเหมือนเดิม เพราะเป็นข้อกำหนดของ algorithm

การทำ dependency ให้ชัดช่วยทั้ง scheduling และ recovery ระบบรู้ว่างานใดทำพร้อมกันได้ งานใดต้องรอ และหาก partition หายต้องย้อนคำนวณจาก parent ใด

จาก Random Write ไปสู่การเขียนผลใหม่

ระบบข้อมูลขนาดใหญ่มักหลีกเลี่ยงการกระโดดไปแก้ record เดิมทีละตำแหน่ง เพราะ random write ทำให้ storage ต้องค้นตำแหน่ง ล็อก block หรือ page และรักษาความถูกต้องระหว่างการเขียนหลายงาน การสร้าง output ใหม่เปิดทางให้แต่ละ partition เขียนไฟล์ของตนแบบต่อเนื่อง แล้วประกาศผลทั้งชุดเมื่อเสร็จ

// แนวคิดแบบแก้ข้อมูลเดิม
for each record:
    locate old position
    lock page
    update value
    unlock page

// แนวคิดแบบ transformation
read old partition sequentially
compute new records
write new partition sequentially

นี่เป็นเหตุผลที่ HDFS เน้นการเขียนแบบ append และระบบ analytical จำนวนมากสร้างไฟล์หรือ version ใหม่แทนการแก้ byte เดิมโดยตรง แนวคิดเดียวกันพบใน copy-on-write table, immutable segment และ LSM-based storage แม้รายละเอียดภายในต่างกัน

Random Access กับ Random Write ไม่ใช่เรื่องเดียวกัน
ข้อมูล immutable ยังอ่านแบบ random access ได้ เช่น immutable array ที่เข้าถึงตำแหน่งใดก็ได้ สิ่งที่ Immutability ช่วยหลีกเลี่ยงโดยตรงคือ random in-place update และการแย่งเขียนตำแหน่งเดิม ไม่ได้รับประกันว่ารูปแบบการอ่านจะต่อเนื่องหรือ cache-friendly เสมอไป

Lazy Evaluation ทำอะไรได้เมื่อเห็นแผนทั้งหมด

ถ้าแต่ละ transformation รันทันที ระบบเห็นเพียงขั้นปัจจุบันและต้องสร้างผลระหว่างทางทุกครั้ง เมื่อชะลอไว้ก่อน ระบบมองเห็น DAG ทั้งสายแล้วอาจ:

แต่ RDD optimizer ไม่ได้เข้าใจความหมายของคอลัมน์ลึกเท่า Catalyst ใน Spark SQL Lazy Evaluation ของ RDD ช่วยวาง execution DAG ส่วนการจัดรูป Relational Algebra ต้องอาศัย schema และ operator ระดับสูงในบทที่ 9

Cache อยู่ตรงไหนในความสัมพันธ์นี้

Lazy ไม่ได้หมายถึงจำผลลัพธ์ไว้ หากมี Action สองตัวใช้ RDD เดียวกัน Spark อาจคำนวณ lineage ซ้ำสองครั้ง จึงใช้ cache() หรือ persist() ระบุว่าผลตรงนี้ควรถูก materialize และเก็บไว้สำหรับการใช้ครั้งถัดไป

val cleaned = raw.filter(valid).map(normalize)

cleaned.count()          // Action แรก
cleaned.saveAsTextFile() // Action ที่สอง

// หาก cleaned ไม่ถูก cache สาย filter + map อาจถูกรันใหม่
// หาก cache ไว้ Action ที่สองอ่านผล cleaned ที่เก็บไว้ได้

จุด cache ที่ดีมักเป็นผลซึ่งสร้างแพง ใช้ซ้ำหลายครั้ง และมีขนาดพอเหมาะกับ storage level ที่เลือก จุด cache ที่ไม่ดีคือข้อมูลใช้ครั้งเดียว ใหญ่จนเบียดข้อมูลอื่น หรืออยู่ก่อน filter ทั้งที่สามารถลดขนาดได้มาก

Lineage ช่วยกู้คืนโดยไม่ต้องทำสำเนาผลระหว่างทางทุกก้อน

เพราะ dependency ชัด Spark สามารถคำนวณ partition ที่หายจาก parent ได้ ไม่จำเป็นต้อง replicate intermediate RDD ทุกตัวไว้หลายสำเนา วิธีนี้เหมาะเมื่อการคำนวณใหม่ถูกกว่าการเก็บสำเนา แต่ยังต้องมีต้นทางที่ทนทาน เช่นไฟล์บน HDFS หรือ object storage

ถ้า lineage ยาวมากหรือการคำนวณใหม่แพง อาจใช้ checkpoint ตัดสาย dependency แล้วบันทึกสถานะลง storage ที่ทนทาน Cache กับ checkpoint จึงต่างกัน: cache เน้นใช้ซ้ำและอาจสูญหายได้ ส่วน checkpoint เน้นสร้างจุดเริ่มกู้คืนใหม่

สิ่งที่ต้องแลก

ประโยชน์ต้นทุนที่ตามมา
ลดการแย่งแก้ข้อมูลเดิมต้องสร้าง object, partition หรือ file version ใหม่
Reader อ่าน version เดิมต่อได้ต้องจัดการหลาย version และลบข้อมูลที่หมดอายุ
เขียนต่อเนื่องและกระจายตาม partition ได้อาจเกิดไฟล์เล็กจำนวนมากและต้อง compaction ภายหลัง
ติดตาม lineage และคำนวณใหม่ได้Lineage ยาวทำให้ recovery แพง จึงอาจต้อง checkpoint
Cache ลดการคำนวณซ้ำใช้ memory, serialization และ eviction อาจทำให้ผลถูกคำนวณใหม่
ข้อสรุปที่แม่นยำ
Immutability, Lazy Evaluation และ Cache เป็นคนละแนวคิด แต่ประกอบกันได้ดี Immutability ลด shared write และทำให้ dependency คงที่ Lazy Evaluation ใช้ dependency นั้นสร้างแผนก่อนรัน ส่วน Cache เลือกเก็บผลที่คุ้มจะใช้ซ้ำ การเขียนผลใหม่ยังช่วยลด random in-place update และ write contention แต่ต้องแลกกับพื้นที่ การจัดการ version และงาน compaction
ลองวิเคราะห์สายงาน
หากมี pipeline read → parse → filter → feature → train ซึ่งต้องฝึกโมเดลหลายค่า parameter ลองตอบว่า ขั้นใดควร immutable จุดใดได้ประโยชน์จาก lazy plan จุดใดควร cache และจุดใดควร checkpoint คำตอบไม่จำเป็นต้องเหมือนกัน เพราะต้นทุนสร้าง ขนาดข้อมูล และจำนวนครั้งที่ใช้ซ้ำต่างกัน

จาก Cache ไปสู่ Superlinear Speedup

เมื่อแบ่งข้อมูลให้หลายหน่วยประมวลผล working set ต่อหน่วยอาจเล็กลงจนพอดีกับ cache จำนวน cache miss จึงลดลงมากกว่าที่คาดจากการแบ่งงานเพียงอย่างเดียว บางกรณีจึงเห็น speedup มากกว่าจำนวน CPU สิ่งนี้เรียกว่า Superlinear Speedup และจะอธิบายเต็มในบทที่ 6

ลองตรวจความเข้าใจ
โปรแกรมหนึ่งบวกค่าทุกตัวใน array ขนาดใหญ่ หากเพิ่ม core แล้วความเร็วหยุดเพิ่ม ทั้งที่แบ่งจำนวน element เท่ากัน สาเหตุอาจไม่ใช่งานไม่สมดุล แต่อาจเป็น memory bandwidth เต็มแล้ว ลองแยกให้ออกว่าเวลาถูกใช้กับ computation หรือ data movement

2โครงสร้างข้อมูลที่งาน Big Data ใช้บ่อย

โครงสร้างข้อมูลไม่ได้เป็นเพียงภาชนะ แต่กำหนดว่าจะเข้าถึงข้อมูลอย่างไร ข้อมูลวางต่อเนื่องหรือกระจัดกระจาย ต้องใช้หน่วยความจำเพิ่มเท่าใด และกระจายไปหลายเครื่องได้ง่ายเพียงใด Big-O บอกแนวโน้มของจำนวน operation แต่ไม่บอก locality, serialization cost หรือจำนวน byte ที่ต้องส่ง

โครงสร้างลักษณะการใช้งานใน Big Data
Array / Contiguous Bufferเข้าถึงตามตำแหน่ง O(1) และใช้ cache line ได้คุ้มเมื่อสแกนต่อเนื่องเหมาะกับ vectorized processing, columnar data และงานที่อ่านเป็นช่วง
Linked Structureแทรกหรือลบได้สะดวกเมื่อมี pointer ไปตำแหน่งนั้น แต่แต่ละ node มี overhead และ locality มักไม่ดีใช้ในโครงสร้างภายในบางชนิด แต่ไม่ใช่ตัวเลือกอัตโนมัติสำหรับ stream หรือ queue ขนาดใหญ่
Hash Tableค้นหาเฉลี่ย O(1) เมื่อ hash กระจายดีและ load factor เหมาะสม แต่กรณีแย่อาจช้าลงHash aggregation, cache, partitioning, hash join และ key-value lookup
Ordered Tree / LSM TreeTree รองรับ range query ตามลำดับ ส่วน LSM เน้นเขียนต่อเนื่องแล้ว merge ภายหลังIndex, storage engine และฐานข้อมูลที่ต้องแลก write amplification กับ read/compaction cost
Graph RepresentationAdjacency matrix ใช้พื้นที่ O(V²); adjacency list/edge list ใช้พื้นที่ตามจำนวน edgeกราฟ sparse มักใช้ adjacency list หรือ edge list และต้องเลือก partition จากรูปแบบการ traverse
Probabilistic Structureยอมให้คำตอบคลาดเคลื่อนที่ควบคุมได้ เพื่อใช้พื้นที่น้อยกว่าการเก็บสมาชิกทั้งหมดBloom Filter, Count-Min Sketch, HyperLogLog และงาน streaming

Array กับ Linked List: Big-O เท่ากัน แต่เครื่องจริงอาจต่างกันมาก

การสแกน array และ linked list มีความซับซ้อน O(n) เหมือนกัน แต่ array วางข้อมูลต่อเนื่องจึงอ่านล่วงหน้าและใช้ cache line ได้ดี Linked list ต้องตาม pointer ไปยัง node ถัดไป ซึ่งอาจอยู่คนละตำแหน่งในหน่วยความจำ ความต่างนี้ไม่ปรากฏใน Big-O แต่ชัดเมื่อข้อมูลมีจำนวนมาก

การบอกว่า LinkedList เหมาะกับ queue เพราะแทรกหรือลบบ่อยจึงต้องระวัง Queue ที่สร้างด้วย circular array สามารถเพิ่มและนำข้อมูลออกหัวท้ายแบบ amortized O(1) เช่นกัน และมักใช้หน่วยความจำน้อยกว่า การเลือกต้องดูทั้ง operation, locality และ overhead ต่อ element

Hash Table: O(1) โดยเฉลี่ยไม่ได้แปลว่าไม่มีต้นทุน

Hash Table ต้องคำนวณ hash จัดการ collision และขยายตารางเมื่อแน่นเกินไป หาก key กระจายไม่ดี บาง bucket จะรับงานมากกว่าส่วนอื่น ในเครื่องเดียวทำให้ lookup ช้าลง ส่วนในระบบกระจายทำให้บาง partition หรือบาง worker กลายเป็น hotspot

Hash เป็นทั้งเครื่องมือค้นหาและเครื่องมือวางข้อมูล
เมื่อใช้ hash(key) % p เลือกหนึ่งใน p partition เราคาดหวังให้ key กระจายใกล้เคียงกัน แต่ถ้างานส่วนใหญ่ถาม key เดียว ต่อให้จำนวน record กระจายเท่ากัน request ก็ยังไม่สมดุล Distribution ของข้อมูลกับ distribution ของ workload เป็นคนละเรื่อง

Range Partition กับ Hash Partition

วิธีข้อดีสิ่งที่ต้องแลก
Hash Partitionกระจาย point lookup และการเขียนได้ดีเมื่อ hash เหมาะสมRange scan ต้องไปหลาย partition และรวมผลกลับมา
Range Partitionข้อมูลที่ key ใกล้กันอยู่ด้วยกัน เหมาะกับ range query และ ordered scanSequential key อาจทำให้การเขียนไหลไป partition ล่าสุดจนเกิด hotspot

ความต่างนี้จะกลับมาใน HBase กับ Cassandra บทที่ 7 HBase เรียงตาม row key และแบ่งเป็นช่วง ส่วน Cassandra ใช้ hash ของ partition key เลือก token การออกแบบ key จึงเป็นการออกแบบตำแหน่งและ workload ไปพร้อมกัน

Tree, LSM Tree และเหตุผลที่ฐานข้อมูลไม่ได้ใช้โครงสร้างเดียว

B-tree และโครงสร้างใกล้เคียงรักษาลำดับและช่วยค้นช่วงข้อมูลได้ดี แต่การเขียนแบบสุ่มจำนวนมากอาจต้องแก้หลายหน้า LSM Tree เปลี่ยนการเขียนให้ไหลต่อเนื่องเข้า memory table และไฟล์เรียงลำดับ แล้วค่อย merge ผ่าน compaction ภายหลัง จึงเขียนเร็วขึ้นแต่ย้ายต้นทุนไปยัง read amplification, write amplification และ compaction

ไม่มี storage engine ที่ “เร็วกว่า” โดยไม่บอก workload ระบบที่รับ log เขียนต่อเนื่องอาจเลือกต่างจากระบบบัญชีที่ต้องแก้ record และอ่านช่วง key บ่อย

Graph: จำนวน vertex เท่ากันไม่ได้แปลว่างานเท่ากัน

Adjacency matrix เหมาะเมื่อกราฟหนาแน่นหรือ operation ต้องเข้าถึงคู่ vertex จำนวนมาก แต่กราฟจริงจำนวนมากมี edge น้อยกว่า V² มาก Adjacency list และ edge list จึงประหยัดพื้นที่กว่า อย่างไรก็ตาม vertex บางตัวอาจมี degree สูงมาก การแบ่ง vertex เท่ากันจึงทำให้จำนวน edge ต่อ worker ต่างกันมาก

Power-law และงานไม่สมดุล
ในเครือข่ายสังคม ผู้ใช้ส่วนใหญ่มีความสัมพันธ์ไม่มาก แต่บัญชีบางตัวเชื่อมกับคนจำนวนมหาศาล Worker ที่รับ vertex เหล่านี้ต้องส่งข้อความและประมวลผล edge มากกว่าส่วนอื่น กลายเป็น straggler ที่ทำให้ทั้งรอบต้องรอ

Bloom Filter และ Sketch: บางครั้งการไม่เก็บทุกอย่างคือคำตอบ

Bloom Filter ตอบว่า key “อาจมี” หรือ “ไม่มีแน่นอน” โดยใช้บิตและ hash หลายตัว มันอาจตอบว่ามีทั้งที่ไม่มีจริง แต่ไม่ควรตอบว่าไม่มีเมื่อเคยเพิ่ม key นั้นแล้ว คุณสมบัตินี้ช่วยหลีกเลี่ยงการเปิดไฟล์หรือส่ง request ที่รู้ล่วงหน้าว่าไม่พบ

Count-Min Sketch ประมาณความถี่โดยไม่เก็บตัวนับสำหรับทุก key ส่วน HyperLogLog ประมาณจำนวนสมาชิกไม่ซ้ำ โครงสร้างเหล่านี้ยอมเสียความแม่นยำเล็กน้อยเพื่อควบคุมหน่วยความจำ เป็นพื้นฐานสำคัญของ stream ซึ่งไม่สามารถเก็บประวัติทั้งหมดได้ตลอดไป

ลองเลือกโครงสร้าง
ถ้าต้องตอบว่า user เคยปรากฏหรือไม่, นับจำนวน user ไม่ซ้ำ, อ่านข้อมูลช่วงเวลา และรวมยอดตามสินค้า สี่คำถามนี้ไม่ควรใช้โครงสร้างเดียวกันทั้งหมด ลองระบุว่าแต่ละคำถามต้องการ membership, cardinality, order หรือ aggregation

3อัลกอริทึมและ Complexity ที่ควรคุ้นเคย

Complexity ช่วยให้เรามองแนวโน้มเมื่อขนาดปัญหาโตขึ้น แต่ใน Big Data ตัวแปรสำคัญไม่ได้มีเพียง n เราต้องคิดถึงจำนวนเครื่อง p, ปริมาณข้อมูลที่ส่ง, จำนวนรอบที่ต้องประสานงาน, ขนาดหน่วยความจำ และรูปแบบการเข้าถึง storage ด้วย

Divide and Conquer
แบ่งปัญหา แก้ส่วนย่อย แล้วรวมคำตอบ แต่ไม่ได้หมายความว่าทุกส่วนทำพร้อมกันได้ ต้องดู dependency และต้นทุนการรวมด้วย
Sorting and Grouping
การเรียงและจัดกลุ่มอยู่ใต้ SQL, MapReduce และการสร้าง index เมื่อข้อมูลเกิน RAM ต้องคิดถึง external sorting และ I/O หลายรอบ
Graph Traversal
BFS เดินกราฟทีละระดับ ใช้ O(V+E) สำหรับ adjacency-list graph และให้เส้นทางสั้นสุดเมื่อ edge ไม่มีน้ำหนักหรือมีน้ำหนักเท่ากัน
Randomized / Approximation
ยอมใช้ randomness หรือความคลาดเคลื่อนที่ควบคุมได้ เพื่อให้ใช้เวลาและพื้นที่น้อยลงเมื่อคำตอบ exact แพงเกินความจำเป็น

Big-O บอกอะไร และไม่บอกอะไร

O(n log n) บอกว่าอัตราการเติบโตสูงกว่า O(n) แต่ไม่บอก constant factor, cache behavior หรือจำนวน byte ที่อ่าน Algorithm O(n) ที่กระโดดอ่านข้อมูลจากเครือข่ายทีละรายการอาจช้ากว่า O(n log n) ที่ทำงานอยู่ใน RAM และอ่านต่อเนื่อง

Complexity จึงเป็นจุดเริ่มต้น ไม่ใช่ผล benchmark เราควรใช้มันตัดแนวทางที่ไม่สามารถโตได้ แล้วใช้ measurement ตรวจต้นทุนบนสถาปัตยกรรมจริง

Work, Span และ Cost ของการทำงานแบบขนาน

ถ้า Work สูงแต่ Span สั้น ปัญหามี parallelism มาก แต่ถ้า Span ยาวจากขั้นตอนที่ต้องทำเรียงกัน การเพิ่มเครื่องไม่สามารถข้าม dependency ได้ ในระบบกระจาย Span ยังรวม barrier และรอบการสื่อสารด้วย

Divide and Conquer ไม่เท่ากับ MapReduce เสมอไป

Divide and Conquer แบ่งปัญหาเป็นส่วนย่อยซึ่งอาจเรียกซ้ำ แล้วรวมคำตอบ ส่วน MapReduce กำหนดรูปการไหลของข้อมูลเป็น Map → Shuffle/Group → Reduce งานบางอย่างเข้ารูปนี้ได้ดี เช่น Word Count และ inverted index แต่ algorithm ที่ต้องแลกสถานะหลายรอบอาจต้องสร้าง MapReduce job ซ้ำจนมี overhead สูง

ค่าเฉลี่ยที่รวมผิดได้ง่าย
การเฉลี่ยค่าเฉลี่ยของแต่ละ partition ตรง ๆ ผิดเมื่อ partition มีจำนวน record ไม่เท่ากัน เราต้องส่งคู่ (sum, count) แล้วรวมสองค่านี้ก่อนหารตอนท้าย นี่คือตัวอย่างที่ representation ของผลย่อยกำหนดว่าการรวมแบบขนานถูกต้องหรือไม่

Sorting เมื่อข้อมูลใหญ่กว่า RAM

ในหน่วยความจำ เราอาจใช้ Merge Sort หรือ Quick Sort ตามคุณสมบัติที่ต้องการ แต่เมื่อข้อมูลใหญ่กว่า RAM ต้องแบ่งข้อมูลเป็น run ที่เรียงแล้วเขียนลง storage จากนั้น merge หลาย run ในรอบถัดไป ต้นทุน I/O และจำนวน pass จึงสำคัญกว่า comparison อย่างเดียว

Distributed sort เพิ่มขั้น partition ข้อมูลตามช่วง key แล้ว Shuffle ไปยัง worker ที่รับผิดชอบ แต่ถ้า boundary เลือกไม่ดี บาง worker จะรับข้อมูลมากกว่าส่วนอื่น Sampling จึงถูกใช้ช่วยประมาณ distribution ก่อนแบ่งช่วง

BFS: “คำตอบแรกดีที่สุด” ต้องระบุเงื่อนไข

BFS สำรวจกราฟทีละระดับ จึงหาเส้นทางที่ใช้จำนวน edge น้อยที่สุดในกราฟไม่มีน้ำหนัก หรือกราฟที่ edge ทุกเส้นมีน้ำหนักเท่ากัน หาก edge มีน้ำหนักต่างกันต้องใช้ algorithm เช่น Dijkstra เมื่อ weight ไม่ติดลบ การกล่าวว่า BFS optimal โดยไม่ระบุเงื่อนไขจึงกว้างเกินไป

สำหรับ graph representation แบบ adjacency list BFS ใช้เวลา O(V+E) และพื้นที่ O(V) ในกรณีทั่วไป ส่วน O(bd) เป็นการอธิบาย tree search ด้วย branching factor b และความลึก d ซึ่งใช้คนละมุมวิเคราะห์ ควรเลือก notation ให้ตรงกับสิ่งที่กำลังนับ

เมื่อทำ Parallel BFS
Vertex ใน frontier ปัจจุบันกระจายให้หลาย worker ขยายพร้อมกันได้ แต่ต้องรวม frontier ใหม่ ป้องกันการเยี่ยม vertex ซ้ำ และรอให้ระดับปัจจุบันเสร็จก่อนเดินระดับถัดไป ความยากจึงอยู่ทั้งที่ decomposition, communication และ synchronization

Monte Carlo กับ Las Vegas

Monte Carlo algorithm ใช้เวลาหรือทรัพยากรที่ควบคุมได้ แต่ยอมให้คำตอบมีโอกาสผิดหรือคลาดเคลื่อน เช่น การประมาณค่าด้วย sampling ส่วน Las Vegas algorithm ให้คำตอบถูกต้องเมื่อจบ แต่เวลาที่ใช้ขึ้นกับ randomness เช่น randomized quicksort ในบางมุมมอง

คำว่า “ยิ่งสุ่มมากยิ่งแม่น” ใช้ได้กับบาง estimator แต่ไม่ใช่คำอธิบาย algorithm randomized ทุกชนิด สิ่งที่ต้องระบุคือ error bound, confidence และทรัพยากรที่เพิ่มตามจำนวน sample

NP-hard ไม่ได้หายไปเมื่อเพิ่มเครื่อง

Parallelism ช่วยสำรวจทางเลือกได้หลายส่วนพร้อมกัน แต่ไม่เปลี่ยนอัตราการเติบโตแบบ exponential ให้กลายเป็น polynomial โดยอัตโนมัติ หากขนาดปัญหาเพิ่มเล็กน้อย จำนวนทางเลือกอาจโตเร็วกว่าจำนวนเครื่องที่เพิ่มมาก

ทางปฏิบัติจึงใช้ heuristic, approximation, relaxation, sampling หรือจำกัดขอบเขตปัญหา เป้าหมายไม่จำเป็นต้องเป็นคำตอบดีที่สุดทางคณิตศาสตร์เสมอ แต่ต้องรู้ว่าคำตอบที่ได้มีข้อรับประกันอะไร และล้มเหลวในกรณีใด

Communication Complexity: บางครั้งนับ byte สำคัญกว่านับ operation

Algorithm สองตัวอาจทำ arithmetic ใกล้กัน แต่ตัวหนึ่งต้องส่งข้อมูลทั้งหมดข้ามเครื่องหลายรอบ ขณะที่อีกตัวรวมผลในเครื่องก่อนส่ง ใน Big Data การวิเคราะห์จึงควรถามเพิ่มว่า:

โจทย์คิด
ถ้า algorithm A ทำงาน 109 operation บนเครื่องเดียว ส่วน algorithm B แบ่งเป็น 100 เครื่องแต่ต้อง Shuffle ข้อมูล 1 TB สองรอบ เราไม่สามารถตัดสินจากจำนวนเครื่องได้ ต้องรู้ทั้งความเร็ว CPU, storage, network และรูปแบบ overlap ระหว่าง computation กับ communication

4เครือข่าย ฐานข้อมูล และระบบปฏิบัติการ: สามเสาที่มองข้ามไม่ได้

Computer Networks

เครือข่ายไม่ได้เป็นสายต่อระหว่างเครื่องเฉย ๆ แต่เป็นทรัพยากรที่มี latency, bandwidth, topology, queue และโอกาสล้มเหลว การเรียกข้อมูลข้ามเครื่องต้องผ่าน serialization, protocol stack, switch และ software ปลายทาง จึงแพงและแปรปรวนกว่าการอ่านหน่วยความจำภายในเครื่องมาก

อย่าเทียบจาก bandwidth อย่างเดียว
ตัวเลข bandwidth ของเครือข่ายรุ่นใหม่อาจดูใกล้ bandwidth ของ storage บางชนิด แต่ latency และเส้นทาง software ต่างกันหลายระดับ การส่ง object เล็กหนึ่งชิ้นข้ามเครื่องไม่ควรถูกมองว่ามีต้นทุนใกล้กับ L3 cache หรือ RAM เพียงเพราะหน่วยบนเอกสารเป็น GB/s เหมือนกัน

Data Locality: ส่ง code ไปหา data

เมื่อข้อมูลหนึ่ง block มีขนาดหลายร้อย MB แต่ code ที่ประมวลผลมีเพียงไม่กี่ KB การส่ง task ไปยังเครื่องที่มีข้อมูลอยู่แล้วคุ้มกว่าขน block กลับมาหาโปรแกรม HDFS กับ scheduler จึงร่วมกันพยายามทำ node-local execution ก่อน rack-local และหลีกเลี่ยง off-rack transfer เท่าที่ทำได้

หลักเดียวกันปรากฏใน predicate pushdown ซึ่งส่งเงื่อนไขกรองลงไปยัง data source และ projection pruning ซึ่งอ่านเฉพาะคอลัมน์ที่ใช้ เป้าหมายคือทำให้ข้อมูลเล็กลงก่อนผ่านเครือข่าย

Topology และ Oversubscription

การส่งข้อมูลระหว่าง worker ใน rack เดียวกันกับข้าม rack อาจใช้เส้นทางและแย่ง bandwidth ต่างกัน หากทุก worker Shuffle พร้อมกัน ลิงก์ส่วนกลางอาจเป็นคอขวดแม้ NIC ของแต่ละเครื่องยังไม่เต็ม Scheduler และ partitioner จึงต้องรู้ตำแหน่งของข้อมูล ไม่ใช่รู้เพียงจำนวนเครื่อง

Failure เป็นเหตุการณ์ปกติเมื่อระบบใหญ่ขึ้น

เครื่องหนึ่งมีโอกาสเสียไม่บ่อย แต่คลัสเตอร์ที่มีเครื่องจำนวนมากจะพบ disk error, process crash, packet loss และ slow node เป็นประจำ ระบบ Big Data จึงออกแบบ retry, replication, lineage และ speculative execution ไว้เป็นส่วนหนึ่งของการทำงาน ไม่ใช่เพิ่มภายหลังเมื่อเกิดเหตุ

เครื่องช้าบางครั้งอันตรายกว่าเครื่องตาย
เครื่องที่ตายชัดเจนสามารถย้าย task ไปที่อื่นได้ แต่เครื่องที่ยังตอบแต่ช้ามากทำให้ทั้ง stage รอโดยไม่รู้ว่าจะเสร็จเมื่อใด Spark อาจเปิดสำเนา task ที่ช้าบน worker อื่น แล้วรับผลจากตัวที่เสร็จก่อน แนวคิดนี้ช่วยจัดการ straggler แต่ใช้ทรัพยากรเพิ่ม

เวลาในระบบกระจาย

Physical clock ของเครื่องต่าง ๆ ไม่ตรงกันสมบูรณ์และ drift ได้ การซิงโครไนซ์ลดความคลาดเคลื่อนแต่ไม่ทำให้เวลาทุกเครื่องเหมือนกันทุกขณะ ส่วน logical clock เช่น Lamport clock ใช้ช่วยจัดลำดับเหตุการณ์ตามความสัมพันธ์ happens-before ไม่ได้ใช้ตั้งนาฬิกาให้ตรงกัน

ความต่างนี้สำคัญต่อ log, event time และ conflict resolution หากใช้ timestamp จากคนละเครื่องตัดสินว่าเหตุการณ์ใดใหม่กว่าโดยไม่รู้ขอบเขต clock skew อาจเลือกข้อมูลผิด version ได้

Database Systems

ฐานข้อมูลสอนสามเรื่องสำคัญต่อ Big Data: วิธีแทนข้อมูล วิธีเข้าถึงข้อมูล และเงื่อนไขความถูกต้อง การเข้าสู่ NoSQL ไม่ได้ทำให้หลักเหล่านี้หายไป เพียงเปลี่ยน trade-off และย้ายความรับผิดชอบบางส่วนจากระบบไปยังผู้ออกแบบข้อมูล

หัวข้อรายละเอียด
IndexingHash index เหมาะกับ equality lookup; ordered index เช่น B-tree รองรับ range query; spatial index รองรับข้อมูลหลายมิติ แต่ทุก index เพิ่มต้นทุนการเขียนและพื้นที่
JoinNested-loop, Hash Join, Sort-Merge Join และ Broadcast Join เหมาะกับข้อมูลและเงื่อนไขต่างกัน Optimizer ต้องประเมินขนาดผลระหว่างทาง
Transactionกำหนดขอบเขตที่หลาย operation ต้องสำเร็จหรือย้อนกลับร่วมกัน พร้อมควบคุมผลของการทำงานพร้อมกัน
NoSQLกลุ่มระบบที่เลือก data model, query และ consistency ต่างจาก relational database เพื่อรองรับ access pattern หรือการกระจายบางรูปแบบ ไม่ได้แปลว่า “ใช้ hash ทั้งหมด” หรือ “ไม่มี schema” เสมอไป

Index คือสำเนาที่จัดระเบียบเพื่อคำถามบางชนิด

Index ทำให้การอ่านเร็วขึ้นเพราะไม่ต้องสแกนทุก record แต่ต้องแลกพื้นที่และต้นทุนอัปเดต เมื่อมี index จำนวนมาก ทุกการเขียนต้องแก้หลายโครงสร้าง ระบบ analytical ที่นำเข้าข้อมูลเป็นชุดอาจเลือก format, partition pruning และ metadata แทนการสร้าง index แบบ OLTP ทุกคอลัมน์

Join ไม่ได้มี complexity เดียว

การกล่าวว่า Join สองตารางใช้เวลาขนาดตารางคูณกันใช้ได้กับ nested-loop แบบพื้นฐาน แต่ระบบจริงมี Hash Join ที่โดยแนวคิดใช้ O(N+M) เมื่อเงื่อนไขและหน่วยความจำเหมาะสม และ Sort-Merge Join ซึ่งอาศัยการเรียงข้อมูล การเลือก algorithm ต้องดูชนิดเงื่อนไข ขนาดข้อมูล memory และการกระจาย key

Broadcast Join
ถ้าตารางหนึ่งเล็กมาก เราอาจส่งสำเนาไปทุก worker แล้ว Join กับ partition ใหญ่ในเครื่อง หลีกเลี่ยงการ Shuffle ตารางใหญ่ แต่ถ้าประเมินขนาดตารางเล็กผิด การ broadcast อาจใช้ memory ทุกเครื่องจนระบบล้มได้ Optimizer จึงต้องอาศัยสถิติที่เชื่อถือได้

Relational Algebra เปิดทางให้ Optimizer

Selection, Projection, Join และ Aggregation รับ relation แล้วคืน relation ใหม่ จึงประกอบและจัดรูปได้ เช่น ดัน filter ลงไปก่อน Join ตัดคอลัมน์ที่ไม่ใช้ออก และรวมผลบางส่วนก่อน Shuffle Spark SQL ใช้แนวคิดนี้สร้าง logical plan แล้วเลือก physical plan ในบทที่ 9

Schema-on-write กับ Schema-on-read

Schema-on-write ตรวจและจัดข้อมูลให้เข้ารูปก่อนบันทึก ทำให้ผู้ใช้ปลายทางเห็นความหมายสม่ำเสมอ แต่การนำเข้าต้องตกลง schema ล่วงหน้า Schema-on-read เก็บข้อมูลดิบยืดหยุ่นกว่า แล้วตีความตอนอ่าน แต่ภาระการทำความเข้าใจอาจกระจายไปยังผู้ใช้ทุกคน

การเลือกระหว่างสองแนวทางไม่ควรกลายเป็นศาสนา ระบบจริงมักมีหลายชั้น: raw zone เก็บข้อมูลใกล้ต้นทาง ส่วน curated table กำหนด schema และ quality สำหรับการใช้ร่วมกัน

Operating Systems: Locking และ Deadlock

Operating Systems จัดสรร CPU, memory, file, process และ I/O ซึ่งเป็นทรัพยากรเดียวกับที่ Big Data engine ต้องบริหาร หากไม่เข้าใจสิ่งเหล่านี้ เราอาจตีความอาการผิด เช่น คิดว่า algorithm ช้า ทั้งที่ระบบกำลัง swap หรือคิดว่าเพิ่ม thread แล้วเร็วขึ้น ทั้งที่ทุก thread แย่ง disk queue เดียวกัน

Process, Thread และ Context Switch

Process มี address space แยกกัน ส่วน thread ใน process เดียวกันใช้ memory ร่วมกัน การใช้หลาย thread ลดต้นทุนบางส่วนและแบ่งข้อมูลได้เร็ว แต่ shared state ทำให้ต้อง synchronize เมื่อ thread มากเกินไป context switch, cache contention และ lock contention อาจทำให้ throughput ลดลง

Virtual Memory และ Page Fault

Virtual memory ทำให้โปรแกรมมองเห็น address space ต่อเนื่อง แต่เมื่อ working set ใหญ่กว่า RAM ระบบอาจย้าย page ไป storage การเกิด page fault จำนวนมากทำให้ performance ตกอย่างรุนแรง โปรแกรมยังรันได้จึงดูเหมือนเพียง “ช้าลง” ทั้งที่กำลังใช้ดิสก์แทนหน่วยความจำ

นี่เป็นสาเหตุหนึ่งของ Superlinear Speedup: เมื่อแบ่งข้อมูลไปหลายเครื่อง working set ต่อเครื่องอาจกลับมาพอดีกับ RAM หรือ cache ทำให้ paging หรือ cache miss หายไปมาก

File, Buffer และ Page Cache

การอ่านไฟล์ไม่ได้แปลว่า byte เดินจากดิสก์ทุกครั้ง Operating System อาจเก็บ page ที่เพิ่งอ่านไว้ใน memory Benchmark รอบสองจึงเร็วกว่ารอบแรกเพราะ cache ไม่ใช่เพราะ algorithm ดีขึ้น การทดลองต้องบันทึกว่าเป็น cold cache หรือ warm cache และรวมเวลา I/O ส่วนใดไว้บ้าง

Locking และ Deadlock

Read lock อาจอนุญาตให้หลายงานอ่านพร้อมกัน ส่วน write lock ต้องกันการแก้ไขที่ขัดแย้ง หากงาน A ถือ lock X แล้วรอ Y ขณะที่งาน B ถือ Y แล้วรอ X จะเกิด deadlock

Wait-for graph ใช้แทนความสัมพันธ์ว่า transaction ใดรอใคร หากพบวงจรแปลว่ามี deadlock ภายใต้แบบจำลองนั้น วิธีจัดการมีทั้งกำหนดลำดับ lock, timeout, deadlock detection แล้ว abort บาง transaction หรือใช้ concurrency control แบบอื่น เช่น MVCC

Container ไม่ใช่ Virtual Machine ขนาดเล็กเฉย ๆ

Container แยก process ผ่าน namespace และจำกัดทรัพยากรผ่าน cgroup แต่ยังใช้ kernel ร่วมกับ host จึงเริ่มเร็วและเบากว่า VM โดยทั่วไป อย่างไรก็ตาม memory limit, CPU quota และ storage mount มีผลต่อ Spark worker และ database หากตั้งค่าไม่เข้าใจ อาการ out-of-memory หรือ throttling อาจถูกเข้าใจผิดว่าเป็น bug ของ application

เชื่อมสามเสาเข้าด้วยกัน
Query หนึ่งอาจช้าเพราะ Optimizer เลือก Join ผิด ทำให้เกิด Shuffle ขนาดใหญ่ เครือข่ายอิ่ม แล้ว worker ใช้ memory เกินจนเริ่ม spill ลง storage ปัญหานี้ไม่มีคำตอบจากวิชาใดวิชาหนึ่ง ต้องอ่าน database plan, network traffic และ OS metrics ร่วมกัน

5Parallel vs Concurrent Computing: คำสองคำที่มักถูกใช้ปนกัน

ความแตกต่างที่ควรยึด
Concurrency คือการออกแบบให้หลายงานมีช่วงเวลาทำงานทับซ้อนกันและก้าวหน้าไปด้วยกัน แม้เครื่องหนึ่ง core อาจสลับทำทีละงาน · Parallelism คือการที่หลายงานหรือหลายส่วนของงานกำลังประมวลผลในเวลาเดียวกันจริงบนหลาย execution unit ความต่างไม่ได้อยู่ที่งานเหมือนหรือต่างกัน แต่อยู่ที่โครงสร้างการก้าวหน้ากับการทำพร้อมกันจริง

ระบบ web server บน CPU หนึ่ง core สามารถ concurrent ได้ด้วย event loop สลับจัดการ request ระหว่างรอ I/O แต่ยังไม่ parallel ในขณะใดขณะหนึ่ง หากมีหลาย core และ request หลายตัวกำลังใช้ CPU พร้อมกัน ระบบจึงเป็นทั้ง concurrent และ parallel

พ่อครัวคนเดียวกับหลายคน
พ่อครัวคนเดียวทำหลายเมนูแบบ concurrent ได้ โดยหั่นผักระหว่างรอน้ำเดือด แล้วกลับไปดูเตา แต่ยังมีคนทำงานจริงเพียงคนเดียว ถ้ามีพ่อครัวหลายคนทำคนละส่วนพร้อมกันจึงเป็น parallel ปัญหาคือหลายคนอาจต้องใช้เตาหรือมีดชุดเดียวกัน จึงต้องออกแบบการแบ่งทรัพยากรด้วย
รูปแบบรายละเอียด
SIMD / Vectorizationคำสั่งหนึ่งทำ operation เดียวกับข้อมูลหลายค่า เหมาะกับ loop ที่ข้อมูลเป็นชนิดเดียวกันและไม่มี branch แตกต่างมาก
Data Parallelismแบ่งข้อมูลเป็น partition แล้วใช้ computation เดียวกันกับแต่ละส่วน Map และ Filter เข้ารูปนี้ได้ดี
Task Parallelismงานหลายชนิดทำพร้อมกัน เช่น อ่านข้อมูล แปลงข้อมูล และเขียนผล แต่ต้องจัด dependency กับทรัพยากรร่วม
Pipeline Parallelismแต่ละขั้นทำ record คนละชุดพร้อมกัน คล้ายสายพาน แต่ throughput ถูกจำกัดด้วยขั้นที่ช้าที่สุด
Shared MemoryThread หลายตัวเข้าถึง address space เดียว สื่อสารง่ายแต่ต้องควบคุม race condition และ contention
Message Passingแต่ละ process มี state ของตนและแลกข้อมูลผ่านข้อความ เหมาะกับหลายเครื่องแต่มี serialization และ network cost

Parallel, Distributed และ Scalable ไม่ใช่คำเดียวกัน

แนวคิดคำถามหลักตัวอย่างสิ่งที่กังวล
Parallel Computingทำอย่างไรให้งานเสร็จเร็วขึ้นด้วยหลายหน่วยประมวลผลDecomposition, dependency, speedup, load balance
Distributed Systemsทำอย่างไรให้หลายเครื่องร่วมกันทำงาน แม้สื่อสารช้าและล้มเหลวแยกกันได้Ordering, consistency, replication, partial failure
Scalable Systemsเมื่อ workload โต ระบบเพิ่มทรัพยากรแล้วรักษาคุณภาพและต้นทุนได้เพียงใดThroughput, latency, bottleneck, elasticity, cost

Spark job เป็นทั้ง parallel และ distributed ส่วนบริการ Spark SQL ที่รับ query หลายผู้ใช้ยังมี concurrency ด้วย การแยกคำช่วยให้รู้ว่าปัญหาที่เห็นเป็น race condition, network failure หรือ scalability limit

Race Condition และ Determinism

Race condition เกิดเมื่อผลลัพธ์ขึ้นกับลำดับเวลาของงานที่แข่งขันกัน เช่น thread สองตัวอ่านค่า counter เดิมแล้วเขียนค่าที่เพิ่มกลับไป ทำให้การเพิ่มหนึ่งครั้งหายไป ระบบกระจายยิ่งตรวจยากเพราะลำดับข้อความเปลี่ยนได้และการรันซ้ำอาจไม่เกิดอาการเดิม

Functional Programming และ immutability ลด shared mutable state แต่ไม่ได้กำจัดปัญหาทุกอย่าง Operation ที่รวมผลควรมีสมบัติที่เหมาะสม เช่น associativity เพื่อให้จัดกลุ่มใหม่แล้วคำตอบไม่เปลี่ยน หากลำดับมีผล ระบบต้องรักษาลำดับหรือยอมรับ nondeterminism อย่างชัดเจน

Load Balance กับ Straggler

แบ่ง record เท่ากันไม่รับประกันว่าเวลาทำงานเท่ากัน บาง record ใช้ computation มากกว่า บาง key มีข้อมูลมาก และบาง worker แชร์ทรัพยากรกับงานอื่น งานหนึ่ง stage มักเสร็จเมื่อ task ตัวสุดท้ายเสร็จ ดังนั้นหางของ distribution สำคัญกว่าค่าเฉลี่ย

คอขวดไม่ได้หายไป เพียงย้ายตำแหน่ง
เพิ่ม worker อาจลดเวลาคำนวณต่อเครื่อง แต่เพิ่มจำนวน connection, metadata และข้อมูลที่ต้อง Shuffle หาก coordinator หรือ network โตตามไม่ทัน speedup จะเริ่มแบน หรือระบบอาจช้าลงหลังผ่านจุดหนึ่ง

Backpressure: เมื่อปลายทางรับไม่ทัน

ใน pipeline หรือ streaming หากผู้ผลิตส่งข้อมูลเร็วกว่าผู้บริโภคประมวลผล queue จะโตจนใช้ memory หมด Backpressure คือกลไกส่งสัญญาณให้ต้นทางชะลอ ลดอัตรา หรือเลือกทิ้งข้อมูลตามนโยบาย การมี concurrent task มากขึ้นไม่ช่วยถ้าขั้นปลายยังเป็นคอขวดเดิม

ลองจำแนกปัญหา
ระบบรับ request ได้หลายรายการแต่รันบน core เดียวเป็น concurrency โปรแกรมคูณ matrix บน GPU เป็น parallelism และฐานข้อมูลทำสำเนาข้ามสามศูนย์ข้อมูลเป็น distributed system ระบบเดียวอาจมีครบทั้งสามอย่าง แต่เหตุผลและความล้มเหลวของแต่ละส่วนต่างกัน

6ACID ในระบบกระจาย: ตัวอย่างธนาคาร

ทำไมต้องพูดถึง ACID ก่อนเข้าเนื้อหา Big Data
ระบบข้อมูลบางชนิดเลือกขอบเขต transaction หรือ consistency model ต่างจาก relational database แบบดั้งเดิม เพื่อรองรับการกระจายและ availability แต่ก่อนจะเข้าใจว่าแลกอะไรไป ต้องแยกความหมายของ ACID ให้ถูกต้อง โดยเฉพาะคำว่า Consistency ซึ่งมักถูกใช้คนละความหมายกับ replica consistency
คุณสมบัติตัวอย่างระบบธนาคาร
Atomicityการหักบัญชีต้นทางและเพิ่มบัญชีปลายทางต้องสำเร็จร่วมกัน หรือย้อนกลับทั้งคู่ ไม่ทิ้งสถานะครึ่งทางให้เห็น
ConsistencyTransaction พาข้อมูลจากสถานะที่รักษากฎไปยังอีกสถานะที่ยังรักษากฎ เช่น ยอดรวมและข้อจำกัดบัญชีถูกต้อง คำนี้ไม่ใช่ข้อรับประกันว่าทุก replica เหมือนกันทันที
Isolationผลของ transaction ที่ทำพร้อมกันต้องสอดคล้องกับระดับ isolation ที่ระบบสัญญา ระดับอ่อนอาจยอมให้เห็นปรากฏการณ์บางอย่างเพื่อเพิ่ม concurrency
Durabilityเมื่อระบบยืนยัน commit แล้ว ผลต้องรอดจากความล้มเหลวตาม fault model ที่กำหนด ผ่าน log, replication หรือ storage ที่ทนทาน

ACID Consistency กับ Distributed Consistency ใช้คำเดียวกันแต่ถามคนละเรื่อง

ACID Consistency ถามว่ากฎหรือ invariant ของข้อมูลยังถูกต้องหรือไม่ เช่น จำนวนเงินไม่ถูกสร้างหรือหายจากการโอน ส่วน distributed consistency model ถามว่าการอ่านจากหลาย client หรือ replica เห็นลำดับและค่าตรงกันเพียงใด เช่น linearizability, causal consistency หรือ eventual consistency

ประโยคที่ควรหลีกเลี่ยง
“Consistency หมายถึงทุกสาขาเห็นยอดตรงกัน” กว้างเกินไปและนำ ACID ไปปนกับ replica consistency ควรระบุว่ากำลังพูดถึง invariant ของ transaction หรือมุมมองต่อข้อมูลหลายสำเนา

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

การทำให้ทุก transaction เหมือนรันเรียงทีละรายการให้ความเข้าใจง่าย แต่ลด concurrency และอาจแพงเมื่อกระจายหลายเครื่อง Database จึงมี isolation level ต่างกัน ซึ่งอาจยอมให้เกิด dirty read, non-repeatable read, phantom หรือ anomaly อื่นตามแบบจำลอง

MVCC ช่วยให้ reader เห็น snapshot โดยไม่ขวาง writer ทุกครั้ง แต่ต้องเก็บหลาย version และจัดการ garbage collection ไม่มีวิธีที่ให้ concurrency สูง ไม่มี overhead และรับประกันเข้มที่สุดพร้อมกันโดยไม่แลกอะไร

Durability ต้องถามว่า “ทนต่อความล้มเหลวแบบไหน”

การเขียนลง memory ของ process ยังไม่ทน process crash การเขียนลง disk เครื่องเดียวอาจไม่ทน disk หรือเครื่องเสีย การทำสำเนาหลายเครื่องช่วยได้ แต่ถ้าทุกสำเนาอยู่ rack เดียวก็ยังเสี่ยงต่อเหตุระดับ rack คำว่า durable จึงต้องผูกกับ failure model และช่วงเวลาที่ระบบยืนยันกับ client

จาก Transaction เดียวไปสู่ Distributed Transaction

เมื่อ transaction แตะหลาย service หรือหลาย database การ commit ต้องประสานหลายฝ่าย Protocol อย่าง Two-Phase Commit ช่วยให้ผู้เข้าร่วมทุกฝ่ายตกลงร่วมกัน แต่หาก coordinator ล้มเหลว ผู้เข้าร่วมอาจต้องรอ การประสานสองช่วงจึงเพิ่มทั้ง latency และความเสี่ยงที่ระบบจะให้บริการต่อไม่ได้ ระบบบางแบบจึงเปลี่ยนไปใช้ Saga ซึ่งแบ่งงานเป็น local transaction และกำหนดงานชดเชยไว้ล่วงหน้า

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

เมื่อใดยอมให้ข้อมูลไม่ตรงกันชั่วคราว

จำนวนไลก์ ยอดผู้ชม หรือ cache ของหน้าเว็บอาจคลาดเคลื่อนชั่วคราวได้ หากค่าจะปรับเข้าหากันภายหลังและไม่กระทบสิทธิสำคัญ แต่ยอดเงิน สินค้าชิ้นสุดท้าย หรือสิทธิการเข้าถึงต้องกำหนดข้อรับประกันเข้มกว่า การตัดสินใจควรมาจากผลเสียของความผิดพลาด ไม่ใช่เลือก ACID หรือ BASE ตามกระแส

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

Idempotency และ Retry

ระบบกระจายมัก retry เมื่อ timeout แต่ timeout ไม่บอกว่า server ไม่ได้ทำงาน อาจเป็นเพียง response หาย หากคำสั่ง “หักเงิน 100 บาท” ถูกทำซ้ำ ผลจะผิด จึงควรใช้ operation ที่ทำซ้ำแล้วผลเท่าเดิม หรือมี idempotency key ให้ระบบจำว่า request นี้เคยประมวลผลแล้ว

หลักที่ควรจำ
Network failure ทำให้ client แยกไม่ออกเสมอว่าคำสั่งไม่ถึง, server ทำแล้วแต่คำตอบหาย หรือ server กำลังทำอยู่ การออกแบบ retry จึงเป็นส่วนของ correctness ไม่ใช่เพียงกลไกเพิ่มความทนทาน

7แผนที่เชื่อมพื้นฐานไปยังบทต่อไป

พื้นฐานจะกลับมาในหัวข้อใดคำถามที่ควรติดตัวไป
Memory hierarchy และ localityDatacenter, HDFS, Spark cache, Superlinear Speedupข้อมูลถูกอ่านจากระดับใด และ working set พอดีกับ memory หรือไม่
Hash, range และ treePartitioning, HBase, Cassandra, Bloom Filterโครงสร้างนี้รองรับ point lookup, range scan หรือ write pattern แบบใด
Work, span และ communicationMapReduce, Parallel BFS, PageRank, Scalable MLงานใดทำพร้อมกันได้ และข้อมูลต้องเดินทางกี่รอบ
Relational Algebra และ JoinSpark SQL, Catalyst Optimizer, Data Integrationกรองหรือลดคอลัมน์ก่อน Shuffle ได้หรือไม่ และ Join แบบใดเหมาะ
Process, memory และ I/ODocker Lab, worker, spill, persistenceระบบติด CPU, memory, storage หรือ network กันแน่
Concurrency และ failureStreaming, distributed database, service deploymentเมื่อ retry หรือทำพร้อมกัน ผลลัพธ์ยังถูกต้องหรือไม่
ACID และ consistency modelCAP, BASE, NoSQL, replicationกฎใดห้ามผิด ข้อมูลส่วนใดยอมไม่ตรงกันชั่วคราว และนานเพียงใด

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

พื้นฐานของ Big Data ไม่ใช่รายการคำศัพท์แยกส่วน แต่เป็นการมองระบบเดียวผ่านหลายระดับ:

หากจำสูตร complexity ได้แต่ไม่เห็น data movement เราจะประเมินระบบกระจายต่ำเกินไป หากอ่าน dashboard ระบบเป็นแต่ไม่เข้าใจ correctness เราอาจเร่งระบบให้ตอบผิดเร็วขึ้น และหากรู้ชื่อเครื่องมือแต่ไม่รู้ workload เราอาจสร้าง cluster เพื่อแก้ปัญหาที่เครื่องเดียวทำได้ดีกว่า

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

บทที่ 3 จะขยายจากเครื่องหนึ่งเครื่องไปสู่ศูนย์ข้อมูล และอธิบายแนวคิด “The Datacenter is the Computer” ก่อนอ่านต่อ ลองเลือกงานหนึ่งแล้วระบุ working set, access pattern, จุดที่ข้อมูลข้ามเครื่อง และกฎที่ผลลัพธ์ห้ามละเมิด คำตอบสี่ข้อนี้จะกลายเป็นแกนสำหรับวิเคราะห์ทุกสถาปัตยกรรมต่อจากนี้

คำถามทบทวนความเข้าใจ
  1. อธิบายความต่างระหว่าง latency กับ bandwidth พร้อมยกตัวอย่างงานที่ติดข้อจำกัดแต่ละแบบ
  2. แยกความหมายของ immutability, lazy evaluation และ caching แล้วอธิบายว่า Spark ใช้แต่ละแนวคิดเพื่ออะไร
  3. อธิบายว่า Immutability ทำให้ dependency ชัดขึ้นและลด write contention ได้อย่างไร พร้อมระบุสิ่งที่ยังต้องใช้ lock หรือการประสานงานอยู่
  4. เปรียบเทียบ Cache กับ Checkpoint ในแง่จุดประสงค์ ความทนทาน และการกู้คืนเมื่อ partition หาย
  5. เปรียบเทียบ Parallel Computing กับ Concurrent Computing พร้อมยกตัวอย่างที่ Big Data ต้องใช้ทั้งสองแบบร่วมกัน
  6. อธิบาย ACID ทั้ง 4 ข้อผ่านตัวอย่างระบบธนาคาร และแยก ACID Consistency ออกจาก replica consistency
  7. อธิบายว่าทำไม Adjacency List จึงเหมาะกับกราฟข้อมูลขนาดใหญ่มากกว่า Adjacency Matrix
  8. เปรียบเทียบ Hash Partition กับ Range Partition และยกตัวอย่าง workload ที่เหมาะกับแต่ละแบบ
  9. อธิบายว่าทำไม Big-O อย่างเดียวไม่พอสำหรับเปรียบเทียบ algorithm บนระบบกระจาย
  10. เลือก Query หนึ่งคำสั่ง แล้วระบุ data structure, Join, network transfer และทรัพยากรของระบบปฏิบัติการที่อาจกลายเป็นคอขวด