4 นาที

วิธีของ Brendan Gregg สำหรับความหน่วงและการโปรไฟล์

เรียนรู้วิธีปฏิบัติของ Brendan Gregg (USE, RED, flame graphs) เพื่อสอบสวนความหน่วงและคอขวดในโปรดักชันด้วยข้อมูล ไม่ใช่การเดา

วิธีของ Brendan Gregg สำหรับความหน่วงและการโปรไฟล์

ทำไมแนวทางของ Brendan Gregg ช่วยลดการเดาเดา

Brendan Gregg เป็นหนึ่งในเสียงที่มีอิทธิพลในด้านประสิทธิภาพของระบบ โดยเฉพาะในโลกของ Linux เขาเขียนหนังสือที่ถูกใช้อย่างแพร่หลาย สร้างเครื่องมือเชิงปฏิบัติ และที่สำคัญที่สุดคือแบ่งปันวิธีการชัดเจนสำหรับการสืบสวนปัญหาในโปรดักชัน ทีมงานนำแนวทางของเขาไปใช้เพราะมันใช้ได้จริงเมื่ออยู่ภายใต้ความกดดัน: เมื่อตัวชี้วัดความหน่วงพุ่งขึ้นและทุกคนต้องการคำตอบ คุณต้องการวิธีจาก “อาจเป็น X” ไปสู่ “แน่นอนว่าเป็น Y” โดยไม่เกิดละครมาก

ความหมายที่แท้จริงของ “ระเบียบวิธีด้านประสิทธิภาพ”

ระเบียบวิธีด้านประสิทธิภาพไม่ใช่เครื่องมือเดียวหรือคำสั่งที่ฉลาด มันคือวิธีการที่ทำซ้ำได้ในการสืบสวน: เช็คลิสต์ว่าต้องดูอะไรเป็นอันดับแรก จะตีความสิ่งที่เห็นอย่างไร และจะตัดสินใจทำอะไรต่อ

ความเป็นไปได้ในการทำซ้ำนี้แหละที่ช่วยลดการเดาเดา แทนที่จะพึ่งพาคนที่มีสัญชาตญาณมากที่สุด (หรือเสียงดังที่สุด) คุณทำตามกระบวนการที่สม่ำเสมอซึ่ง:

  • จำกัดปัญหาให้ลงไปยังทรัพยากร บริการ หรือเส้นทางโค้ดที่เฉพาะเจาะจง
  • วัดสิ่งที่เกิดขึ้นในช่วงเวลาเดียวกับเหตุการณ์
  • ยืนยันคอขวดด้วยหลักฐานก่อนทำการเปลี่ยนแปลง

โหมดล้มเหลวที่พบบ่อย: แก้ก่อนวัด

การสืบสวนความหน่วงมักพังในห้านาทีแรก คนมักกระโดดไปหาการแก้ไขทันที: “เพิ่ม CPU”, “รีสตาร์ทเซอร์วิส”, “เพิ่มแคช”, “ปรับ GC”, “ต้องเป็นเครือข่ายแน่ๆ” บางครั้งการกระทำนั้นช่วย—แต่บ่อยครั้งมันทำให้สัญญาณหายไป เสียเวลา หรือเพิ่มความเสี่ยงใหม่ๆ

วิธีของ Gregg ผลักดันให้คุณชะลอการทำ “วิธีแก้” จนกว่าจะตอบคำถามง่ายๆ ได้: อะไรอิ่มตัว? อะไรมีข้อผิดพลาด? อะไรช้าลง—throughput, queueing หรือการดำเนินการแต่ละรายการ?

สิ่งที่คู่มือนี้จะช่วยให้คุณทำได้

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

พื้นฐานความหน่วง: ควรวัดอะไรก่อนปรับจูน

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

Throughput, latency และ errors เคลื่อนที่ด้วยกัน

สัญญาณทั้งสามนี้เชื่อมโยงกัน:

  • Throughput (requests/second) ที่เพิ่มขึ้นสามารถเพิ่ม queueing ซึ่งทำให้ latency เพิ่มได้
  • Errors อาจ ลด ความหน่วงที่สังเกตได้ (fail เร็ว) หรือ เพิ่ม มัน (retry และ timeout)
  • การจำกัด throughput (rate limits, backpressure) อาจปรับปรุง tail latency ในขณะที่คำขอน้อยลงจะสำเร็จ

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

อย่าเชื่อค่าเฉลี่ย: เปอร์เซ็นไทล์และ tail latency

ค่าเฉลี่ยซ่อนสไปก์ที่ผู้ใช้จดจำ บริการที่มีค่าเฉลี่ย 50 ms อาจยังมีการสะดุด 2 s บ่อยครั้ง

ติดตาม เปอร์เซ็นไทล์:

  • p50: ประสบการณ์ผู้ใช้ทั่วไป
  • p95/p99: tail latency (ที่ส่วนใหญ่ของความเจ็บปวดเกิดขึ้น)

ดูรูปร่างของ latency ด้วย: p50 ที่นิ่งแต่ p99 ที่เพิ่มขึ้นมักบ่งชี้การสะดุดเป็นครั้งคราว (เช่น การแย่งล็อก, I/O hiccups, หยุดโลกของ GC) มากกว่าการชะลอตัวโดยรวม

งบประมาณความหน่วง: เวลาไปอยู่ที่ไหนได้บ้าง

