เป็นการกลับมาเจอกันอีกครั้งกับ CI ( Codeigniter ) งานที่ได้รับมอบหมายคือ ให้เขียนสคริปสร้าง XML Feed จากฐานข้อมูลที่มีจำนวนข้อมูลเยอะๆ ยอมรับเลยครับว่านี่เป็นครั้งแรกที่ได้จับงานแนวๆ นี้ ก็จัดไปครับ ทำการเชื่อมตารางดึงข้อมูลมาทีเดียว แล้วยัดลง SimepleXML แล้วจัดเรียงข้อมูลนิดหน่อย ก่อนนำไปแสดงผลบน Browser ปรากฏว่า บรึ้ม โกโก้ครั้น ใช้หน่วยความจำเยอะจน Server น็อคเอาท์ ละทีนี้ทำไงละ ก็ลองงมๆ ไปอ่านเจอบทความตามลิ้งค์ด้านล่างเข้า
http://narhinen.net/2011/01/15/Serving-large-xml-files.html
http://codeinthehole.com/writing/creating-large-xml-files-with-php/
ก็แนะนำให้ลองใช้ XMLWriter ที่มีการ Flush ข้อมูลได้เพื่อคืนหน่วยความจำให้หลังจากแสดงผล ก็ลองนำไปประยุกต์ดู ปรากฏว่ายังไม่ได้ ความจริงแล้วตามบทความข้างต้นที่แปะไว้สามารถใช้งานได้นะครับ แต่สำหรับ Server ปกติที่ไม่ได้มีการเรียกข้อมูลตลอดเวลาซึ่ง ลองกับเครื่องตัวเองสามารถดึงเอาข้อมูลจำนวนมากไว้ยัดมาไว้ใน XML ได้สำเร็จ
แต่ว่าพอเอาขึ้น Server จริงๆ ที่มีคนใช้งานนับพันเท่านั้นแหละครับ Server Down!! เลย เนื่องจาก Server ที่มีอยู่มีการทำ XML Feed อยู่ด้วยจึงทำให้เกิดการอ่าน และเขียนไฟล์ตลอดเวลา แล้วผมก็ใช้ XMLWriter เป็นการสร้างไฟล์ และแสดงผลโดยการดึงเอาแรมมาใช้ ซึ่งใช้เยอะมาก
สุดท้าย เรื่องราวก็ไปจบลงตรงที่ "สูงสุดคืนสู่สามัญ" การทำอะไรที่ล้ำเกินไปก็ไช่ว่าจะดี ผมไปอ่านเจอบทความนี้เข้าซึ่งตอนแรกเคยเปิดเจอแล้วก็ไม่ได้สนใจ หรือตั้งใจอ่านดีๆ
http://stackoverflow.com/questions/13536032/generating-an-xml-file-with-large-amounts-of-data-and-avoid-memory-cap
บทสรุปของการทำ XML ด้วย PHP เลยมาจบลงที่ตรงนี้ ผมนี่ถึงกับ ร้องเลยว่าเสียเวลาไป 3 วันงม ไปหาว่าจะไปเพิ่มแรม หรือกำหนดค่าอย่างไรให้ Server รันสคริปได้โดยไม่น็อคซึ่งความจริงแล้วเป็นไปได้ยาก และก็เป็นเงื่อนไขบังคับจากหัวหน้าว่าห้ามตั้งค่า Server เพราะมันไม่ไช่สิ่งที่ถูกต้อง
ซึ่งก็จะได้โค้ดแบบคร่าวๆ หน้าตาประมาณนี้ครับ
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class XMLFeed | |
{ | |
private $xmlHeader = '<products>'; | |
private $xmlFooter = '</products>'; | |
private $xmlFeedFile = 'products.xml'; | |
public function feed() | |
{ | |
$offeset = 0; | |
$perLoop = 1000; | |
$xmlFile = fopen($this->xmlFeedFile, 'w'); | |
fwrite($xmlFile, $this->xmlHeader); | |
for ($i = 0; $i < 20; $i++) | |
{ | |
$offeset = $i * $perLoop; | |
$sql = "SELECT id, name, price, category FROM product " | |
. "LEFT JOIN category ON product.category_id = category.id " | |
. "LIMIT " . $offeset . "," . $perLoop; | |
$query = mysql_query($sql); | |
$products = mysql_fetch_assoc($query); | |
foreach ($products as $product) | |
{ | |
$xml = '<product>'; | |
$xml .= '<id>' . $product->id . '</id>'; | |
$xml .= '<name>' . $product->name . '</name>'; | |
$xml .= '<price>' . $product->price . '</price>'; | |
$xml .= '<category>' . $product->category . '</category>'; | |
$xml .= '</product>'; | |
} | |
fwrite($xmlFile, $xml); | |
} | |
fwrite($xmlFile, $this->xmlFooter); | |
fclose($xmlFile); | |
} | |
} |
- เราไม่จำเป็นต้อง Query ทุกข้อมูลภายในครั้งเดียวเสมอไป ซึ่งบางครั้งจำนวนข้อมูลก็มีมากเกินไป เราสามารถลดทอนได้ด้วยการใช้ Limit ตัดเอาข้อมูลบางช่วงมาทำงานก่อนจากนั้น จึงค่อยๆ ทำในส่วนต่อไปจนถึงข้อมูลชุดสุดท้าย ทำแบบนี่จะช่วยลดการใช้งาน Ram ของ Server
- ผมเสียเวลางม และจมอยู่กับการที่ไปแก้ไขเรื่องหน่วยความจำ และเวลาในการเรียกข้อมูลเพื่อรองรับการรันสคริปในระยะเวลานานๆ ซึ่งแท้จริงนั่นไม่ไช่ประเด็นเลย แต่ที่ควรทำ คือ การเขียนอย่างไรให้ไม่ใช้หน่วยความจำมากเกินไป ซึ่งจะได้ตามคำแนะนำในบทความจากลิ้งค์ที่ 3
- อย่ายึดติดกับการใช้ประโยชน์จาก PHP ฟังก์ชันมากเกินไป บางทีไม่ได้ศึกษาให้เข้าใจว่ามันมีการใช้หน่วยความจำเยอะหรือไม่? ซึ่งท้ายที่สุด ก็จบด้วยการ echo string ออกมาแบบธรรมดาเลย
ก็เหมือนกับทุกครั้ง สิ่งที่ผมนำมาแชร์นั้น ก็เปรียบเสหมือนเรื่องเล่าประจำวัน อาจจะไม่ได้ถูกที่สุด 100% สามารถโต้แย้งกันได้ตลอดเวลาครับ ผิดพลาดประการใดก็ฝากมา ณ ที่นี้ด้วยครับ
ความคิดเห็น
แสดงความคิดเห็น