SCT

กำลังเตรียมประสบการณ์ดิจิทัล

Cloudflare ประหยัด RAM 100 TB ด้วยคณิตศาสตร์และ Rust: ลดจุดแฮชใน consistent hashing ลง 90% โดยการกระจายโหลดแทบไม่เปลี่ยน

Cloudflare ลดหน่วยความจำของบริการกระจายโหลดภายในลงรวม 100 TB ทั่วโลก จากการตั้งคำถามกับค่าตั้งต้นที่ทุกคนใช้กันมาหลายปี คือจำนวนจุดแฮชต่อเซิร์ฟเวอร์ใน consistent hashing การคำนวณพบว่าจุดแฮช 90,000 จุดสุดท้ายต่อเซิร์ฟเวอร์ช่วยให้โหลดกระจายสม่ำเสมอขึ้นเพียง 0.7% และเมื่อเกินราว 10,000 จุด การชนกันของค่าแฮชกลับทำให้แย่ลง ทีมจึงลดจุดแฮชลง 90% และบีบขนาดโครงสร้างข้อมูลใน Rust อีก 25% บทความนี้อธิบายคณิตศาสตร์เบื้องหลัง วิธีบีบข้อมูลโดยไม่ใช้ unsafe และวิธีปล่อยการเปลี่ยนแปลงที่เสี่ยงโดยไม่ทำให้แคชทั้งโลกหาย

Cloudflare ลดหน่วยความจำของบริการกระจายโหลดภายในลงรวม 100 TB ทั่วโลก จากการตั้งคำถามกับค่าตั้งต้นที่ทุกคนใช้กันมาหลายปี คือจำนวนจุดแฮชต่อเซิร์ฟเวอร์ใน consistent hashing การคำนวณพบว่าจุดแฮช 90,000 จุดสุดท้ายต่อเซิร์ฟเวอร์ช่วยให้โหลดกระจายสม่ำเสมอขึ้นเพียง 0.7% และเมื่อเกินราว 10,000 จุด การชนกันของค่าแฮชกลับทำให้แย่ลง ทีมจึงลดจุดแฮชลง 90% และบีบขนาดโครงสร้างข้อมูลใน Rust อีก 25% บทความนี้อธิบายคณิตศาสตร์เบื้องหลัง วิธีบีบข้อมูลโดยไม่ใช้ unsafe และวิธีปล่อยการเปลี่ยนแปลงที่เสี่ยงโดยไม่ทำให้แคชทั้งโลกหาย

consistent hashing คืออะไร และทำไมต้องมีจุดแฮชหลายพันจุด

เมื่อมีเซิร์ฟเวอร์ปลายทางหลายตัว ระบบกระจายโหลดต้องตัดสินใจว่าคำขอแต่ละรายการควรไปที่ไหน วิธีง่ายที่สุดคือเอาค่าแฮชของคำขอหารเอาเศษด้วยจำนวนเซิร์ฟเวอร์ แต่ทุกครั้งที่เพิ่มหรือถอดเซิร์ฟเวอร์ คำขอเกือบทั้งหมดจะย้ายที่ ทำให้แคชหายทั้งหมด consistent hashing แก้ปัญหานี้ด้วยการวางเซิร์ฟเวอร์เป็นจุดบนวงแหวนค่าแฮช คำขอจะไปที่เซิร์ฟเวอร์ที่อยู่ถัดไปบนวงแหวน เมื่อเซิร์ฟเวอร์เปลี่ยน คำขอที่ต้องย้ายมีเพียงส่วนน้อย แต่ถ้าแต่ละเซิร์ฟเวอร์มีจุดเดียว ช่วงบนวงแหวนจะยาวไม่เท่ากันจนโหลดเอียง ทางแก้มาตรฐานคือให้แต่ละเซิร์ฟเวอร์มีหลายจุด ค่าที่ใช้กันทั่วไปใน NGINX และ Pingora คือ 160 จุดต่อเซิร์ฟเวอร์

ปัญหาที่ Cloudflare เจอ: จุดแฮชทวีคูณจนกิน RAM หลาย GB

