- กำลังใช้งานคลัสเตอร์ NoSQL DB (ScyllaDB) ที่ประมวลผลข้อความ 2 ล้านรายการต่อวินาที
- สิ่งที่ส่งผลต่อประสิทธิภาพของ DB มากที่สุดคือเลเทนซีของฮาร์ดแวร์ดิสก์แบบกายภาพ
→ ในระดับที่ปริมาณคิวรีไม่สูงมากอาจไม่เป็นปัญหา แต่เมื่อเกินจุดหนึ่งไปแล้ว แม้เวลาอ่าน 1–2ms ก็เพียงพอที่จะทำให้เกิดคิวรออ่านจากดิสก์ และทำให้คิวรีเองเกิด timeout
- โดยปกติเลเทนซีของดิสก์มักอยู่ในระดับไมโครวินาที แล้วทำไม disk operation จึงใช้เวลา 1–2ms?
- Discord รันฮาร์ดแวร์ส่วนใหญ่บน Google Cloud
- รองรับ Local SSD แบบ NVMe แต่เมื่อทดสอบเองพบปัญหาเรื่องความเสถียร จึงไม่สบายใจที่จะใช้เป็นที่เก็บข้อมูลสำคัญ
- Persistent Disk สามารถเชื่อมต่อ/ถอดออกจากเซิร์ฟเวอร์ได้แบบเรียลไทม์, resize ได้โดยไม่ต้องมี downtime, สร้าง snapshot ได้ตลอดเวลา และถูกออกแบบให้มีการทำสำเนาโดยปริยาย
→ ปัญหาคือมันไม่ได้ต่ออยู่กับเซิร์ฟเวอร์โดยตรง แต่เชื่อมต่อผ่านเครือข่าย
- ต่อให้เลเทนซีของ local network connection ต่ำแค่ไหน ก็ไม่ต่ำกว่า PCI/SATA
→ เครือข่ายอยู่ที่ 1–2ms ส่วนดิสก์ที่ต่อโดยตรงอยู่ที่ 0.5ms
- Local SSD หากเกิดปัญหาฮาร์ดแวร์เหมือน HDD ก็จะสูญเสียข้อมูลบนดิสก์นั้น และถ้าโฮสต์เองมีปัญหาก็ไม่สามารถทำ snapshot ได้ ทำให้เสี่ยงต่อการสูญเสียข้อมูลทั้งหมด
→ เพราะเหตุนี้ Discord จึงไม่ใช้ Local SSD แต่ใช้ Persistent Disk
การวิเคราะห์ปัญหา
- ถ้ามีสตอเรจที่รวมข้อดีของทั้ง Local SSD และ Persistent Disk ได้ทั้งหมดก็คงดีที่สุด แต่ความจริงไม่มี ถ้าเลือกเอามาได้เพียงบางข้อดีล่ะ?
- สำหรับ Discord เลเทนซีฝั่งเขียนไม่ใช่ปัญหา สิ่งที่กระทบประสิทธิภาพคือ "เลเทนซีฝั่งอ่าน"
- "การ resize ดิสก์โดยไม่มี downtime" ไม่ใช่ฟีเจอร์จำเป็น เพราะขนาดสามารถคาดการณ์ล่วงหน้าได้
- ข้อกำหนดสุดท้ายคือ
- ยังคงอยู่บน GCP
- ใช้ snapshot แบบ Point-in-Time เพื่อสำรองข้อมูล
- ให้ความสำคัญสูงสุดกับการลดเลเทนซีฝั่งอ่าน
- ต้องไม่แลกมากับการลดทอนการรับประกัน uptime ของฐานข้อมูลเดิม
- ถ้าใช้ Local SSD สำหรับการอ่าน และใช้ Persistent Disk สำหรับการเขียนก็น่าจะดี
→ สามารถสร้าง super-disk แบบนี้ในระดับซอฟต์แวร์ได้หรือไม่?
การสร้าง Super-Disk
- โดยพื้นฐานแล้วข้อกำหนดคือแคชแบบ Write-Through โดยใช้ Local SSD ของ GCP เป็นแคช และใช้ PD เป็น storage layer
- เซิร์ฟเวอร์ฐานข้อมูลใช้ Ubunut อยู่ จึงสามารถใช้แคชระดับดิสก์ที่ชั้น Linux kernel ได้ (โมดูลอย่าง dm-cache, lvm-cache, bcache)
- แต่เมื่อทดลองพบว่าหากเกิด bad sector บนดิสก์แคช การอ่านทั้งหมดจะล้มเหลว
- เมื่อเกิด bad sector ควรต้องอ่านจาก storage layer แล้วเขียนทับกลับลงมา แต่โซลูชัน disk caching ที่ประเมินไว้ไม่มีฟังก์ชันนี้
- เมื่อเกิด bad sector ฐานข้อมูลจะ shutdown ไปเลยเพราะปัญหาด้านความปลอดภัยของข้อมูล
- จึงมีการเพิ่มข้อกำหนดว่า "ต้องอยู่รอดได้แม้ Local SSD จะเกิด bad sector"
- เลยไปศึกษาระบบ "md" ของ Linux kernel
- md รองรับการสร้าง software RAID
- การ mirror ระหว่าง SSD กับ PD อย่างเดียวไม่ช่วยแก้ปัญหา เพราะการอ่านเกินครึ่งจะยังไปเกิดที่ PD
- ใน md มีฟีเจอร์ "write-mostly" ที่ไม่มีใน RAID แบบดั้งเดิม
- หากกำหนดดิสก์ลูกใดเป็น write-mostly ดิสก์นั้นจะถูกตัดออกจากการอ่านปกติ และจะถูกอ่านก็ต่อเมื่อไม่มีทางเลือกอื่นเท่านั้น ซึ่ง "มีประโยชน์กับอุปกรณ์ที่เชื่อมต่อช้า"
- กล่าวคือ หากรวม SSD และ PD เป็น RAID1 แล้วตั้งค่า PD ให้เป็น write-mostly ก็จะตอบโจทย์ข้อกำหนดได้
- ปัญหาสุดท้ายที่เหลือคือ Local SSD ของ GCP มีขนาดตายตัวที่ 375GB
- สำหรับบางแอปพลิเคชันของ Discord ต้องการมากกว่า 1TB ต่อ DB instance
- จึงตัดสินใจรวม SSD หลายลูกเป็น RAID0
- โครงสร้างสุดท้ายคือ
- Local SSD 4 ลูกที่รวมกันเป็น RAID0 คือ md0
- จากนั้นนำ md0 กับ Persistent Disk มารวมกันเป็น RAID1 ชื่อ md1
ประสิทธิภาพของ DB
- ผลลัพธ์ออกมาตรงตามที่คาดไว้
- แม้ในช่วงพีก disk operation ก็ไม่ไปกองคิว และ query latency ไม่เปลี่ยนแปลง
- ประสิทธิภาพดีขึ้น ทำให้แต่ละเซิร์ฟเวอร์รองรับปริมาณคิวรีได้มากขึ้น
- คนที่เคยใช้ RAID อาจสงสัยว่า "มันจะใช้งานได้จริงแบบนี้เหรอ?" แต่ในความเป็นจริงมีรายละเอียดอีกมาก และจะมีการเล่าต่อในภายหลัง
3 ความคิดเห็น
ก่อนหน้านี้ดูเหมือนว่าพวกเขาจะไม่พอใจกับประสิทธิภาพของ Golang จนถึงขั้นเขียนเซิร์ฟเวอร์ด้วย Rust ด้วย ทำให้รู้สึกได้เลยว่าบริษัทอย่าง Discord ก็มีความเป็นสายกี๊กสูงมากเหมือนกันครับ
ให้ความรู้สึกเหมือนเอาเรื่องเล็กมาจัดการแบบเกินจำเป็นมากครับ
ใน HN ก็มีคนพูดกันอยู่ว่า หรือจริง ๆ แล้วมันก็แค่ปัญหาของ GCP ไม่ใช่เหรอ?..
https://news.ycombinator.com/item?id=32474093
คิดว่าอย่างน้อยก็น่ารู้ไว้ประมาณว่า อ้อ มีความพยายามแบบนี้ด้วยเหมือนกัน