การใช้ Loop สำหรับ Insert ข้อมูลปริมาณมากให้ทำงานได้เร็ว

แชร์ ความรู้ในการ พัฒนา Joomla Component Extension Module Plugin

Moderator: mindphp, ผู้ดูแลกระดาน

ภาพประจำตัวสมาชิก
tsukasaz
PHP VIP Members
PHP VIP Members
โพสต์: 6350
ลงทะเบียนเมื่อ: 18/04/2012 9:39 am

การใช้ Loop สำหรับ Insert ข้อมูลปริมาณมากให้ทำงานได้เร็ว

โพสต์โดย tsukasaz » 24/07/2015 2:08 pm

:idea:

สำหรับตัวอย่างจะใช้รูปแบบโค้ดของ Joomla โดยได้สร้างตารางแบบง่ายๆ เตรียมไว้แล้ว เก็บแค่ 2 ฟิลด์ คือ
id เก็บตัวเลขรันไปเรื่อยๆ
md5 เก็บข้อความ


การเขียน Insert ใน Joomla

โค้ด: เลือกทั้งหมด

$db    = JFactory::getDbo();
$query    = $db->getQuery(true);

$query->insert('#__helloworld')->columns(array('md5'))->values($db->q(md5($i)));

$db->setQuery($query);
$db->execute();

ถ้าเทียบเป็น sql จะได้

โค้ด: เลือกทั้งหมด

INSERT INTO `ชื่อตาราง`(`id`, `name`) VALUES (1, 'test')



แล้วถ้าจะ Insert ข้อมูล 5 แถว

โค้ด: เลือกทั้งหมด

$db    = JFactory::getDbo();
$query    = $db->getQuery(true);

$query->insert('#__helloworld')->columns(array('md5'));
for(
$i = 1; $i <= 5; $i++)
{
    $query->values($db->q(md5($i)));
}

$db->setQuery($query);
$db->execute();

ถ้าเทียบเป็น sql จะได้

โค้ด: เลือกทั้งหมด

INSERT INTO `ชื่อตาราง`(`id`, `name`) VALUES (1,'test'),(2,'test'),(3,'test'),(4,'test'),(5,'test')

ความเร็ว

โค้ด: เลือกทั้งหมด

0.000 seconds (0.000); 8.45 MB (8.447) - Start Insert
0.054 seconds (0.054); 8.47 MB (0.019) - End Insert


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

ลอง Insert ข้อมูล 100000 แถว

โค้ด: เลือกทั้งหมด

$db    = JFactory::getDbo();
$query    = $db->getQuery(true);

$query->insert('#__helloworld')->columns(array('md5'));
for(
$i = 1; $i <= 100000; $i++)
{
    $query->values($db->q(md5($i)));
}

$db->setQuery($query);
$db->execute();

ผลที่เกิด

โค้ด: เลือกทั้งหมด

0.000 seconds (0.000); 8.45 MB (8.447) - Start Insert

Fatal error: Maximum execution time of 300 seconds exceeded in ...


จาก Error คือมันใช้เวลาประมวลผลนานเกินกำหนด

หากจะแก้ไขปัญหาโดยไปแก้ไข config ให้ประมวลผลได้นานขึ้น มันไม่ใช่วิธีที่ดีเท่าไหร่

การแก้ไขปัญหาที่ดีกว่าไปแก้ไข config ก็คือการแบ่งการประมาลผลออกเป็นส่วนๆ

100000 แถว แบ่งเป็นครั้งละ 10000 แถว

โค้ด: เลือกทั้งหมด

$db    = JFactory::getDbo();
$query    = $db->getQuery(true);

$query->insert('#__helloworld')->columns(array('md5'));
for(
$i = 1; $i <= 100000; $i++)
{
    $query->values($db->q(md5($i)));
    if(($i % 10000) == 0)
    {
        $db->setQuery($query);
        $db->execute();

        $query->clear()->insert('#__helloworld')->columns(array('md5'));
    }
}

ผลที่ได้ไม่ Error

โค้ด: เลือกทั้งหมด

0.000 seconds (0.000); 8.44 MB (8.439) - Start Insert
36.799 seconds (36.799); 12.13 MB (3.692) - End Insert


ลองแบ่งเป็นครั้งละ 1000 แถว

โค้ด: เลือกทั้งหมด

0.000 seconds (0.000); 8.44 MB (8.439) - Start Insert
13.035 seconds (13.035); 13.60 MB (5.158) - End Insert


จะเห็นว่าวิธีนี้เป็นทางเลือกที่ดีสำหรับปรับความเร็ว จากที่เกิน 300 วินาที สามารถปรับเหลือแค่ 13 วินาทีได้
The last bug isn't fixed until the last user is dead. (Sidney Markowitz, 1995)

ย้อนกลับไปยัง

ผู้ใช้งานขณะนี้

กำลังดูบอร์ดนี้: 1 และ บุคคลทั่วไป 0 ท่าน