บริการที่เป็นต้นเรื่องคือ Pingora Backend Router หรือ PBR ระบบกระจายโหลดภายในที่ใช้ไลบรารีโอเพนซอร์ส pingora-ketama สองอย่างทำให้จำนวนจุดพองขึ้น อย่างแรกคือการถ่วงน้ำหนักตามความจุของเซิร์ฟเวอร์ ทำให้จำนวนจุดต่อเซิร์ฟเวอร์ขึ้นไปถึงราว 100,000 จุด อย่างที่สองคือระบบสร้างวงแหวนแยกหนึ่งวงต่อการผสมกันของฟีเจอร์แต่ละแบบ มีวงแหวนหลายสิบวงที่เก็บข้อมูลซ้ำกัน บางโปรเซสจึงใช้ RAM ถึงราว 6 GB

คณิตศาสตร์ที่ตอบว่าจุดแฮชกี่จุดถึงพอ

ทีมใช้ค่าสัมประสิทธิ์การแปรผัน (coefficient of variation หรือ CV) วัดว่าส่วนแบ่งโหลดของแต่ละเซิร์ฟเวอร์ต่างจากค่าเฉลี่ยเท่าไร สำหรับเซิร์ฟเวอร์ N ตัวที่มีจุดแฮช k จุดต่อตัว ค่า CV ประมาณได้จากรากที่สองของ (N−1) หารด้วย (N·k + 1) ข้อสังเกตสำคัญคือ CV ลดลงตามรากที่สองของ k ไม่ใช่ตาม k ตรง ๆ การเพิ่มจุดสิบเท่าจึงลดความเอียงได้เพียงราว 3 เท่า ที่ 160 จุด CV อยู่ราว 8% และเมื่อคำนวณช่วงที่ระบบใช้จริง พบว่าจุดแฮช 90,000 จุดสุดท้ายต่อเซิร์ฟเวอร์ช่วยลดความคลาดเคลื่อนได้เพียง 0.7%

ยิ่งกว่านั้น ค่าแฮชที่ใช้เป็นจำนวนเต็ม 32 บิต ตามหลัก birthday paradox เมื่อจำนวนจุดรวมบนวงแหวนมากขึ้น โอกาสที่จุดสองจุดจะได้ค่าแฮชเดียวกันจะสูงขึ้นเร็วกว่าที่คนส่วนใหญ่คาด การจำลองของทีมพบว่าเมื่อเกินราว 10,000 จุดต่อเซิร์ฟเวอร์ การชนกันทำให้ความคลาดเคลื่อนเพิ่มขึ้นแทนที่จะลดลง ข้อสรุปคือจุดส่วนใหญ่ไม่ได้ช่วยอะไรเลย และบางส่วนทำให้แย่ลงด้วยซ้ำ ทีมจึงลดจากราว 100,000 เหลือราว 10,000 จุดต่อเซิร์ฟเวอร์

บีบโครงสร้างข้อมูลใน Rust โดยไม่ใช้ unsafe

แต่ละจุดบนวงแหวนเดิมเก็บค่าแฮชแบบ u32 (4 ไบต์) กับดัชนีเซิร์ฟเวอร์แบบ u32 (4 ไบต์) รวม 8 ไบต์ เมื่อพบว่าดัชนีแบบ u16 ซึ่งรองรับได้ถึง 65,536 เซิร์ฟเวอร์เพียงพอแล้ว ขนาดข้อมูลจริงควรเหลือ 6 ไบต์ แต่กฎการจัดเรียงหน่วยความจำ (alignment) ของ Rust ทำให้ struct ที่มีฟิลด์ u32 ถูกเติมช่องว่างจนยังเป็น 8 ไบต์ ทางแก้ปกติคือใส่ #[repr(packed)] ซึ่งเกี่ยวพันกับโค้ด unsafe ทีมเลือกเก็บจุดเป็นอาร์เรย์ไบต์ขนาด 6 ไบต์ แล้วเขียนเมธอดอ่านค่าแฮชจากไบต์ 0-3 และดัชนีจากไบต์ 4-5 ซึ่งคอมไพล์ออกมาเหมือน packed struct ทุกประการแต่ไม่ต้องมีโค้ด unsafe เลย ได้ส่วนลดอีก 25%