งบประมาณความหน่วงคือแบบจำลองการนับง่ายๆ: “ถ้า request ต้องเสร็จใน 300 ms เวลาไปอยู่ที่ไหนได้บ้าง?” แบ่งเป็นบั๊คเก็ตเช่น:

  • เวลาในเซอร์วิสของคุณ (คำนวณ + รอ)
  • เวลาในบริการ downstream
  • เวลาในฐานข้อมูล/แคช
  • การเดินทางของเครือข่ายและ TLS
  • เวลาอยู่ในคิว (threads, connection pools, load balancers)

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

เริ่มด้วยคำถามและขอบเขตที่ชัดเจน

งานความหน่วงมักไปผิดทางเมื่อปัญหาถูกอธิบายว่า ระบบช้า วิธีของ Gregg เริ่มต้นก่อนหน้านั้น: บังคับให้ปัญหาเป็นคำถามที่ทดสอบได้เฉพาะเจาะจง

กำหนดว่าคำว่า “ช้า” หมายถึงอะไร (และกับใคร)

เขียนสองประโยคก่อนจะแตะเครื่องมือใดๆ:

  • อะไรที่ช้า? (การโหลดหน้า, API endpoint, งานแบตช์, การล็อกอิน, เช็คเอาต์, คิวรี SQL เฉพาะ)
  • ที่ไหนสังเกตเห็นความช้า? (เบราว์เซอร์ลูกค้า, แอปมือถือ, ภูมิภาคหนึ่ง, พ็อดหนึ่ง, โฮสต์หนึ่ง, บริการภายใน)

สิ่งนี้ช่วยป้องกันการปรับจูนชั้นที่ผิด—เช่น CPU โฮสต์—เมื่อความเจ็บปวดถูกจำกัดอยู่ที่ endpoint หรือบริการ downstream เฉพาะ

เลือกช่วงเวลาและขอบเขต

เลือกหน้าต่างเวลาที่ตรงกับคำร้องเรียนและรวมช่วงเปรียบเทียบ “ปกติ” ถ้าเป็นไปได้

กำหนดขอบเขตการสืบสวนอย่างชัดเจน:

  • โฮสต์ vs เซอร์วิส vs endpoint: “โหนด Kubernetes หนึ่งเครื่อง” ต่างจาก “เส้นทาง API หนึ่งเส้น”
  • ชิ้นของทราฟฟิก: ภูมิภาค, ชั้นลูกค้า, คำขอที่เกิดข้อผิดพลาดเท่านั้น, หรือทุกคำขอ
  • สัญญาณที่ผลักดันรายงาน: p95 latency, timeouts, ความลึกคิว หรือ user timing

ความแม่นยำตรงนี้ทำให้ขั้นตอนต่อไป (USE, RED, โปรไฟล์) เร็วขึ้นเพราะคุณจะรู้ว่าข้อมูลอะไร ควร เปลี่ยนหากสมมติฐานของคุณถูกต้อง

ถือว่าการเปลี่ยนแปลงล่าสุดเป็นสมมติฐาน ไม่ใช่คำตอบ

จด deploys, การเปลี่ยน config, การเปลี่ยนทราฟฟิก, และเหตุการณ์โครงสร้างพื้นฐาน—แต่ไม่สมมติความเป็นสาเหตุ เขียนเป็น “ถ้า X, เราควรคาดหวัง Y” เพื่อให้สามารถยืนยันหรือปฏิเสธได้เร็ว

เก็บบันทึกการสืบสวนแบบเบาๆ

บันทึกสั้นๆ ป้องกันการทำงานซ้ำโดยเพื่อนร่วมทีมและทำให้การส่งงานต่อราบรื่นขึ้น

Time | Question | Scope | Data checked | Result | Next step

แม้แต่ห้าบรรทัดแบบนี้ก็เปลี่ยนเหตุการณ์ตึงเครียดให้เป็นกระบวนการที่ทำซ้ำได้

วิธี USE: ตรวจสอบทรัพยากรอย่างรวดเร็วเพื่อหาคอขวด

USE Method (Utilization, Saturation, Errors) เป็นเช็คลิสต์ด่วนของ Gregg สำหรับสแกน “ทรัพยากรหลักสี่อย่าง”—CPU, หน่วยความจำ, ดิสก์ (storage), และเครือข่าย—เพื่อให้คุณหยุดเดาและเริ่มจำกัดปัญหา

มันคืออะไร: เช็คลิสต์ต่อทรัพยากร

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

  • Utilization: ตอนนี้มันยุ่งแค่ไหน?
  • Saturation: งานกำลังสะสมหรือไม่ (คิว, เวลารอ), แม้ utilization จะไม่ได้เต็ม?
  • Errors: มันล้มเหลวหรือ retry ในแบบที่สร้างความล่าช้าหรือไม่?

เมื่อประยุกต์อย่างสม่ำเสมอ นี่จะเป็นการตรวจสอบอย่างรวดเร็วว่า “แรงกดดัน” อยู่ที่ไหน

ใช้งานจริงอย่างไร

สำหรับ CPU utilization คือ % การใช้งาน CPU, saturation แสดงเป็นแรงกดดันของ run-queue หรือเธรดที่รอรัน, และ errors อาจรวมถึงการ throttling (ในคอนเทนเนอร์) หรือการขัดข้องของ interrupt

สำหรับ memory utilization คือหน่วยความจำที่ถูกใช้งาน, saturation มักปรากฏเป็น paging หรือการทำงานของ garbage collection บ่อย ๆ, และ errors รวมถึงการจัดสรรล้มเหลวหรือเหตุการณ์ OOM

สำหรับ disk utilization คือเวลาที่อุปกรณ์ยุ่ง, saturation คือความลึกคิวและเวลารออ่าน/เขียน, และ errors คือ I/O errors หรือ timeouts

