Sharding
replica set ทำให้ข้อมูลของคุณยังอยู่รอด แต่สมาชิกทุกตัวก็ยังถือ ทั้งหมด ของชุดข้อมูล นั่นใช้ได้ไปจนถึงจุดที่เครื่องหนึ่งไม่สามารถบรรจุข้อมูลทั้งหมดของคุณได้อีกต่อไป หรือ primary หนึ่งไม่สามารถรับ write ทั้งหมดได้อีกต่อไป การเพิ่มหน่วยความจำและดิสก์ที่เร็วขึ้น — การ scale up — มีเพดานและแพงขึ้นอย่างรวดเร็ว ส่วน Sharding เลือก scale out แทน คือกระจาย collection เชิงตรรกะหนึ่งอันออกไปหลายเครื่อง ทั้งข้อมูลและภาระ write จึงถูกแบ่งกัน แต่ละเครื่องถือแค่ส่วนของตัวเอง และคุณเพิ่ม capacity ด้วยการเพิ่มเครื่อง
ส่วนประกอบของ sharded cluster
หัวข้อที่มีชื่อว่า “ส่วนประกอบของ sharded cluster”Sharding นำเสนอสามบทบาทนอกเหนือจากตัว shards เอง shard คือ replica set ที่ถือส่วนหนึ่งของข้อมูล config servers คือ replica set อีกชุดหนึ่งที่เก็บ metadata ของ cluster — แผนที่ว่าข้อมูลไหนอยู่บน shard ไหน และ mongos คือ router ขนาดเบาที่แอปพลิเคชันของคุณเชื่อมต่อแทนที่จะต่อ shard ใด shard หนึ่งตรง ๆ โดย mongos จะดูแผนที่นั้นแล้วส่งต่อแต่ละ request ไปยัง shard ที่ถูกต้อง
flowchart TB App["Application"] --> M["mongos router"] M --> CFG["Config servers — store the chunk map"] M --> Sh1["Shard 1 (replica set)"] M --> Sh2["Shard 2 (replica set)"] M --> Sh3["Shard 3 (replica set)"] CFG -. "tells mongos where each chunk lives" .- M
แอปของคุณไม่เคยคุยกับ shard โดยตรง แต่ต่อเข้า mongos ซึ่งดูแยกไม่ออกจาก server MongoDB ปกติ เบื้องหลัง endpoint เดียวนั้น mongos อ่าน metadata ที่ config servers ถืออยู่และตัดสินใจว่าแต่ละ query และแต่ละ write อยู่ที่ไหน
shard key, chunks และ balancer
หัวข้อที่มีชื่อว่า “shard key, chunks และ balancer”ทั้งระบบขึ้นอยู่กับการตัดสินใจหนึ่งอย่าง: shard key ที่เป็นฟิลด์ (หรือชุดของฟิลด์) ที่ MongoDB ใช้ตัดสินว่า document หนึ่งอยู่บน shard ไหน MongoDB แบ่งช่วงของ key ออกเป็นบล็อกที่ต่อเนื่องกันเรียกว่า chunks และกำหนดแต่ละ chunk ให้กับ shard เมื่อข้อมูลโตขึ้น chunk ที่ใหญ่เกินไปจะถูกแบ่งเป็นสอง และทำงานเบื้องหลังที่เรียกว่า balancer จะย้าย chunk ระหว่าง shard อย่างเงียบ ๆ เพื่อให้แต่ละ shard ถือของพอ ๆ กัน
flowchart LR K["Shard key value"] --> C["Which chunk owns this value"] C --> S["Which shard owns that chunk"] S --> Done["Document lands on that shard"] Bal["Balancer"] -. "migrates chunks to keep shards even" .- S
มีสองวิธีในการแมป key ไปยัง chunks Ranged sharding เก็บค่า key ที่อยู่ใกล้กันไว้ด้วยกัน — มีประโยชน์เมื่อคุณ query ตามช่วง แต่เสี่ยงถ้า write กระจุกตัวที่ปลายด้านหนึ่งของช่วง เพราะ write จะไปกองทับกันอยู่บน shard เดียว Hashed sharding ส่ง key ผ่าน hash ก่อน กระจายแม้แต่ค่าที่เรียงต่อเนื่องให้กระจายทั่วถึงข้าม shards — เยี่ยมสำหรับการกระจาย write แต่ทำลาย locality ไปด้วย query แบบช่วงจึงต้องกระจายไปถามทุก shard คุณเปิด sharding บน collection และเลือกกลยุทธ์ได้ในการเรียกเดียว:
sh.enableSharding("library")
// Ranged shard key on memberIdsh.shardCollection("library.loans", { memberId: 1 })
// Or hashed, to spread writes evenlysh.shardCollection("library.events", { deviceId: "hashed" })const admin = client.db("admin");await admin.command({ enableSharding: "library" });await admin.command({ shardCollection: "library.loans", key: { memberId: 1 },});admin = client.adminadmin.command("enableSharding", "library")admin.command( "shardCollection", "library.loans", key={"memberId": 1})admin := client.Database("admin")if err := admin.RunCommand(ctx, bson.D{ {Key: "shardCollection", Value: "library.loans"}, {Key: "key", Value: bson.D{{Key: "memberId", Value: 1}}},}).Err(); err != nil { return err}let admin = client.database("admin");admin .run_command(doc! { "shardCollection": "library.loans", "key": { "memberId": 1 }, }) .await?;การเลือก shard key ที่ดี
หัวข้อที่มีชื่อว่า “การเลือก shard key ที่ดี”shard key ที่ไม่ดีไม่สามารถแก้ไขแบบเงียบ ๆ ภายหลังได้ ดังนั้นการเลือกนี้สมควรได้รับความใส่ใจตั้งแต่แรก สามคุณสมบัติที่ทำให้ key ดี High cardinality หมายถึง key มีค่าที่แตกต่างกันจำนวนมาก เพื่อให้ข้อมูลแบ่งออกเป็น chunks ได้หลายอัน — ฟิลด์ที่มีค่าเพียงไม่กี่ค่าจำกัดว่าคุณกระจายได้ไกลแค่ไหน Even distribution หมายถึงค่ากระจายตัวข้าม document เพื่อให้ไม่มี shard ใด shard หนึ่งกลายเป็นคอขวด และ low hotspotting หมายถึง write ไม่ได้พุ่งเป้าไปยังช่วงที่กำลังโตเดียวกันทั้งหมด; key ที่เพิ่มขึ้นเรื่อย ๆ อย่างสม่ำเสมอเช่น timestamp หรือ id ที่ auto-increment คือกับดักคลาสสิก เพราะทุก write ใหม่ลงบน chunk “สูงสุด” เดียวกันและถล่ม shard เดียว การ hash key เช่นนั้น หรือการนำมาผสมกับฟิลด์ที่มี cardinality สูงกว่า จะทำลาย hotspot นั้น
ใน Atlas: มุมมอง cluster เปิด sharding ออกมาเป็นตัวเลือกการตั้งค่า และ Shard Key Advisor กับ Shard Key Analyzer ช่วยให้คุณประเมินการกระจายตัวของ key ที่กำลังพิจารณาก่อนตัดสินใจ ซึ่งคุ้มมากที่จะใช้ เมื่อพิจารณาว่า key เปลี่ยนยากแค่ไหนหลังจากนั้น
เคล็ดลับและข้อควรระวัง
หัวข้อที่มีชื่อว่า “เคล็ดลับและข้อควรระวัง”- เลือก shard key อย่างระมัดระวัง; เปลี่ยนทีหลังยากมาก และสมัยก่อนถึงกับต้องสร้าง collection ใหม่ collection ที่ reshard ได้มีอยู่ในเวอร์ชันสมัยใหม่ แต่ก็ยังเป็น operation ที่ควรหลีกเลี่ยง ดังนั้นเลือก key ให้ถูกตั้งแต่ครั้งแรก
- อย่า shard เร็วเกินไป replica set เดี่ยวรองรับ traffic ได้มากมาย shard เมื่อคุณมีหลักฐานว่าเครื่องเดียวตามไม่ทัน ไม่ใช่ตามความรู้สึก
- query ที่ใส่ shard key มาด้วยคือ targeted —
mongosส่งตรงไปยัง shard เดียวเป๊ะ ๆ ส่วน query ที่ไม่มี shard key คือ scatter-gather ต้องกระจายไปถามทุก shard ออกแบบ query ทั่วไปของคุณให้พก shard key ไปด้วย - hashed key กระจาย write ได้สวยงามแต่ทำลาย query แบบช่วง; ranged key รักษา locality ไว้แต่เสี่ยง hotspot เลือกการแลกเปลี่ยนที่ตรงกับวิธีที่คุณอ่านและเขียนจริง ๆ
ข้อแลกเปลี่ยน
หัวข้อที่มีชื่อว่า “ข้อแลกเปลี่ยน”| ตัวเลือก | Benefit | Cost |
|---|---|---|
| Ranged sharding | รองรับ query แบบช่วงได้เร็ว — targeted ไปยัง chunk ที่ต่อเนื่องกัน | เสี่ยง hotspot ถ้า write กระจุกตัวที่ปลายด้านหนึ่งของช่วง |
| Hashed sharding | กระจาย write สม่ำเสมอข้าม shard ลด hotspot | ทำลาย locality ของช่วง query แบบช่วงกลายเป็น scatter-gather ทุก shard |
ข้อผิดพลาดที่พบบ่อย
หัวข้อที่มีชื่อว่า “ข้อผิดพลาดที่พบบ่อย”- เลือก field ที่เพิ่มขึ้นแบบ monotonic (เช่น timestamp หรือ auto-increment
_id) เป็น shard key ตรง ๆ — ทุก write ใหม่พุ่งไปที่ chunk บนสุดของช่วงเดียว ถล่ม shard เดียวขณะที่ shard อื่นว่างเปล่า นี่คือ hotspot ที่พบบ่อยที่สุดของ sharded cluster ที่เพิ่งเปิดใช้งาน - ลืมว่า balancer ต้องใช้ I/O และ network ระหว่าง migrate chunk — migration เกิดขึ้นแบบ background แต่ก็แย่ง resource กับ traffic จริงอยู่ดี cluster ที่ sensitive ต่อ latency ควรตั้ง balancing window ไว้ในช่วงที่ traffic ต่ำ
- คิดว่า sharding แก้ปัญหา query ที่ไม่มี index รองรับ — sharding กระจายข้อมูล ไม่ได้ทำให้ query ที่ scan ทั้ง collection เร็วขึ้น ถ้า query ไม่มี index ที่เหมาะสม ก็จะ scan ทุก shard พร้อมกันแทนที่จะ scan เครื่องเดียว ซึ่งแย่กว่าเดิม
💡 ตัวอย่างจากของจริง
Baidu — ใช้ sharded cluster ของ MongoDB เก็บข้อมูลระดับหลายพันล้าน document กระจายข้ามหลาย shard สำหรับระบบค้นหาและ log ขนาดใหญ่
MongoDB Atlas — ช่วยแนะนำเวลาที่ควร shard ผ่าน Shard Key Advisor และจัดการ balancer กับ chunk migration ให้อัตโนมัติ ลดงาน operational ที่ต้องทำเองเทียบกับ self-hosted cluster