ปรับขนาดเป็น 5M RPM

Dec 01 2022
ใน Trendyol เรามีหน้าที่รับผิดชอบในการให้บริการหน้าเนื้อหาและข้อมูล เรายังให้บริการข้อมูลเนื้อหาเป็นกลุ่มสำหรับรายการโปรด คอลเลกชัน หรือแม้แต่หน้าชำระเงิน

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

ความต้องการหรือภาระที่คาดหวังของเราคืออะไร?

ตามที่คาดไว้ เรามักจะได้รับการเข้าชมมากขึ้นในช่วงเทศกาลช้อปปิ้งหรือช่วงกิจกรรม/ส่วนลด ในขณะที่เราได้รับประมาณ 1M rpm (คำขอต่อนาที) ในวันปกติ โหลดจะเพิ่มเป็นประมาณ 3M rpm ในช่วงเวลาที่มีงานยุ่ง ในช่วงเวลานั้นเราไม่สามารถปฏิบัติตามข้อกำหนดได้อย่างแม่นยำ เวลาตอบสนองของเราเพิ่มขึ้นเป็นสองเท่าจากปกติ และเรายังมีระยะหมดเวลามากกว่าที่เราจะเพิกเฉยได้

เป้าหมายและความคาดหวังต่อไปของเราคือทำความเร็วให้ได้อย่างน้อย 8 ล้านรอบต่อนาทีด้วยทรัพยากรเท่าเดิม ปัญหาก็คือเราไม่สามารถปรับขนาดได้มากเท่าที่ต้องการ นับประสาอะไรกับสิ่งที่เราคาดหวังต่อไป

ใครชอบความท้าทายบ้าง? ใช่เราแน่นอน :)

เราพบสาเหตุของปัญหาได้อย่างไร?

ดังนั้นเราจึงทำโปรไฟล์เพื่อทำความเข้าใจว่าโค้ดส่วนใดใช้เวลามากกว่าและใช้ทรัพยากรมากกว่ากัน อันดับแรก เราใช้ Java profiler เพื่อจำกัดส่วนที่จะติดตามให้แคบลง หลังจากนั้น เราได้เพิ่มร่องรอยของ New Relic แบบกำหนดเองในโค้ด เมื่อเราดูผลลัพธ์ เราพบว่าโค้ดบางส่วนใช้เวลาและทรัพยากรมากที่สุด

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

แค่ทำให้ดีที่สุดเท่านั้นยังไม่พอ คุณต้องรู้ว่าต้องทำอะไรแล้วทำให้ดีที่สุด — W. Edwards Deming

เราสามารถจัดการข้อมูลแบบอะซิงโครนัสได้หรือไม่?

เรากำลังใช้ Couchbase เป็นแหล่งข้อมูล เมื่อเราดูว่าเราสามารถแปลงข้อมูล Couchbase แบบอะซิงโครนัสได้อย่างไร เราพบว่า Couchbase มีโปรโตคอลการเปลี่ยนแปลงฐานข้อมูล (DCP) DCP สามารถให้สตรีมสำหรับการกลายพันธุ์แก่เราได้ ดังนั้นเราจึงสามารถฟังจากแหล่งข้อมูลเก่า ทำการแปลง async และเขียนไปยังแหล่งข้อมูลใหม่ของเรา คุณสามารถตรวจสอบบทความของAhmet Hatipogluหากคุณสงสัยเกี่ยวกับรายละเอียด

ตอนนี้ ถึงเวลาที่จะพิจารณาว่าเราสามารถเพิ่มประสิทธิภาพข้อมูลได้หรือไม่

เราต้องการข้อมูลทั้งหมดหรือไม่?

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

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

ตอนนี้เรามีอะไรบ้าง?

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

คุณพร้อมไหม? เราบันทึกข้อมูลได้ 77% (1.28TB) ค่อยยังชั่ว!

อะไรตอนนี้?

เราต้องการบางอย่างที่ใช้ข้อมูลที่เราเตรียมไว้ เราต้องการแยกความต้องการเนื้อหาจำนวนมากออกจากกัน และความต้องการหน้ารายละเอียดสินค้าเพียงเพราะลูกค้าของพวกเขา ความต้องการด้านขนาด และชุดกฎทางธุรกิจแตกต่างกัน ได้เวลาเขียนบริการใหม่แล้ว