ที่มาของหน่วยความจำที่ประหยัดได้
การเปลี่ยนแปลงก่อนหลังส่วนแบ่งของการประหยัด
จำนวนจุดแฮชต่อเซิร์ฟเวอร์~100,000~10,000~75%
ขนาดข้อมูลต่อจุด8 ไบต์ (u32 + u32 + padding)6 ไบต์ ([u8; 6])~25%
รวมทั้งเครือข่ายประหยัด RAM ราว 100 TB · การกระจายโหลดไม่แย่ลงอย่างมีนัยสำคัญ–

ปล่อยการเปลี่ยนแปลงที่เสี่ยงอย่างไรไม่ให้แคชทั้งโลกหาย

การเปลี่ยนจำนวนจุดแฮชทำให้คำขอบางส่วนย้ายเซิร์ฟเวอร์ ถ้าเปลี่ยนพร้อมกันทั่วโลก แคชที่อุ่นอยู่จะหายเป็นวงกว้างและโหลดจะพุ่งไปที่ต้นทาง ทีมจึงให้ระบบถือวงแหวนเก่าและใหม่ไว้คู่กัน แล้วใช้ค่าแฮชของคำขอเป็นตัวเลือกว่าคำขอนั้นจะใช้วงแหวนไหน คำขอเดิมจึงได้ผลเดิมทุกครั้ง จากนั้นค่อย ๆ ขยายจากศูนย์ข้อมูลทดสอบไปยังกลุ่มที่ใหญ่ขึ้น โดยคุมสองมิติแยกกัน คือสัดส่วนทราฟฟิกและขอบเขตพื้นที่ ระหว่างทางเฝ้าดูการเลือกเซิร์ฟเวอร์ ตัวนับเวอร์ชันวงแหวน ข้อผิดพลาดการเชื่อมต่อ หน่วยความจำ เวลาเริ่มโปรเซส พฤติกรรมแคช และทราฟฟิกไปต้นทาง และย้อนกลับได้ทันทีโดยไม่ต้อง deploy ใหม่ หน่วยความจำลดลงอย่างเห็นได้ชัดเมื่อถอดโค้ดวงแหวนเก่าออกหลังย้ายครบ 100%

บทเรียนสำหรับทีมพัฒนาไทย

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

ทีมงาน Smart Cyber Tech ให้บริการออกแบบและปรับแต่งระบบคลาวด์ ตั้งแต่วิเคราะห์ต้นทุนหน่วยความจำและเครื่อง ไปจนถึงวางแผนปล่อยการเปลี่ยนแปลงแบบค่อยเป็นค่อยไปที่ย้อนกลับได้ ปรึกษาได้ที่ แบบฟอร์มติดต่อ

สรุปสาระสำคัญ

  • consistent hashing คืออะไร และทำไมต้องมีจุดแฮชหลายพันจุด
  • ปัญหาที่ Cloudflare เจอ: จุดแฮชทวีคูณจนกิน RAM หลาย GB
  • คณิตศาสตร์ที่ตอบว่าจุดแฮชกี่จุดถึงพอ
แหล่งอ้างอิงเรียบเรียงจาก Cloudflare Blog — Saving another 100TB of RAM with math (and Rust) — อ่านบทความต้นฉบับ
ภาพปก: Cloudflare Blog · ลิขสิทธิ์ภาพเป็นของเจ้าของต้นฉบับ ใช้ประกอบการรายงานพร้อมอ้างอิงแหล่งที่มา
มีคำถามเพิ่มเติมเกี่ยวกับบทความนี้? เขียนหาเราได้ที่ info@smart-cyber-tech.com
บริการที่เกี่ยวข้องจากทีมงาน Smart Cyber Tech

บทความอื่นที่เกี่ยวข้อง

บทความที่เกี่ยวข้อง

ลดหน่วยความจำได้ 100 เทระไบต์ด้วยการจัดโครงสร้างข้อมูลใหม่: บทเรียนจากแคช DNS ระดับโลก คลาวด์