สำหรับ network utilization คือ throughput, saturation คือการดรอป/คิว/latency, และ errors คือ retransmits, resets, หรือ packet loss

สัญญาณที่มีประโยชน์ที่สุดระหว่างเหตุการณ์ความหน่วง

เมื่อผู้ใช้รายงานความช้า สัญญาณ saturation มักเผยมากที่สุด: คิว เวลารอ และการแย่งชิงมักสอดคล้องกับ latency มากกว่าการใช้งานดิบ

USE เสริมเมตริกระดับบริการ (ไม่ได้มาทดแทน)

เมตริกระดับบริการ (เช่น latency ของคำขอและอัตราข้อผิดพลาด) บอกคุณถึง ผลกระทบ ในขณะที่ USE บอกคุณ จะต้องไปดูที่ไหนต่อ โดยชี้ว่าทรัพยากรใดกำลังถูกกดดัน

ลูปปฏิบัติได้คือ:

  1. ยืนยันผลกระทบต่อผู้ใช้ (Duration/Errors)
  2. รันการตรวจ USE
  3. ขยายไปยังทรัพยากรที่น่าสงสัยด้วยเครื่องมือเชิงลึก (โปรไฟล์, trace, สถิติ kernel)

วิธี RED: สัญญาณจากมุมมองบริการที่บอกผลกระทบ

RED Method ช่วยให้คุณยึดโยงกับประสบการณ์ผู้ใช้ก่อนจะลงลึกในกราฟโฮสต์

  • Rate: จำนวนคำขอต่อวินาทีที่เซอร์วิสหรือ endpoint รับมือได้
  • Errors: จำนวนคำขอที่ล้มเหลว (และความหมายของ “ล้มเหลว” สำหรับแอปของคุณ)
  • Duration: เวลาที่คำขอสำเร็จใช้ (ติดตามเป็นเปอร์เซ็นไทล์ ไม่ใช่ค่าเฉลี่ย)

ทำไม RED ช่วยให้คุณจัดลำดับความสำคัญ

RED ป้องกันไม่ให้คุณไล่ตามเมตริกที่ “น่าสนใจ” แต่ไม่กระทบผู้ใช้ มันบังคับให้เป็นวงปิดที่เข้มงวดขึ้น: endpoint ไหนช้า สำหรับผู้ใช้กลุ่มใด และตั้งแต่เมื่อไหร่? ถ้า Duration กระโดดเฉพาะเส้นทางเดียวในขณะที่ CPU รวมราบเรียบ คุณก็มีจุดเริ่มต้นที่คมชัดขึ้นแล้ว

นิสัยที่มีประโยชน์: เก็บ RED แยกตาม เซอร์วิสและ endpoints ชั้นนำ (หรือ RPC สำคัญ) เพื่อให้แยกแยะการเสื่อมสภาพทั่วไปจากการถดถอยที่จำกัดได้ง่าย

การแมปอาการ RED กับการตรวจ USE

RED บอกคุณ ที่ไหนเจ็บปวด USE ช่วยทดสอบ ทรัพยากรไหนเป็นสาเหตุ

ตัวอย่าง:

  • Duration ขึ้น + Rate คงที่ → ตรวจ saturation/queueing: run queue ของ CPU, latency ของ storage, pool การเชื่อมต่อ DB
  • Errors ขึ้น + Duration ขึ้น → ตรวจ timeouts และ retries: downstream ออเวอร์โหลด, thread pools, packet drops
  • Rate ขึ้น + Duration ขึ้น → ตรวจขีดจำกัดความจุ: การใช้งาน CPU, การทำงานของ load balancer, ความล่าช้าในการ autoscaling

แดชบอร์ด “อะไรที่เปลี่ยนไป?” ขั้นพื้นฐาน

เก็บเลย์เอาต์ที่เน้น:

  1. ภาพรวม RED: Rate, Errors, และ p50/p95/p99 Duration ของเซอร์วิส
  2. Top endpoints: สัญญาณ RED เดียวกันต่อ endpoint เรียงตามทราฟฟิกหรือ p95 แย่ที่สุด
  3. Dependencies: แผงในสไตล์ RED สำหรับ downstream สำคัญ (DB, cache, APIs ภายนอก)
  4. แถวการเชื่อมโยง: เมตริกระบบชุดเล็ก (CPU, ความกดดันหน่วยความจำ, latency I/O ดิสก์, retransmits เครือข่าย) เพื่อเร่งการขยับจากมุมมองบริการไปสู่การทดสอบสาเหตุราก

ถ้าต้องการเวิร์กโฟลว์เหตุการณ์ที่สม่ำเสมอ ให้จับคู่ส่วนนี้กับการตรวจ USE ใน /blog/use-method-overview เพื่อให้คุณย้ายจาก “ผู้ใช้รู้สึกได้” ไปเป็น “ทรัพยากรนี้คือข้อจำกัด” โดยลดการผันผวนลง

การจัดลำดับความสำคัญ: เลือกคำถามถัดไปที่ดีที่สุดที่จะถาม

ทดสอบการเปลี่ยนแปลงด้านประสิทธิภาพอย่างรวดเร็ว
ปรับใช้เวอร์ชันทดสอบและวัด p95 และ p99 ก่อนแตะระบบจริง

