PlusMagi's Blog By Pitt Phunsanit Backend,Database PHP: การจัดการไฟล์ขนาดใหญ่ด้วย Stream (เช่น fopen, fgets) แทน file_get_contents

PHP: การจัดการไฟล์ขนาดใหญ่ด้วย Stream (เช่น fopen, fgets) แทน file_get_contents

ในโลกของการพัฒนาเว็บแอปพลิเคชันระดับองค์กร ข้อมูลจากผู้ใช้อาจมาในรูปแบบของไฟล์ที่มีขนาดมหาศาล เช่น ไฟล์ Log ที่บันทึกกิจกรรมตลอดวัน, ชุดข้อมูล CSV ขนาดหลายร้อยเมกะไบต์ หรือ Dump ฐานข้อมูล การจัดการกับไฟล์เหล่านี้อย่างมีประสิทธิภาพและเสถียรภาพจึงเป็นทักษะที่สำคัญระดับสูงสำหรับ Senior Developer ที่ต้องเชื่อมต่อกับระบบฐานข้อมูลอย่าง PostgreSQL


💡 บทนำ: ปัญหา Memory Exhaustion เมื่อจัดการไฟล์ขนาดใหญ่

เมื่อเราใช้ฟังก์ชันมาตรฐานอย่าง file_get_contents() หรือการอ่านไฟล์ทั้งหมดเข้าสู่หน่วยความจำในรูปแบบของ string ตัวแปร PHP จะต้องจัดสรรพื้นที่ RAM ขนาดเท่ากับขนาดของไฟล์นั้นๆ หากไฟล์มีขนาดใหญ่มาก (เช่น หลาย GB) การทำเช่นนี้จะนำไปสู่ภาวะที่เรียกว่า Memory Exhaustion ทำให้สคริปต์หยุดทำงานด้วยข้อผิดพลาดร้ายแรง (Fatal Error: Allowed memory size of X bytes exhausted) ซึ่งเป็นข้อจำกัดที่ระบบปฏิบัติการกำหนดไว้


🚀 File Streaming คืออะไร? หลักการทำงานที่เหนือกว่า

File Streaming คือกลไกการอ่านและประมวลผลข้อมูลจากไฟล์ ทีละส่วน (Chunk by Chunk) หรือ ทีละบรรทัด (Line by Line) โดยไม่จำเป็นต้องโหลดเนื้อหาทั้งหมดของไฟล์เข้ามาเก็บไว้ในหน่วยความจำพร้อมกัน (In-memory) ในขณะใดขณะหนึ่ง สิ่งนี้ช่วยให้สคริปต์สามารถจัดการกับไฟล์ที่มีขนาดใหญ่เกินกว่าขีดจำกัดของ RAM ที่ระบบกำหนดได้อย่างมั่นคงและมีประสิทธิภาพ

ในทางปฏิบัติ เราจะใช้ฟังก์ชัน fopen() เพื่อเปิดไฟล์ และใช้ fgets() ในการอ่านข้อมูลทีละบรรทัด ซึ่งเหมาะอย่างยิ่งสำหรับการนำเข้าข้อมูล CSV จำนวนมากเข้าสู่ตารางของ PostgreSQL

⚙️ ตัวอย่างโค้ด: การ Stream ข้อมูลเข้า PostgreSQL

สมมติว่าเรามีไฟล์ CSV ขนาดใหญ่ชื่อ data.csv ที่มีข้อมูล ‘user_id,username,email’ และเราต้องการนำเข้าข้อมูลนี้เข้าสู่ตาราง user_data ใน PostgreSQL โดยใช้ Streaming:

SQL

// 1. เปิดไฟล์ในโหมดอ่าน (r) และเตรียมการเชื่อมต่อ PostgreSQL (PDO)<0x0D>$file_handle = fopen('data.csv', 'r');<0x0D>$pdo = new PDO('pgsql:host=localhost;dbname=mydatabase', 'user', 'pass');<0x0D>

// 2. ตรวจสอบว่าเปิดไฟล์ได้หรือไม่<0x0D>if ($file_handle === false) {<0x0D>die('ไม่สามารถเปิดไฟล์ได้');}<0x0D>

// 3. อ่านทีละบรรทัด<0x0D>while (($row = fgets($file_handle)) !== false) {<0x0D>
    // ลบช่องว่างและขึ้นบรรทัดใหม่
    $row = trim($row);
    if (empty($row)) continue; // ข้ามบรรทัดว่าง<0x0D>

    // แยกค่า (สมมติว่าคั่นด้วย comma)<0x0D>    list($user_id, $username, $email) = explode(',', $row);<0x0D>

    // 4. เตรียมและรันคำสั่ง SQL สำหรับ PostgreSQL<0x0D>try {<0x0D>    $stmt = $pdo->prepare("INSERT INTO user_data (user_id, username, email) VALUES (?, ?, ?)");
    $stmt->execute([$user_id, $username, $email]);
} catch (PDOException $e) {<0x0D>    // จัดการข้อผิดพลาดในการแทรกข้อมูล<0x0D>    echo "Error processing row: " . $e->getMessage() . "\n";
}
}

// 5. ปิดไฟล์เมื่อประมวลผลเสร็จสิ้น<0x0D>fclose($file_handle);<0x0D>echo "การนำเข้าข้อมูลเสร็จสมบูรณ์ด้วย Streaming.";

✨ สรุปและข้อได้เปรียบทางสถาปัตยกรรม

การใช้ File Streaming ด้วย fopen() และ fgets() แทนการโหลดไฟล์ทั้งหมดด้วย file_get_contents() ไม่เพียงแค่ช่วยป้องกันข้อผิดพลาด Memory Exhaustion เท่านั้น แต่ยังเป็นแนวทางปฏิบัติที่ดีที่สุด (Best Practice) ในการออกแบบระบบที่ต้องรับมือกับข้อมูลขนาดใหญ่ (Big Data Ingestion) ในระดับ Enterprise Architecture

ด้วยการประมวลผลข้อมูลแบบ Stream ทำให้ทรัพยากรหน่วยความจำของ PHP ถูกใช้ไปเพียงเล็กน้อยในแต่ละรอบการวนลูป (Iteration) ทำให้สคริปต์มีความเสถียรสูง สามารถรันได้ยาวนานขึ้น และเหมาะอย่างยิ่งสำหรับการทำ ETL (Extract, Transform, Load) ข้อมูลจำนวนมหาศาลเข้าสู่ PostgreSQL ได้อย่างมีประสิทธิภาพสูงสุด

Exit mobile version