ลดหน่วยความจำได้ 100 เทระไบต์ด้วยการจัดโครงสร้างข้อมูลใหม่: บทเรียนจากแคช DNS ระดับโลก

เมื่อระบบมีรายการแคชกว่า 250,000 ล้านรายการ การเปลืองหน่วยความจำเพียงหนึ่งไบต์ต่อรายการเท่ากับสูญเปล่าไปกว่า 250 กิกะไบต์ บทความนี้ถอดวิธีลดขนาดต่อรายการลง 56% ทีละขั้น พร้อมบทเรียนเรื่องการวัดผลที่ทีมไทยนำไปใช้กับระบบของตัวเองได้

อ่านบทความ
ลดความหน่วง P99 ของ CDN ลง 91% ด้วยการรวมข้อมูลเป็นชิ้นขนาดพอดี: บทเรียนเรื่อง cache จาก Vercel เว็บ

ลดความหน่วง P99 ของ CDN ลง 91% ด้วยการรวมข้อมูลเป็นชิ้นขนาดพอดี: บทเรียนเรื่อง cache จาก Vercel

Vercel เล่าว่าลดเวลาค้นหาข้อมูลเส้นทางของ CDN ในกลุ่มคำขอที่ช้าที่สุด 1 เปอร์เซ็นต์ (P99) จาก 215.8 มิลลิวินาทีเหลือ 19.1 มิลลิวินาทีได้อย่างไร สิ่งที่น่าสนใจคือไม่ได้ใช้เซิร์ฟเวอร์ที่แรงขึ้น แต่เปลี่ยนวิธีจัดเก็บข้อมูลจากอ็อบเจกต์แยกทีละเส้นทางมาเป็นชิ้นข้อมูลขนาดราว 200 KB ที่ค้นหาแบบ binary search ได้ในตัว บทความนี้สรุปปัญหา วิธีแก้ ตัวเลขผลลัพธ์ และบทเรียนที่ทีมพัฒนาเว็บในไทยนำไปใช้กับระบบของตัวเองได้

อ่านบทความ
GitHub ย้ายรันไทม์ของ Copilot จาก TypeScript 430,000 บรรทัดเป็น Rust 830,000 บรรทัดใน 14 สัปดาห์ด้วยเอเจนต์ AI: วิธีทำ ตัวเลขจริง และข้อผิดพลาด 5 แบบที่หลุดออกไป DevOps

GitHub ย้ายรันไทม์ของ Copilot จาก TypeScript 430,000 บรรทัดเป็น Rust 830,000 บรรทัดใน 14 สัปดาห์ด้วยเอเจนต์ AI: วิธีทำ ตัวเลขจริง และข้อผิดพลาด 5 แบบที่หลุดออกไป

GitHub เล่าเมื่อวันที่ 16 กันยายน 2026 ว่าย้ายรันไทม์ของเอเจนต์ Copilot ซึ่งเดิมเป็น TypeScript ราว 430,000 บรรทัด ไปเป็น Rust ที่จบด้วยโค้ดใช้งานจริง 832,378 บรรทัด ภายใน 14 สัปดาห์ครึ่ง โดยมีวิศวกรกำกับหลักเพียงคนเดียวและให้เอเจนต์ AI เขียนโค้ดเกือบทั้งหมดผ่าน pull request 128 รายการ ประเด็นสำหรับทีมพัฒนาไทยไม่ใช่ว่าควรเปลี่ยนไปใช้ Rust แต่คือวิธีจัดการงานเขียนใหม่ขนาดใหญ่ด้วยเอเจนต์ให้ระบบไม่หยุดให้บริการ บทความนี้สรุปเหตุผล กลยุทธ์แทนที่ทีละชิ้น ตัวเลขจากบันทึกเซสชัน และข้อผิดพลาด 5 แบบที่หลุดออกไปพร้อมวิธีกัน

อ่านบทความ

ติดต่อเพื่อขอรับคำปรึกษาจากทีมงาน

ตั้งแต่ประเมินระบบปัจจุบัน วางแผน จนถึงลงมือพัฒนา — คุยกับเราได้โดยไม่มีค่าใช้จ่าย

ปรึกษาทีมของเรา