ทำอะไรได้ไม่ต่างกัน?

บางทีเทคโนโลยีที่เราใช้ บริการเก่าใช้ Java 8 และ Spring boot เรามีประสบการณ์ที่มั่นคงกับQuarkus บางทีเราอาจลดการใช้ทรัพยากรและเวลาบูตลงเพื่อให้สามารถปรับขนาดได้มากขึ้น หรือเราใช้Fluxเพื่อรับเอกสารจาก Couchbase และบางทีเราอาจใช้ CompletableFuture เพื่อเปลี่ยนแปลง หลังจากระบุตัวเลือกของเราแล้ว เราได้พัฒนาแอปพลิเคชัน POC จำนวนมากและทำการทดสอบโหลดจำนวนมาก แต่ผลลัพธ์น่าจะดีกว่านี้ อย่างน้อยก็ไม่ใช่ความพยายามที่เราต้องการ การวิจัยควรดำเนินต่อไป

อย่างไรก็ตาม ฉันเป็นโกเฟอร์มาสี่ปีแล้ว ฉันเชื่อว่าเราสามารถรับเอกสารได้อย่างมีประสิทธิภาพด้วย goroutines ควบคู่กันไป ดังนั้นฉันจึงอยากลองทำ จากนั้น ฉันดู Couchbase Go SDK อย่างเป็นทางการ ซึ่งมีคุณสมบัติ "รับเอกสารจำนวนมาก" มันเยี่ยมมาก! ความต้องการเหมาะสมกับการใช้ Go และฉันต้องการลองทั้งสองตัวเลือก

ฉันรันการทดสอบโหลดทั้ง goroutines แบบขนานและคุณลักษณะแบบกลุ่ม เวลาตอบสนองและการใช้ทรัพยากรได้รับการปรับปรุงอย่างมีนัยสำคัญสำหรับทั้งคู่ คุณลักษณะแบบกลุ่มดีกว่าการใช้งานแบบธรรมดาของเรา แต่มีบางอย่างดับลง การใช้งานเครือข่ายเพิ่มขึ้นสี่เท่า เราสามารถกำหนดค่าให้กับการเชื่อมต่อ Couchbase ใน Java SDK ที่เปิดใช้งานการบีบอัด แต่ Go SDK ไม่มีการกำหนดค่าการบีบอัดในตัวเลือกการกำหนดค่าคลัสเตอร์ ดังนั้นฉันจึงพลาดการกำหนดค่าตามนั้น น่าเสียดายที่คุณลักษณะการบีบอัดไม่สามารถควบคุมได้ผ่าน Couchbase Go SDK ซึ่งแตกต่างจาก Couchbase Java SDK ดังนั้นฉันจึงตรวจสอบโค้ดของมัน ทำการดีบั๊กที่เจ็บปวด และค้นพบว่าการบีบอัดสามารถควบคุมได้ผ่านตัวแปรเคียวรีที่ส่วนท้ายของสตริงการเชื่อมต่อ

couchbase://{HOST_HERE}?compression=true

      
                

  • เรามีการใช้ RAM เพิ่มขึ้นอย่างมาก และการใช้หน่วยความจำลดลงจาก800MBเป็น60MBต่อพ็อด
  • เวลาตอบสนองหายไปครึ่งหนึ่ง 10msถึง5ms _ และมีความเสถียรมากกว่าเมื่อเทียบกับปริมาณงานสูง
  • การใช้งาน CPU ลดลงอย่างมาก เพื่อให้ถึง 5M ด้วยบริการเก่า เราต้องการพ็อดเพิ่มเติม นี่จะเป็นการเปรียบเทียบการใช้งาน CPU ทั้งหมด ตอนนี้เป็น130 คอร์แทนที่จะเป็น300
  • ขนาดเอกสารรวมตอนนี้น้อยกว่าหนึ่งในสี่ของเมื่อก่อน จาก1.67TBเหลือเพียง386GB
  • โดยปกติแล้ว โหลดเครือข่ายจะต่ำกว่ามาก
  • เวลาบูตตอนนี้คือ85 มิลลิวินาทีแทนที่จะเป็น12 วินาที !

ขอบคุณEmre Odabasสำหรับการสนับสนุนและให้กำลังใจในการเขียนบทความนี้