การสืบสวนประสิทธิภาพอาจขยายเป็นกราฟและสมมติฐานนับสิบในเวลาไม่กี่นาที กรอบความคิดของ Gregg คือทำให้แคบลง: งานของคุณไม่ใช่ “เก็บข้อมูลให้มากขึ้น” แต่เป็นการถามคำถามถัดไปที่ตัดความไม่แน่นอนได้เร็วที่สุด

กฎ 80/20 สำหรับคอขวด

ปัญหาความหน่วงส่วนใหญ่ถูกครอบงำโดยต้นทุนเดียว (หรือคู่เล็กๆ): ล็อกร้อนหนึ่งตัว, การพึ่งพาช้า, ดิสก์ออเวอร์โหลด, รูปแบบ GC หนึ่งแบบ การจัดลำดับความสำคัญคือการตามหาต้นทุนที่โดดเด่นนั้นก่อน เพราะการลด 5% จากห้าจุดต่างกันแทบจะไม่เปลี่ยนความหน่วงที่ผู้ใช้เห็นได้

การทดสอบเชิงปฏิบัติ: “อะไรที่อธิบายการเปลี่ยนแปลงความหน่วงส่วนใหญ่ที่เราเห็นได้?” ถ้าสมมติฐานให้คำอธิบายเพียงเศษเสี้ยว มันคือคำถามที่มีลำดับต่ำกว่า

แนวบนลงล่าง vs แนวล่างขึ้นบน: จะเริ่มที่ไหนดี

ใช้แนว top-down เมื่อคุณตอบคำถามว่า “ผู้ใช้ได้รับผลกระทบไหม?” เริ่มจาก endpoints (สัญญาณสไตล์ RED): latency, throughput, errors นี่ช่วยหลีกเลี่ยงการปรับจูนสิ่งที่ไม่อยู่ในเส้นทางสำคัญ

ใช้แนว bottom-up เมื่อโฮสต์ป่วยชัดเจน (อาการแบบ USE): CPU อิ่มตัว, หน่วยความจำวิ่งเพี้ยน, I/O wait ถ้าโหนดหนึ่งติดลม คุณจะเสียเวลาเพ่งที่เปอร์เซ็นไทล์ endpoint โดยไม่เข้าใจข้อจำกัด

ต้นไม้ตัดสินใจง่าย ๆ เพื่อป้องกันการวุ่นวาย

เมื่อมีการแจ้งเตือน ให้เลือกสาขาและยืนอยู่ที่นั่นจนกว่าคุณจะยืนยันหรือปฏิเสธ:

  • Latency spike + errors spike → “นี่เป็นปัญหา dependency หรือความจุ?” (timeouts, connection pool exhaustion, downstream 5xx)
  • Latency spike + CPU spike → “CPU ทำงานมีประโยชน์หรือถูกติดค้าง?” (on-CPU vs off-CPU time)
  • Latency spike + high I/O wait → “อุปกรณ์หรือไฟล์ซิสเต็มไหนคิวเต็ม?”
  • Latency spike without resource spikes → “เวลาไปอยู่ที่ไหนเพื่อรอ?” (ล็อก, scheduler, เครือข่าย, การเรียกระยะไกล)

หลีกเลี่ยงการล้นของเมตริก รักษาระบบ

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

การโปรไฟล์ในโปรดักชันโดยไม่ทำให้ระบบล่ม

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

การโปรไฟล์ตอบคำถามอะไรบ้าง

การโปรไฟล์คือเครื่องมือ “งบประมาณเวลา” แทนการถกเถียง (“เป็น DB” vs “เป็น GC”) คุณจะได้หลักฐานเช่น “45% ของตัวอย่าง CPU อยู่ใน JSON parsing” หรือ “คำขอส่วนใหญ่ถูกบล็อกบน mutex” ซึ่งจำกัดก้าวถัดไปให้เป็นการแก้ไขที่เป็นรูปธรรมหนึ่งหรือสองอย่าง

ประเภทที่ใช้ในโปรดักชันได้บ่อย

  • CPU profiling: แสดงโค้ดที่ถูกรันบน CPU
  • Off-CPU (wait) profiling: แสดงที่ที่เธรดใช้เวลาอยู่ในสถานะบล็อก (รอ I/O, scheduler, sleep, network, disk)
  • Lock profiling: แสดงการแย่งล็อก—เวลาที่เสียไปรอ locks, mutexes, read/write latches

แต่ละแบบตอบคำถามต่างกัน ความหน่วงสูงพร้อม CPU ต่ำมักชี้ไปที่ off-CPU หรือการรอล็อก มากกว่าจุดร้อนของ CPU

เสมอเปิดใช้งาน vs กดใช้งานเมื่อจำเป็น

  • การโปรไฟล์เสมอเปิด (always-on) (ต่อเนื่อง, overhead ต่ำ) ช่วยแก้ปัญหา “มันเกิดขึ้นตอนตี 3” เพราะคุณสามารถย้อนดูได้
  • กดใช้งานเมื่อจำเป็น (on-demand) เป็นการจับช่วงเป้าหมายระหว่างสไปก์ ง่ายกว่าสำหรับการเริ่มต้น แต่ต้องพร้อมสั่งจับข้อมูลอย่างรวดเร็ว

หลายทีมเริ่มจาก on-demand แล้วค่อยขยับไป always-on เมื่อเชื่อถือในความปลอดภัยและเห็นปัญหาซ้ำๆ

ความปลอดภัย: overhead, sampling, และหน้าต่างจับสั้นๆ

