เว็บไซต์วิดีโอที่มีทราฟฟิกสูงสามารถลดจำนวนการคิวรีฐานข้อมูลสำหรับฟีดหน้า trending จาก 4,000 ครั้งต่อนาที เหลือไม่ถึง 50 ครั้ง และลดเวลาตอบสนองที่ percentile ที่ 95 จาก 380 ms เหลือเพียง 40 ms โดยการเปลี่ยนจากการทำ whole-page caching มาเป็นการทำ fragment caching ด้วย Varnish และ Edge Side Includes (ESI)
ทำไมเว็บไซต์จึงต้องการกลยุทธ์การทำแคชที่แตกต่างออกไป
หน้าแรกที่แสดงคลิปที่มีผู้ชมมากที่สุดในแต่ละวันจะมีลักษณะเหมือนกันสำหรับผู้เข้าชมเกือบทุกคนในภูมิภาคหนึ่งๆ โดย HTML ประมาณ 95 เปอร์เซ็นต์จะเหมือนกันสำหรับผู้ใช้หนึ่งล้านคน ในขณะที่อีก 5 เปอร์เซ็นต์ที่เหลือจะเป็นข้อมูลส่วนบุคคล เช่น ชื่อของผู้ใช้ที่ล็อกอินอยู่ หรือช่องค้นหา ทีมวิศวกรต้องเผชิญกับสองทางเลือกที่ไม่น่าพึงพอใจ:
- ทำแคชทั้งหน้าเว็บ ซึ่งเสี่ยงต่อการแสดงข้อมูลส่วนบุคคลที่ล้าสมัยให้กับผู้ใช้ที่ล็อกอินอยู่
- ข้ามการทำแคชไปเลย ซึ่งจะทำให้ทุกคำขอ (request) เข้าไปรุมถล่มฐานข้อมูล
ทั้งสองวิธีส่งผลเสียต่อประสบการณ์ของผู้ใช้ ทีมงานจึงหันไปใช้ ESI ซึ่งเป็นเทคนิคที่ช่วยให้ reverse-proxy สามารถประกอบหน้าเว็บขึ้นมาจาก fragment ต่างๆ ที่ถูกทำแคชแยกกันไว้ที่ edge ของเครือข่าย
วิธีการวางระบบ fragment caching
Varnish ซึ่งเป็น HTTP accelerator แบบ open-source มองหน้าเว็บเป็นโครงร่าง (skeleton) ที่ประกอบด้วยชิ้นส่วนสามส่วนที่สามารถเปลี่ยนแทนกันได้:
- Video grid – รายการวิดีโอเทรนด์ที่ครอบคลุมทั้งภูมิภาคซึ่งใช้ทรัพยากรสูง ทำแคชไว้ 60 วินาที เนื่องจากมีการเปลี่ยนแปลงบ่อยแต่เหมือนกันสำหรับผู้เข้าชมที่ไม่ระบุตัวตนทุกคน
- Language switcher – องค์ประกอบ UI แบบ static ที่แทบไม่มีการเปลี่ยนแปลง ทำแคชไว้ 24 ชั่วโมง
- Header – fragment ส่วนเดียวที่เป็นข้อมูลส่วนบุคคลจริงๆ (ชื่อผู้ใช้, รูปโปรไฟล์, การแจ้งเตือน) ไม่มีการทำแคช โดย Varnish จะส่งคำขอไปยัง application server ทุกครั้ง
เมื่อมีคำขอเข้ามา Varnish จะส่งโครงร่างที่ทำแคชไว้ พร้อมกับดึง fragment สองส่วนที่ทำแคชไว้จาก local store และแทรก header แบบสดๆ (live) จาก backend เข้าไป
ตัวเลขที่สำคัญ
หลังจากการเปลี่ยนระบบ:
- ภาระของฐานข้อมูลสำหรับหน้า trending ลดลงจาก 4,000 queries ต่อนาที เหลือ ไม่ถึง 50
- ค่า latency ที่ percentile ที่ 95 ลดลงจาก 380 ms เหลือ 40 ms
บทเรียนเชิงปฏิบัติ 3 ข้อจากการนำไปใช้งานจริง
1. การใช้ grace period ช่วยลดผลกระทบจาก cache misses เมื่อ TTL ของ fragment หมดอายุ ปกติแล้ว Varnish จะหยุดรอเพื่อดึงเนื้อหาใหม่ ซึ่งทำให้เกิด latency spike ที่อาจลุกลามกลายเป็นปรากฏการณ์ “thundering herd” หรือการที่ backend ถูกเรียกใช้งานพร้อมกันจำนวนมหาศาล การกำหนดค่า grace period จะช่วยให้ Varnish ยังคงส่ง fragment ที่ล้าสมัยออกไปได้ในขณะที่ทำการรีเฟรชแคชในพื้นหลังอย่างเงียบๆ ผู้ใช้จะไม่รู้สึกถึงการหยุดชะงัก และ backend จะได้รับอัตราคำขอที่สม่ำเสมอและจัดการได้ง่าย
2. การลบคุกกี้สำหรับ fragment ที่ไม่ระบุตัวตน คุกกี้ที่แนบมากับทุกคำขอจะทำให้ Varnish มองว่าแต่ละคำขอเป็นข้อมูลที่ไม่ซ้ำกัน ซึ่งจะทำให้การทำ cache hit ไร้ผล ทีมงานจึงทำการลบคุกกี้สำหรับ video grid และ language switcher เพื่อให้ fragment เหล่านั้นสามารถทำแคชได้อย่างเต็มประสิทธิภาพ โดยมีเพียง fragment ส่วน header เท่านั้นที่แนบคุกกี้ไว้ เพื่อรักษาการแสดงผลเฉพาะบุคคลโดยไม่สูญเสียประสิทธิภาพในการทำแคช
3. การใช้ surrogate keys ช่วยให้สามารถล้างแคช (purge) ได้ทันที ในบางครั้ง วิดีโอจำเป็นต้องถูกลบออกทันที เช่น ด้วยเหตุผลด้านลิขสิทธิ์ การรอให้ TTL 60 วินาทีหมดอายุจึงเป็นเรื่องที่ยอมรับไม่ได้ ด้วยการติดแท็ก surrogate key ให้กับแต่ละ fragment ที่ทำแคชไว้ โดยอ้างอิงตาม video ID ทีมงานสามารถส่งคำสั่ง purge เพียงคำสั่งเดียวเพื่อยกเลิกความถูกต้อง (invalidate) ของวิดีโอเฉพาะเจาะจงนั้นๆ ในทุก edge node ได้ทันที วิธีนี้ช่วยหลีกเลี่ยงการล้างแคชทั้งหมด (full cache sweep) และทำให้เว็บไซต์ปฏิบัติตามกฎระเบียบได้อย่างถูกต้อง
บทสรุป: การทำ fragment caching ด้วย Varnish และ ESI เปลี่ยนหน้าเว็บแบบ monolithic ที่ต้องพึ่งพาฐานข้อมูลอย่างหนัก ให้กลายเป็นชุดของชิ้นส่วนที่น้ำหนักเบาและนำกลับมาใช้ใหม่ได้ ซึ่งช่วยลดภาระของ backend และค่า latency ลงอย่างมาก ในขณะที่ยังคงรักษาการแสดงผลเฉพาะบุคคลสำหรับผู้ใช้แต่ละรายไว้ได้
