☕ 5-Min Tech Story #27

The BigKey Trap: ข้อมูลแค่ Key เดียวใน Redis ทำไมถึงทำให้ทั้งเซิร์ฟเวอร์กลายเป็นอัมพาต?

⏱️ ใช้เวลาอ่านประมาณ: 4 นาที 20 วินาที

ในบ่ายวันหนึ่ง ระบบแคช Redis ของคุณเริ่มมีอาการแปลกๆ: Response Time พุ่งสูงสลับกับ Timeout เป็นพักๆ ทั้งที่จำนวนผู้ใช้งานไม่ได้เพิ่มขึ้นเลยสักคน

พอเปิดดู CPU Usage กลับพบว่าพุ่งแตะ 100% เต็มคอร์... มีคนสงสัยว่าโดนโจมตีหรือเปล่า? แต่ความจริงกลับตลกร้ายกว่านั้น: ต้นเหตุเกิดจากทีม Marketing เพิ่งสั่งลบ Key บัญชีดำผู้ใช้ที่ไม่ได้ใช้งาน (List ขนาด 500,000 สมาชิก) ทิ้งด้วยคำสั่งสั้นๆ แค่ DEL blacklist_users!

แค่คำสั่ง DEL คำสั่งเดียว ทำไมถึงมีพลังทำลายล้างขนาดหยุดหายใจทั้งระบบได้?

🧵 พ่อครัวคนเดียวกับภาระระดับภูเขาไฟ

เราเรียนรู้กันไปแล้วว่า Redis ทำงานแบบ Single-Threaded Event Loop นั่นแปลว่าทุกคำสั่งต้องเข้าคิวรับบริการจากพ่อครัวคนเดียวกันทีละคน

สิ่งที่หลายคนมักลืมไปคือ คำว่า BigKey ไม่ได้แปลว่าตัวหนังสือชื่อ Key ยาว แต่หมายถึง "ขนาดของ Value ที่ผูกอยู่กับ Key นั้นมีขนาดมหึมา":

"เมื่อคุณสั่ง DEL blacklist_users Redis จะต้องทำหน้าที่คืนหน่วยความจำ (Memory Deallocation) ของทั้ง 500,000 ก้อนนั้นพร้อมกันใน Thread หลักทันที... พ่อครัวจึงต้องยืนก้มหน้าแกะสลักหน่วยความจำตัวนี้คนเดียว 2 วินาทีเต็ม โดยที่คำขออื่นทั้งหมดต้องยืนรอคิวแข็งเป็นหิน!"

💣 กับดักที่มองไม่เห็น: Network Spikes และ Cluster Skew

ความวิบัติของ BigKey ยังแอบซ่อนอยู่อีก 2 มิติที่คนมักนึกไม่ถึง:

1. ท่อเน็ตแตก (Network Bandwidth Saturation):

ถ้า Key ขนาด 20 MB ถูกเรียกอ่านพร้อมกันแค่ 50 ครั้งต่อวินาที ปริมาณ Bandwidth ขาออกจะพุ่งแตะ 1 GB/s (8 Gbps) การ์ดแลนของเซิร์ฟเวอร์จะตันสนิททันที กลายเป็นปัญหาคอขวดเน็ตเวิร์กที่ลากแอปอื่นล่มตามไปด้วย

2. คลัสเตอร์เอียงกระเท่เร่ (Cluster Data Skew):

ใน Redis Cluster ข้อมูลจะถูกกระจายเก็บตาม Slot ด้วยการแฮชชื่อ Key... ถ้าใน Slot หนึ่งบังเอิญมี BigKey ก้อนยักษ์ตกอยู่ โหนดนั้นจะกินแรมมหาศาลอยู่เครื่องเดียว จน RAM เต็มและเริ่มเตะข้อมูลอื่นทิ้ง (Eviction) ในขณะที่โหนดอื่นว่างงานสบายใจ

🛡️ ท่ารอดชีวิต: UNLINK และการหั่นก้อนข้อมูล

ถ้าเจอปัญหานี้เข้าให้แล้ว มีวิธีรับมือ 2 สเต็ป:

1. อย่าใช้ DEL ให้เปลี่ยนมาใช้ UNLINK:

// ปลดการเชื่อมโยงทันที แล้วส่งงานลบแรมไปทำแบบ Asynchronous ข้างหลัง
redis-cli> UNLINK big_key_name

คำสั่ง UNLINK (มีตั้งแต่ Redis 4.0 เป็นต้นมา) จะถอด Key ออกจากระบบทันทีในเสี้ยวนาโนวินาที ส่วนการเคลียร์พื้นที่ใน RAM จริงๆ จะโยนให้ Background Thread ค่อยๆ เก็บกวาด โดยไม่รบกวน Main Thread เลยแม้แต่น้อย

2. หั่น Key ให้เล็กลง (Sharding / Bucketing):

แทนที่จะเก็บผู้ใช้ 1,000,000 คนลงใน Hash เดียวชื่อ users ให้แบ่งเป็น Bucket ย่อยๆ ตาม ID เช่น users:bucket_001, users:bucket_002 โดยคุมให้แต่ละก้อนมีสมาชิกไม่เกิน 5,000 ชิ้นเสมอ

💡 5-Min Takeaway

"ในระบบ In-Memory ที่เร็วที่สุด ขนาดของชิ้นข้อมูลคือตัวแปรที่อันตรายที่สุด" ท่องกฎเกณฑ์ความปลอดภัยไว้เสมอ: พยายามคุมให้ String มีขนาดไม่เกินหลักสิบ KB และ Collection มีสมาชิกไม่เกินหลักพันตัว หมั่นสแกนหาตัวป่วนด้วยคำสั่ง redis-cli --bigkeys และฝึกใช้ UNLINK ให้ติดเป็นนิสัย เพื่อไม่ให้ก้อนหินเพียงก้อนเดียวมาขวางทางไหลของแม่น้ำทั้งสาย