การโปรไฟล์ที่ปลอดภัยในโปรดักชันคือการควบคุมต้นทุน เลือก sampling (ไม่ต้องติดตามทุกเหตุการณ์), ให้หน้าต่างจับสั้น (เช่น 10–30 วินาที), และวัด overhead ใน canary ก่อน ถ้าไม่แน่ใจ ให้เริ่มด้วย sampling ความถี่ต่ำแล้วเพิ่มเมื่อตัวสัญญาณยังมีเสียงรบกวนมากเกินไป

Flame graphs: อ่านอย่างไรและหลีกเลี่ยงข้อสรุปผิดพลาด

สร้างตัวช่วยโปรไฟล์อย่างรวดเร็ว
สร้างเครื่องมือภายในเล็ก ๆ เพื่อบันทึกไทมิงและเปรียบเทียบช่วงเวลาที่แย่กับช่วงเวลาที่ดี

Flame graphs แสดงภาพเวลาที่ถูกสุ่มในช่วงโปรไฟล์ แต่ละ “กล่อง” คือฟังก์ชัน (หรือเฟรมสแตก) และแต่ละสแตกแสดงเส้นทางการเรียกที่นำไปสู่ฟังก์ชันนั้น เหมาะมากสำหรับการมองเห็นรูปแบบอย่างรวดเร็ว—แต่ไม่ได้บอกโดยอัตโนมัติว่า “บั๊กอยู่ตรงนี้”

Flame graph แสดงอะไร (และไม่แสดงอะไร)

Flame graph โดยทั่วไปจะแสดง ตัวอย่าง on-CPU: เวลาที่โปรแกรมกำลังรันบนคอร์ CPU มันสามารถเน้นเส้นทางโค้ดที่ใช้ CPU หนัก การแปลงที่ไม่มีประสิทธิภาพ การซีเรียลไลซ์มากเกินไป หรือ hotspot ที่เผา CPU จริงๆ

มันไม่แสดงโดยตรงถึงการรอดิสก์ เครือข่าย ความล่าช้าของ scheduler หรือเวลาที่บล็อกบน mutex (นั่นคือ off-CPU และต้องการการโปรไฟล์แบบต่างหาก) นอกจากนี้มันก็ไม่พิสูจน์สาเหตุสำหรับ latency ที่ผู้ใช้เห็นเว้นแต่คุณจะผูกกับอาการที่กำหนดช่วงเวลาและบริบท

อ่านความกว้างและความลึกของสแตก

  • ความกว้าง: บ่อยครั้งที่เฟรมปรากฏในตัวอย่าง กล่องกว้างมักหมายถึง “ใช้เวลา CPU มากขึ้น” แต่เฉพาะในหน้าต่างเวลาที่เลือก
  • ความลึกสแตก: ความลึกการเรียก ฟังก์ชันที่ลึกราวกับสแตกไม่ได้หมายความว่าแย่เสมอไป สิ่งที่สำคัญคือเส้นทางไหนที่โดดเด่นและสอดคล้องกับงานที่คุณสนใจ

กับดักที่พบบ่อยให้หลีกเลี่ยง

กล่องที่กว้างที่สุดน่าจะถูกโทษ แต่ถามตัวเอง: นี่คือ hotspot ที่คุณเปลี่ยนได้หรือแค่ “เวลาไปใน malloc, GC หรือ logging” เพราะปัญหาจริงอยู่ต้นน้ำ? ดูบริบทที่ขาด (JIT, inlining, symbols) ที่อาจทำให้กล่องดูเหมือนเป็นผู้ร้ายทั้งที่เป็นแค่ผู้ส่งสาร

จับคู่ flame graphs กับคำถามที่เฉพาะเจาะจง

ถือว่า flame graph เป็นคำตอบต่อคำถามที่กำหนด: endpoint ไหน, ช่วงเวลาไหน, โฮสต์ไหน, และ อะไรเปลี่ยนไป เปรียบเทียบ flame graphs “ก่อน vs หลัง” (หรือ “ปกติ vs เสีย”) สำหรับเส้นทางคำขอเดียวกันเพื่อลดเสียงรบกวนจากการโปรไฟล์

Off-CPU time: แหล่งซ่อนของความหน่วง

เมื่อ latency พุ่ง ทีมส่วนใหญ่มอง CPU% ก่อน นั่นเข้าใจได้—แต่บ่อยครั้งมันชี้ผิดทิศ บริการอาจใช้ CPU เพียง 20% แต่ยังช้ามากหากเธรดใช้เวลาส่วนใหญ่ ไม่ได้ถูกรัน

ทำไม CPU% เพียงอย่างเดียวทำให้เข้าใจผิด

CPU% ตอบคำถามว่า “ตัวประมวลผลยุ่งแค่ไหน?” แต่มันไม่ตอบว่า “คำขอของฉันเวลาไปอยู่ที่ไหน?” คำขอสามารถหยุดชะงักในขณะที่เธรดรอ บล็อก หรือถูกพักโดย scheduler

แนวคิดสำคัญ: เวลาจริงของคำขอรวมทั้ง on-CPU และ off-CPU

ผู้ร้าย off-CPU ทั่วไป

เวลาที่ไม่ได้อยู่บน CPU มักซ่อนอยู่หลังการพึ่งพาและการแย่งชิง:

  • Disk I/O: การอ่าน/เขียนแบบ synchronous, fsyncs, storage ช้า, page cache misses
  • รอเครือข่าย: DNS lookup, TCP retransmits, upstream บริการออเวอร์โหลด
  • การแย่งล็อก: เธรดบล็อกบนล็อก, reader/writer locks, allocator contention
  • Queueing: รอใน thread pools, connection pools, หรือคิวงานภายใน

อาการที่ควรเฝ้าดู

