เมื่อ WordPress โหลดช้าโดยไม่มีสาเหตุชัดเจน หนึ่งในต้นเหตุที่พบบ่อยที่สุดคือ Database Slow Query — SQL Query ที่ MySQL ใช้เวลาประมวลผลนานเกินควร ทุก pageview ของ WordPress จะเรียก Database Query หลายสิบครั้ง ตั้งแต่โหลด Post, Comment, Widget ไปจนถึง Plugin Options ถ้ามี Query ที่ช้าแม้แค่ไม่กี่ตัวก็จะส่งผลต่อ TTFB (Time To First Byte) โดยตรง
บทความนี้จะอธิบายวิธีตรวจสอบ Slow Query อย่างเป็นระบบตั้งแต่เปิด Log, อ่านผล, วิเคราะห์ด้วย EXPLAIN ไปจนถึงการ Optimize Database ด้วยทั้ง phpMyAdmin และ MySQL Command Line ให้นำไปใช้ได้ทันทีไม่ว่าจะใช้ Shared Hosting หรือ VPS
ทำความเข้าใจ: ทำไม WordPress ถึงช้าจาก Database
WordPress ใช้ MySQL เป็น Database หลัก ทุก Request ที่เข้ามาจะมีขั้นตอน PHP Bootstrap → โหลด wp-config.php → เชื่อมต่อ MySQL → ดึงข้อมูล Post/Option/Metadata → Render HTML ส่งกลับ ส่วนที่ใช้เวลามากที่สุดในกระบวนการนี้คือ Database Query เพราะต้องเดินทางระหว่าง PHP Process กับ MySQL Process หลายครั้ง
ปัจจัยที่ทำให้ Query ช้าใน WordPress มีหลายอย่าง ได้แก่:
- ไม่มี Index ที่เหมาะสม — MySQL ต้อง Full Table Scan แทนที่จะใช้ Index เพื่อหาแถวที่ต้องการ
- ตารางขนาดใหญ่เกินไป — wp_postmeta และ wp_options ที่สะสมข้อมูลมาหลายปีโดยไม่เคย Clean
- Plugin สร้าง Query ซ้ำซ้อน — Plugin บางตัวทำ Query เดิมซ้ำหลายครั้ง (N+1 Query Problem)
- Transients ที่หมดอายุสะสม — WordPress เก็บ Transient Cache ใน wp_options ถ้าไม่ล้างจะทำให้ตารางนี้บวมขึ้นอย่างรวดเร็ว
- Post Revisions ไม่จำกัด — ค่าเริ่มต้น WordPress จะบันทึกทุก Revision ของทุกโพสต์ เว็บที่มีบทความหลายพันบทความจะมี wp_posts บวมมาก
การแก้ปัญหาที่ได้ผลระยะยาวคือการระบุให้ได้ว่า Query ไหนช้า และช้าเพราะอะไร แล้วค่อยแก้ที่ต้นเหตุ ไม่ใช่แค่เพิ่ม RAM หรือเปลี่ยน Hosting เพียงอย่างเดียว
วิธีเปิด Slow Query Log ใน MySQL
ขั้นตอนแรกคือเปิด Slow Query Log เพื่อบันทึก Query ที่ใช้เวลานานเกินกำหนด มีสองวิธีหลักตามระดับการเข้าถึงเซิร์ฟเวอร์
วิธีที่ 1 — รันผ่าน MySQL Console (ชั่วคราว ไม่ต้อง Restart)
วิธีนี้เหมาะเมื่อต้องการเปิดใช้ชั่วคราวเพื่อ Debug โดยไม่ต้อง Restart MySQL:
-- เปิด Slow Query Log SET GLOBAL slow_query_log = 'ON'; -- กำหนดว่า Query นานเกินกี่วินาทีถึงจะบันทึก (1 วินาที) SET GLOBAL long_query_time = 1; -- กำหนดไฟล์ที่จะบันทึก SET GLOBAL slow_query_log_file = '/tmp/mysql-slow.log'; -- บันทึก Query ที่ไม่ใช้ Index ด้วย (แนะนำ) SET GLOBAL log_queries_not_using_indexes = 'ON';
เมื่อ Restart MySQL ค่าเหล่านี้จะหายไป เหมาะสำหรับ Debug ชั่วคราวบน Shared Hosting ที่เข้าถึง phpMyAdmin ได้
วิธีที่ 2 — แก้ my.cnf (ถาวร สำหรับ VPS)
บน VPS ที่มีสิทธิ์ Root สามารถแก้ไขไฟล์ Config ให้เปิดถาวรได้:
[mysqld] slow_query_log = 1 long_query_time = 1 slow_query_log_file = /var/log/mysql/slow.log log_queries_not_using_indexes = 1 min_examined_row_limit = 100
จากนั้น Restart MySQL:
sudo systemctl restart mysql # หรือ sudo service mysqld restart
อ่านและวิเคราะห์ Slow Query Log
หลังจาก Log ทำงานสักระยะ ให้ดึงข้อมูลออกมาวิเคราะห์ MySQL มาพร้อมกับ mysqldumpslow ที่ใช้ได้เลย:
# แสดง Top 10 Query ที่ช้าที่สุด mysqldumpslow -s t -t 10 /var/log/mysql/slow.log # แสดงตามจำนวนครั้งที่เรียก (Query ที่เรียกบ่อย) mysqldumpslow -s c -t 10 /var/log/mysql/slow.log
ถ้าต้องการรายละเอียดมากกว่าให้ใช้ pt-query-digest จาก Percona Toolkit:
# ติดตั้ง (Ubuntu/Debian) sudo apt-get install percona-toolkit # วิเคราะห์ Log pt-query-digest /var/log/mysql/slow.log
ผลที่ได้จะแสดง Query ที่มี Execute Time, Lock Time, และจำนวน Rows Examined สูงสุด ซึ่งเป็นตัวชี้วัดสำคัญในการหา Bottleneck
ใช้ EXPLAIN วิเคราะห์ Query Plan
เมื่อพบ Query ที่ช้า ให้รัน EXPLAIN เพื่อดูว่า MySQL วางแผนประมวลผลอย่างไร ซึ่งจะบอกได้ว่ามีการใช้ Index หรือไม่:
EXPLAIN SELECT * FROM wp_postmeta WHERE meta_key = '_thumbnail_id' AND post_id IN (SELECT ID FROM wp_posts WHERE post_status = 'publish');
คอลัมน์ที่ต้องดูใน EXPLAIN:
| คอลัมน์ | ความหมาย | ค่าที่ดี | ค่าที่ต้องแก้ |
|---|---|---|---|
| type | วิธีเข้าถึงตาราง | const, ref, range | ALL (Full Scan) |
| rows | จำนวนแถวที่ MySQL คาดว่าต้องอ่าน | น้อย (ใกล้กับ result) | สูงมาก (หลักแสน+) |
| key | Index ที่ถูกเลือกใช้ | ชื่อ Index | NULL (ไม่ใช้ Index) |
| Extra | ข้อมูลเพิ่มเติมของการ Query | Using index | Using filesort, Using temporary |
ถ้าค่า type = ALL และ key = NULL แปลว่า MySQL ต้องอ่านทุกแถวในตาราง (Full Table Scan) ซึ่งเป็นต้นเหตุหลักของ Slow Query ให้พิจารณาเพิ่ม Index ที่ Column ที่ใช้ใน WHERE Clause
ใช้ Query Monitor Plugin ตรวจสอบจาก WordPress Admin
สำหรับผู้ใช้ที่ไม่สะดวกเข้าถึง MySQL โดยตรง Plugin Query Monitor (ฟรี) เป็นเครื่องมือที่ทรงพลังมากใน Dashboard WordPress เอง สามารถติดตั้งได้ทันทีจาก Plugins > Add New
สิ่งที่ Query Monitor แสดงได้:
- จำนวน Database Query ทั้งหมดในแต่ละหน้า พร้อมเวลาที่ใช้รวม
- Query ที่ช้าที่สุด เรียงจากมากไปน้อย พร้อม Stack Trace ว่าถูกเรียกจาก Plugin/Theme ใด
- Duplicate Queries — Query ที่เหมือนกันถูกเรียกซ้ำหลายครั้ง
- HTTP API Calls, Script/Style loading, PHP Errors ในหน้าเดียวกัน
-- ตัวอย่าง Duplicate Query ที่ Query Monitor มักตรวจพบ SELECT option_value FROM wp_options WHERE option_name = 'siteurl' -- Query นี้อาจถูกเรียกซ้ำ 5-10 ครั้งต่อหน้า ถ้า Plugin ไม่ Cache ผลลัพธ์
หลังจากพบ Query ที่มีปัญหา Query Monitor จะบอก File + Line Number ที่เรียก Query นั้น ทำให้ไปแก้ที่ต้นเหตุได้ทันที หรือถ้าเป็น Plugin ที่สร้าง Duplicate Query ให้ลองหา Alternative หรือติดต่อนักพัฒนา Plugin เพื่อรายงานปัญหา
Optimize Database ด้วย phpMyAdmin
การ Optimize Table ใน MySQL ทำให้ MySQL จัดระเบียบข้อมูลใหม่ ลด Fragmentation และลดขนาดตารางหลังจาก Delete ข้อมูลออกไปจำนวนมาก ทำได้ง่ายผ่าน phpMyAdmin
ขั้นตอน Optimize ผ่าน phpMyAdmin
- เข้า phpMyAdmin แล้วเลือก Database ของ WordPress
- คลิกแท็บ Structure แล้วเลือกตารางที่ต้องการ Optimize (หรือ Check All เพื่อเลือกทั้งหมด)
- ที่เมนู "With selected" ด้านล่าง เลือก Optimize table
- รอจนกว่าจะขึ้น Status: OK
ตารางที่ควร Optimize บ่อยที่สุดได้แก่ wp_posts, wp_postmeta, wp_options, wp_comments เพราะเป็นตารางที่มีการเขียน-ลบข้อมูลบ่อย ทำให้เกิด Fragmentation สะสม
Optimize ผ่าน MySQL Command Line
-- Optimize ตารางทีละตาราง OPTIMIZE TABLE wp_posts, wp_postmeta, wp_options, wp_comments; -- หรือ Optimize ทุกตารางในฐานข้อมูลด้วย mysqlcheck mysqlcheck -u root -p --optimize wordpress_db_name
ทำความสะอาด Database — ลบข้อมูลที่ไม่จำเป็น
ก่อน Optimize ควรลบข้อมูลที่ไม่จำเป็นออกก่อน เพราะ Optimize ตารางที่ยังมีข้อมูลขยะอยู่เต็มจะไม่ได้ประโยชน์เท่าที่ควร
ลบ Post Revisions เก่า
-- ดูว่ามี Revision กี่อัน SELECT COUNT(*) FROM wp_posts WHERE post_type = 'revision'; -- ลบ Revision ทั้งหมด DELETE FROM wp_posts WHERE post_type = 'revision'; -- ล้าง Orphaned PostMeta ที่เกี่ยวกับ Revision ที่ลบแล้ว DELETE pm FROM wp_postmeta pm LEFT JOIN wp_posts p ON p.ID = pm.post_id WHERE p.ID IS NULL;
จำกัด Post Revisions ใน wp-config.php
เพิ่มบรรทัดนี้ใน wp-config.php ก่อนบรรทัด /* That's all, stop editing! */ เพื่อจำกัดไม่ให้ WordPress สะสม Revision มากเกินไปในอนาคต:
// เก็บ Revision ไว้แค่ 3 อัน (0 = ปิดใช้งาน Revision ทั้งหมด)
define('WP_POST_REVISIONS', 3);
ล้าง Transients ที่หมดอายุ
-- ลบ Transients ที่หมดอายุแล้ว
DELETE FROM wp_options
WHERE option_name LIKE '_transient_timeout_%'
AND option_value < UNIX_TIMESTAMP();
DELETE FROM wp_options
WHERE option_name LIKE '_transient_%'
AND option_name NOT LIKE '_transient_timeout_%'
AND REPLACE(option_name, '_transient_', '_transient_timeout_') NOT IN (
SELECT option_name FROM (SELECT option_name FROM wp_options) AS tmp
);
ลบ Spam Comments
-- ดูจำนวน Spam Comments SELECT COUNT(*) FROM wp_comments WHERE comment_approved = 'spam'; -- ลบ DELETE FROM wp_comments WHERE comment_approved = 'spam'; DELETE FROM wp_commentmeta WHERE comment_id NOT IN (SELECT comment_ID FROM wp_comments);
เคล็ดลับสำคัญ: ก่อนรัน SQL DELETE ทุกครั้ง ให้ Backup Database ก่อนเสมอ ทำได้ง่ายผ่าน phpMyAdmin > Export หรือใช้ Plugin เช่น UpdraftPlus เพื่อ Backup อัตโนมัติ นอกจากนั้น ให้รัน SQL Query แบบ SELECT ก่อนเสมอเพื่อดูว่าจะ Delete กี่แถว แล้วค่อยเปลี่ยนเป็น DELETE ถ้าตัวเลขสมเหตุสมผล
ตั้งค่า wp-config.php เพื่อลด Database Overhead
มีการตั้งค่าหลายอย่างใน wp-config.php ที่ช่วยลด Database Load ได้โดยตรง:
// จำกัด Post Revisions
define('WP_POST_REVISIONS', 3);
// ปิด Autosave ถี่เกินไป (ค่าเริ่มต้น 60 วินาที → เปลี่ยนเป็น 300 วินาที)
define('AUTOSAVE_INTERVAL', 300);
// เปิด WP_CACHE (จำเป็นสำหรับ Caching Plugin)
define('WP_CACHE', true);
// ปิด Trash (ลบถาวรเลย ไม่ต้องเก็บใน Trash) — ใช้ถ้าต้องการลด Row ใน wp_posts
define('EMPTY_TRASH_DAYS', 7);
// เพิ่ม MySQL Timeout (ป้องกัน Query หมดเวลากลางทาง)
define('DB_CHARSET', 'utf8mb4');
define('DB_COLLATE', 'utf8mb4_unicode_ci');
เพิ่ม Index ให้ตาราง WordPress ที่ใช้บ่อย
ถ้า EXPLAIN บอกว่า Query ไม่ใช้ Index การเพิ่ม Index ใหม่จะช่วยได้มาก โดยเฉพาะ wp_postmeta ที่มักถูก Query ด้วย meta_key และ post_id พร้อมกัน
-- ตรวจดู Index ที่มีอยู่ SHOW INDEX FROM wp_postmeta; -- เพิ่ม Composite Index สำหรับ Query ที่กรองด้วย meta_key + meta_value พร้อมกัน ALTER TABLE wp_postmeta ADD INDEX idx_key_value (meta_key, meta_value(20)); -- wp_options: เพิ่ม Index ที่ autoload เพื่อเร่ง WordPress Startup ALTER TABLE wp_options ADD INDEX idx_autoload (autoload); -- ตรวจ wp_options ที่ autoload = 'yes' มีขนาดรวมเท่าไร SELECT SUM(LENGTH(option_value)) as autoload_size FROM wp_options WHERE autoload = 'yes';
ค่า autoload_size ที่เกิน 800KB ถือว่าสูงเกินไปและจะทำให้ WordPress Startup ช้า ให้ตรวจว่า Plugin ใดเป็นตัวเพิ่มข้อมูล Autoload จำนวนมาก แล้วพิจารณาปิดหรือเปลี่ยน Plugin นั้น
คำถามที่พบบ่อย (FAQ)
Slow Query ใน WordPress คืออะไร และส่งผลต่อเว็บอย่างไร
Slow Query คือ SQL Query ที่ใช้เวลาประมวลผลนานกว่าที่กำหนด ค่าเริ่มต้น MySQL คือ 10 วินาที แต่ในทางปฏิบัติ Query ที่ใช้นานเกิน 1-2 วินาทีก็ถือว่าช้าแล้ว ใน WordPress ทุก pageview จะเรียก Query หลายสิบครั้ง ถ้ามี Slow Query ปนอยู่สักตัวก็จะทำให้หน้าเว็บโหลดช้าลงอย่างเห็นได้ชัด โดยเฉพาะในช่วง traffic สูง นอกจากนั้น Slow Query ยังทำให้ MySQL ใช้ CPU และ Memory เพิ่มขึ้น ซึ่งกระทบ performance ของหน้าอื่นๆ บนเซิร์ฟเวอร์เดียวกันด้วย
จะเปิด Slow Query Log ใน MySQL ได้อย่างไร
บน Shared Hosting ให้เปิดจาก phpMyAdmin แล้วรัน SET GLOBAL slow_query_log = 'ON'; SET GLOBAL long_query_time = 1; ซึ่งมีผลชั่วคราว บน VPS ที่เข้าถึง my.cnf ได้ให้เพิ่มใน [mysqld] section: slow_query_log=1, long_query_time=1, slow_query_log_file=/var/log/mysql/slow.log แล้ว restart MySQL เพื่อให้มีผลถาวร จากนั้นใช้ mysqldumpslow หรือ pt-query-digest วิเคราะห์ log ที่ได้
WordPress มี Plugin ช่วยวิเคราะห์ Database Performance ไหม
มีหลายตัวที่ใช้ได้ดี เช่น Query Monitor (ฟรี) ที่แสดงทุก Database Query พร้อมเวลาที่ใช้แยกตามหน้า WP Sweep และ Advanced Database Cleaner ช่วย Clean ข้อมูลเก่าออก WP-DBManager ช่วย Optimize ตารางและ Auto-backup แต่ต้องระวังว่า Debug Plugin จะเพิ่ม Overhead ให้เปิดใช้เฉพาะตอน Debug แล้วปิดใน Production เสมอ
ควร Optimize Database WordPress บ่อยแค่ไหน
สำหรับเว็บที่มีกิจกรรมสูง เช่น บล็อกที่โพสต์ทุกวัน หรือ WooCommerce ที่มี Order เข้ามาตลอด ควร Optimize Database อย่างน้อยเดือนละครั้ง เว็บที่มีการแก้ไขเนื้อหาบ่อยๆ จะสะสม Post Revisions และ Transients เยอะ ควรตั้ง wp-cron หรือ Scheduled Task ให้ Clean ข้อมูลอัตโนมัติ สิ่งที่ควรทำสม่ำเสมอได้แก่: ลบ Post Revisions เก่า ลบ Spam Comments ล้าง Transients ที่หมดอายุ และ OPTIMIZE TABLE ตาราง wp_posts และ wp_postmeta ที่มีการเขียน-ลบบ่อย
Hosting ที่ออปติไมซ์สำหรับ WordPress
AsiaGB Hosting รองรับ PHP 8.3 MySQL LiteSpeed Cache และ WordPress Toolkit เริ่มต้น 500 บาท/ปี
ดูแพ็กเกจ Hosting