PlusMagi's Blog By Pitt Phunsanit Database NGINX: แสดงผลลัพธ์ทันที

NGINX: แสดงผลลัพธ์ทันที

ในโลกของการพัฒนาแอปพลิเคชันที่ต้องจัดการกับชุดข้อมูล (Dataset) ขนาดมหึมา การเรียกใช้คำสั่ง SQL ที่ซับซ้อนและกินเวลานานอาจทำให้เกิดปัญหาด้านประสบการณ์ผู้ใช้งาน (UX) ได้อย่างมาก ผู้ใช้อาจรู้สึกว่าระบบค้างหรือ Timeout เนื่องจากฐานข้อมูลพยายามประมวลผลและส่งผลลัพธ์ทั้งหมดออกมาในครั้งเดียว

สำหรับนักพัฒนาที่ใช้ PostgreSQL การรับมือกับปัญหานี้ไม่ใช่แค่การเพิ่ม `timeout` แต่ต้องเข้าใจหลักการของการ ‘สตรีม’ (Streaming) ข้อมูล ซึ่งหมายถึงการดึงข้อมูลมาประมวลผลหรือแสดงผลเป็นส่วนๆ อย่างต่อเนื่อง แทนที่จะรอให้ชุดข้อมูลทั้งหมดพร้อมใช้งาน

💡 หลักการ Streaming Data ใน PostgreSQL

โดยพื้นฐานแล้ว เมื่อคุณรันคำสั่ง `SELECT` ทั่วไป PostgreSQL จะพยายามส่งผลลัพธ์ทั้งหมดที่พบออกมาในครั้งเดียว (Batch Processing) ซึ่งหากข้อมูลมีขนาดหลายแสนหรือหลายล้านแถว อาจทำให้เกิดปัญหา Buffer Overflow หรือการหน่วงเวลาที่ปลายทางได้

แนวคิดในการสตรีมข้อมูลจึงเป็นการจำลองกระบวนการทำงานแบบ ‘Cursor’ หรือการแบ่งชุดข้อมูลออกเป็นกลุ่มย่อยๆ (Chunking) เพื่อให้แอปพลิเคชันสามารถรับและประมวลผลข้อมูลทีละส่วนได้อย่างต่อเนื่อง ซึ่งช่วยลดภาระหน่วยความจำทั้งของเซิร์ฟเวอร์และไคลเอนต์

🛠️ วิธีที่ 1: การใช้ Cursors สำหรับการวนซ้ำ (Iteration)

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

SQL

SQL

-- ตัวอย่างการประกาศ Cursor ใน PostgreSQL (ต้องใช้ใน Transaction Block) 
BEGIN;

DECLARE large_data_cursor CURSOR FOR SELECT * FROM massive_table WHERE status = 'pending' ORDER BY id;

-- จากนั้นจึงทำการ FETCH ข้อมูลทีละชุดผ่าน Client Library (เช่น psycopg2, pgx)
FETCH NEXT FROM large_data_cursor; 
COMMIT;

(หมายเหตุ: การใช้งาน Cursor จริงๆ จะต้องจัดการที่ระดับ Application Layer โดยใช้ Driver ของภาษาโปรแกรม เช่น Python หรือ Node.js)

🛠️ วิธีที่ 2: Chunking Data ด้วย Generate Series (Best Practice)

สำหรับกรณีที่ต้องการจำลองการสตรีมข้อมูลโดยไม่จำเป็นต้องใช้ Cursor ในระดับแอปพลิเคชัน การแบ่งชุดข้อมูลออกเป็นกลุ่มย่อยๆ โดยใช้ `generate_series` ร่วมกับ `LIMIT` และ `OFFSET` เป็นวิธีที่มีประสิทธิภาพสูงและเข้าใจง่ายใน PostgreSQL

เราสามารถสร้างตัวเลขลำดับ (Sequence) เพื่อกำหนดขอบเขตของข้อมูลที่ต้องการดึงออกมาทีละชุด เช่น ชุดละ 10,000 แถว ทำให้การเรียก API หรือการประมวลผลเป็นไปอย่างต่อเนื่องเหมือนกับการสตรีมจริง

SQL

SQL

-- ตัวอย่าง: การจำลองการแบ่งข้อมูลออกเป็น Chunk ขนาด 5000 แถว
WITH chunk_generator AS (
    SELECT generate_series(1, (SELECT COUNT(*) FROM large_table)) AS row_num
),
chunked_data AS (
    SELECT 
        t.*,
        c.row_num
    FROM massive_table t
    JOIN chunk_generator c ON c.row_num <= (SELECT offset + 5000 FROM generate_series(1, 1) * 5000)
    WHERE t.id = (SELECT id FROM massive_table ORDER BY id LIMIT 1 OFFSET (c.row_num - 1)) -- Logic จำลองการดึงทีละกลุ่ม
    LIMIT 5000 
)
SELECT * FROM chunked_data;

ในตัวอย่างนี้ เราใช้ `generate_series` เพื่อสร้างลำดับของแถวที่ต้องการประมวลผล ทำให้เราสามารถจำกัดขอบเขตการดึงข้อมูล (Chunk) ได้อย่างแม่นยำและเป็นระบบ ซึ่งเลียนแบบพฤติกรรมการสตรีมได้อย่างสมบูรณ์

✅ สรุปแนวทางการปฏิบัติงาน

เมื่อต้องจัดการกับชุดข้อมูลขนาดใหญ่ใน PostgreSQL ควรหลีกเลี่ยงการเขียนคำสั่งที่ดึงผลลัพธ์ทั้งหมดออกมาพร้อมกัน (Mass Select) แต่ควรปรับไปใช้เทคนิคใดเทคนิคหนึ่งดังนี้:

  1. Application Layer: ใช้ Cursor หรือ Pagination Logic (LIMIT/OFFSET) ในโค้ดแอปพลิเคชันเพื่อเรียกข้อมูลเป็นชุดๆ
  2. Database Layer: พิจารณาการใช้ `generate_series` เพื่อสร้างขอบเขตของข้อมูลที่ต้องการประมวลผลในแต่ละรอบ

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