สัญญาณไม่กี่อย่างมักสอดคล้องกับคอขวด off-CPU:

  • เวลา รอคิว เพิ่มขึ้น (คำขอรอเริ่มประมวลผล)
  • จำนวน เธรด runnable เพิ่ม (มีการแข่งขันมากขึ้นเพื่อเวลา CPU)
  • I/O wait สูงขึ้นและ latency ของดิสก์/เครือข่ายยาวขึ้น

สัญญาณเหล่านี้บอกว่า “เรากำลังรอ” แต่ไม่บอกว่าเรากำลังรออะไร

การโปรไฟล์ off-CPU แสดงว่า “เวลาไปอยู่ที่ไหน” อย่างไร

การโปรไฟล์ off-CPU ให้เวลาที่จะระบุ เหตุผลที่เราไม่ได้ถูกรัน: บล็อกใน syscalls, รอล็อก, หลับ, หรือถูกยกเลิก นั่นมีพลังสำหรับงาน latency เพราะมันเปลี่ยนการชะลอตัวที่คลุมเครือให้เป็นหมวดหมู่ที่ทำได้จริง: “บล็อกบน mutex X”, “รอ read() จากดิสก์”, หรือ “ติดใน connect() ไปยัง upstream” เมื่อคุณตั้งชื่อการรอได้ คุณก็สามารถวัด ยืนยัน และแก้ได้

ยืนยันคอขวดด้วยหลักฐาน ไม่ใช่สัญชาตญาณ

งานประสิทธิภาพมักพังตรงจุดเดียวกัน: ใครบางคนเห็นเมตริกที่น่าสงสัย ประกาศว่า “นี่คือปัญหา” แล้วเริ่มปรับจูน วิธีของ Gregg ผลักดันให้คุณชะลอและ พิสูจน์ ว่าสิ่งใดจำกัดระบบก่อนเปลี่ยนแปลงใดๆ

คอขวด, hot spot, และเสียงรบกวน

คอขวด คือทรัพยากรหรือองค์ประกอบที่ตอนนี้จำกัด throughput หรือขับเคลื่อน latency หากคุณบรรเทามัน ผู้ใช้จะเห็นการปรับปรุง

hot spot คือที่ที่เวลาไป (เช่น ฟังก์ชันที่ปรากฏบ่อยในโปรไฟล์) Hot spot อาจเป็นคอขวดจริง—หรือเป็นงานที่ยุ่งแต่ไม่ได้อยู่บนเส้นทางช้า

เสียงรบกวน คือทุกอย่างที่ดูมีความหมายแต่ไม่ใช่: งานแบ็กกราวด์, สไปก์ครั้งเดียว, artifacts ของการสุ่มตัวอย่าง, ผลกระทบการแคช, หรือ “top talkers” ที่ไม่สอดคล้องกับปัญหาที่ผู้ใช้เห็น

พิสูจน์ด้วยการเปรียบเทียบและการเปลี่ยนแปลงที่ควบคุมได้

เริ่มจากการจับภาพ “ก่อน” ที่ชัดเจน: อาการที่ผู้ใช้เห็น (latency หรืออัตราข้อผิดพลาด) และสัญญาณผู้สมัครชั้นนำ (CPU saturation, ความลึกคิว, I/O ดิสก์, การแย่งล็อก ฯลฯ) แล้วทำการ เปลี่ยนที่ควบคุมได้ ที่ควรส่งผลเฉพาะต่อสาเหตุที่สงสัย

ตัวอย่างการทดสอบเชิงสาเหตุ:

  • เพิ่มความจุให้ทรัพยากรที่สงสัย (เพิ่ม worker, เพิ่ม CPU shares, เพิ่ม connection pool) แล้วดูว่า latency ดีขึ้นหรือไม่
  • ลดความต้องการชั่วคราว (จำกัด endpoint ที่ส่งเสียงดัง, เล่นโหลดที่น้อยลง) แล้วดูว่าข้อจำกัดผ่อนลงไหม

ความสอดคล้องเป็นแค่เบาะแส ไม่ใช่คำตัดสิน ถ้า “CPU ขึ้นเมื่อ latency ขึ้น” ให้ยืนยันด้วยการเปลี่ยนแปลงความพร้อมใช้งาน CPU หรือการลดงาน CPU แล้วสังเกตว่า latency เป็นไปตามหรือไม่

บันทึกสิ่งที่คุณพิสูจน์ได้

เขียนลงว่า: วัดอะไร เปลี่ยนแปลงอะไรอย่างแม่นยำ ผลก่อน/หลัง และการปรับปรุงที่สังเกต นี่จะเปลี่ยนชัยชนะครั้งเดียวให้เป็นเพลย์บุ๊กที่ใช้ซ้ำได้สำหรับเหตุการณ์ถัดไป—และป้องกันไม่ให้ “สัญชาตญาณ” เขียนประวัติใหม่ในภายหลัง

สร้างเวิร์กโฟลว์ที่ทำซ้ำได้สำหรับเหตุการณ์ด้านประสิทธิภาพ

ทำการเปลี่ยนทีละอย่าง
ใช้สแนปช็อตเพื่อให้การทดลองแบบตัวแปรเดียวปลอดภัยขณะไล่คอขวด

เหตุการณ์ด้านประสิทธิภาพรู้สึกเร่งด่วน ซึ่งเป็นเวลาที่การเดาเดาแทรกซึมได้ง่าย เวิร์กโฟลว์เบา ๆ ที่ทำซ้ำได้ช่วยให้คุณย้ายจาก “บางอย่างช้า” เป็น “เรารู้ว่าอะไรเปลี่ยนไป” โดยไม่วุ่นวาย

วงจรเหตุการณ์: ตรวจจับ → ระบุ → วัด → แก้ไข

Detect: แจ้งเตือนจาก latency และอัตราข้อผิดพลาดที่ผู้ใช้เห็น ไม่ใช่แค่ CPU แจ้งหน้าเมื่อ p95/p99 ข้ามเกณฑ์เป็นเวลาต่อเนื่อง

Triage: ตอบทันทีสามคำถาม: อะไรช้า, เริ่มเมื่อไหร่, ใครได้รับผลกระทบ? ถ้าคุณบอกขอบเขต (เซอร์วิส, endpoint, ภูมิภาค, cohort) ไม่ได้ คุณยังไม่พร้อมจะปรับจูน

Measure: เก็บหลักฐานที่จำกัดคอขวด ชอบการจับแบบมีขอบเขตเวลา (เช่น 60–180 วินาที) เพื่อให้เปรียบเทียบ “ไม่ดี” กับ “ดี” ได้

Fix: เปลี่ยนครั้งละอย่าง แล้ววัดสัญญาณเดิมเพื่อยืนยันการปรับปรุงและหักล้างผลลวงตา

มาตรฐานชุดกราฟเล็กๆ

เก็บแดชบอร์ดร่วมที่ทุกคนใช้ตอนเหตุการณ์ ทำให้มันน่าเบื่อและสม่ำเสมอ:

  • Latency: p50 / p95 / p99 (ต่อ endpoint สำคัญ)
  • สัญญาณ RED: Rate, Errors, Duration (มุมมองจากเซอร์วิส)
  • เมตริก USE บางตัว: utilization, saturation, errors สำหรับ CPU, ดิสก์, เครือข่าย

เป้าหมายไม่ใช่การกราฟทุกอย่าง แต่เพื่อลดเวลาไปสู่ข้อเท็จจริงแรก

กำหนด “สัญญาณทอง” ต่อ endpoint สำคัญ

ติดตั้งเครื่องมือตรวจสอบสำหรับ endpoints ที่สำคัญที่สุด (checkout, login, search) ไม่ใช่ทุก endpoint สำหรับแต่ละอัน ให้ตกลง: p95 ที่คาดหวัง, อัตราข้อผิดพลาดสูงสุด, และ dependency สำคัญ (DB, cache, third-party)

ตกลงว่าจะจับอะไรระหว่างเหตุการณ์

ก่อนเกิด outage ครั้งต่อไป ให้ตกลงชุดการจับ:

  • โปรไฟล์ (CPU และ off-CPU), พร้อม flame graphs
  • Traces สำหรับ endpoints ที่ช้า
  • Logs สำหรับ errors/timeouts (sampled)

บันทึกไว้ใน runbook สั้นๆ (เช่น /runbooks/latency), รวมถึงใครสามารถเรียกการจับและที่เก็บ artifacts

Koder.ai เข้ากับเวิร์กโฟลว์สไตล์ Gregg อย่างไร

วิธีของ Gregg เน้นที่ การเปลี่ยนแปลงที่ควบคุมได้และการยืนยันอย่างรวดเร็ว หากทีมคุณสร้างเซอร์วิสโดยใช้ Koder.ai (แพลตฟอร์มแชทสำหรับสร้างและวน iterate เว็บ, แบ็กเอนด์, และแอปมือถือ) ฟีเจอร์สองอย่างเชื่อมโยงกับแนวคิดนี้ได้ดี:

  • Planning Mode ช่วยเปลี่ยน “อาจเป็น X” ให้เป็นสมมติฐานชัดเจนและชุดการเปลี่ยนแปลงขนาดเล็กที่ทดสอบได้ก่อนแตะโปรดักชัน
  • Snapshots and rollback รองรับการทดลองตัวแปรเดียวอย่างปลอดภัย: ทำการเปลี่ยนแปลงหนึ่งอย่าง วัด RED/USE แล้วย้อนกลับอย่างรวดเร็วถ้าหลักฐานบอกว่า “ไม่ใช่”

แม้คุณจะไม่ได้สร้างโค้ดใหม่ระหว่างเหตุการณ์ นิสัยเหล่านั้น—diffs เล็ก ๆ ผลลัพธ์ที่วัดได้ และการย้อนกลับเร็ว—คือสิ่งที่ Gregg ส่งเสริม

การเดินผ่านเชิงปฏิบัติ: จากสไปก์ความหน่วงสู่วิธีแก้ที่ยืนยันแล้ว

สถานการณ์: p99 พุ่งตอนทราฟฟิกพีค

เวลา 10:15 น. แดชบอร์ดของคุณแสดง p99 latency ของ API พุ่งจาก ~120ms เป็น ~900ms ในช่วงทราฟฟิกพีค อัตราข้อผิดพลาดคงที่ แตาลูกค้ารายงานคำขอ “ช้า”

ขั้นตอน 1 — เริ่มด้วย RED เพื่อหาผลกระทบผู้ใช้

เริ่มจากมุมมองเซอร์วิส: Rate, Errors, Duration

คุณแยก Duration ตาม endpoint และเห็นเส้นทางหนึ่งโดดเด่นที่ p99: POST /checkout Rate ขึ้น 2×, errors ปกติ, แต่ Duration พุ่งเฉพาะตอนที่ความขนานเพิ่มขึ้น นั่นชี้ไปที่ การคิวหรือการแย่งชิง, ไม่ใช่ความล้มเหลวโดยตรง

จากนั้นตรวจดูว่า latency เป็นเวลาประมวลผลหรือเวลารอ: เปรียบเทียบ “handler time” ของแอปกับเวลาคำขอทั้งหมด (หรือ upstream vs downstream spans ถ้ามี tracing) Handler time ต่ำ แต่เวลาทั้งหมดสูง—คำขอกำลังรอ

ขั้นตอน 2 — ใช้ USE กับโฮสต์ที่สงสัย

สำรวจคอขวดที่เป็นไปได้: Utilization, Saturation, Errors ของ CPU, memory, disk, และ network

การใช้งาน CPU ประมาณ 35% แต่ run queue และ context switches เพิ่มขึ้น ดิสก์และเครือข่ายดูนิ่ง ความไม่ตรงกันนี้ (CPU% ต่ำ แต่รอสูง) เป็นเบาะแสคลาสสิก: เธรดไม่ได้เผา CPU—พวกมันถูกบล็อก

ขั้นตอน 3 — เลือกการโปรไฟล์ตามอาการ

  • ถ้า CPU สูง: ใช้ CPU profiling (on-CPU flame graphs) เพื่อดูว่าเวลาไปอยู่ที่โค้ดไหน
  • ถ้าคำขอกำลังรอ: ใช้ off-CPU profiling เพื่อดูว่าเธรดบล็อกที่ไหน (ล็อก, I/O, scheduling)

คุณจับ off-CPU profile ระหว่างสไปก์และพบเวลามากใน mutex รอบแคช “promotion validation” ที่แชร์กัน

ขั้นตอน 4 — แก้แล้วยืนยัน

คุณเปลี่ยน global lock เป็น per-key lock (หรือทางอ่านแบบ lock-free), ปรับใช้ และสังเกต p99 กลับสู่ปกติขณะที่ Rate ยังคงสูง

เช็คลิสต์หลังเหตุการณ์:

  • บันทึกอาการ RED ที่แน่นอนและ endpoint ที่ระบุ
  • เก็บโปรไฟล์และหน้าต่างเวลาที่จับได้
  • เพิ่มการแจ้งเตือนสำหรับสัญญาณ saturation เฉพาะ (เช่น lock wait / run queue)
  • จดคำถาม “ขั้นต่อไปที่จะถาม” ถ้ามันเกิดขึ้นอีก

คำถามที่พบบ่อย

ควรเริ่มตรวจสอบเวลาแฝงที่พุ่งสูงอย่างไร?

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

อะไรเป็นสาเหตุที่พบบ่อยของเวลาแฝงสูงในแอปพลิเคชัน?

เวลาแฝงบอกว่าผู้ใช้ต้องรอนานขึ้น การแย่งใช้ CPU การรอล็อก การเข้าคิว I/O ของดิสก์ ความล่าช้าของเครือข่าย การเก็บกวาดหน่วยความจำ หรือการพึ่งพาที่ช้า อาจเป็นสาเหตุของการรอนั้น

เหตุใดจึงควรติดตาม p95 และ p99 แทนเวลาแฝงเฉลี่ย?

ค่าเฉลี่ยซ่อนช่วงที่ระบบค้างสั้น ๆ แต่สร้างความเจ็บปวด ติดตาม p50 เพื่อดูประสบการณ์ทั่วไป และ p95 หรือ p99 เพื่อจับคำขอที่ช้าซึ่งผู้ใช้สังเกตเห็นระหว่างเหตุขัดข้อง

วิธี RED คืออะไร?

RED หมายถึง Rate, Errors และ Duration ใช้เพื่อค้นหาว่าบริการหรือเอนด์พอยต์ใดส่งผลต่อผู้ใช้ รับทราฟฟิกมากเพียงใด และคำขอล้มเหลวหรือช้าลงหรือไม่

วิธี USE คืออะไร?

USE หมายถึง Utilization, Saturation และ Errors ตรวจสอบสัญญาณทั้งสามสำหรับ CPU หน่วยความจำ ดิสก์ และเครือข่าย เพื่อค้นหาทรัพยากรที่กำลังรับภาระหนัก

RED และ USE ทำงานร่วมกันอย่างไร?

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

ควรใช้การทำโปรไฟล์ CPU เทียบกับการทำโปรไฟล์นอก CPU เมื่อใด?

ใช้การทำโปรไฟล์ CPU เมื่อการใช้ CPU เพิ่มขึ้นและคุณต้องดูว่าโค้ดใดทำงานบ่อยที่สุด ใช้การทำโปรไฟล์นอก CPU เมื่อคำขอต้องรอแม้ใช้ CPU ไม่มาก โดยเฉพาะกรณีล็อก I/O คิว หรือการเรียกใช้ระยะไกล

กล่องที่กว้างในกราฟเปลวไฟหมายความว่าอย่างไร?

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

จะพิสูจน์ได้อย่างไรว่าพบคอขวดที่แท้จริงแล้ว?

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

Koder.ai ช่วยสนับสนุนการทดลองด้านประสิทธิภาพได้อย่างไร?

Planning Mode สามารถเปลี่ยนสาเหตุที่คาดไว้ให้เป็นการปรับเปลี่ยนเล็ก ๆ ที่ทดสอบได้ ขณะที่สแนปช็อตและการย้อนกลับช่วยให้ย้อนการทดลองนั้นได้ง่ายขึ้น ตรวจสอบสัญญาณ RED และ USE อีกครั้งหลังการเปลี่ยนแปลงแต่ละครั้ง

Related posts