MyISAM vs InnoDB: เลือก Storage Engine ให้ถูกใจ MySQL
เคยไหม? รัน Query เดิม ข้อมูลเท่าเดิม แต่พอเปลี่ยน Storage Engine ปุ๊บ เร็วขึ้นเป็นเท่าตัว!
เรื่องนี้เกิดขึ้นจริงกับผม เมื่อสัปดาห์ที่แล้ว ตาราง sendrefer ขนาด 130,793 แถว ที่ใช้ MyISAM อยู่ ๆ JOIN ทีช้าจนได้ เปลี่ยนเป็น InnoDB เท่านั้นแหละครับ Query ที่เคยใช้เวลาเป็น 10 วินาที เหลือหลักเสี้ยววินาทีทันที!
บทความนี้จะพาทุกคนไปรู้จักกับ 2 Storage Engine ยอดฮิตของ MySQL ว่ามันต่างกันยังไง? เมื่อไหร่ควรใช้อะไร? และทำไม InnoDB ถึงเป็น Default ในปัจจุบัน
🧠 Storage Engine คืออะไร?
Storage Engine เปรียบเสมือน เครื่องยนต์ของรถ MySQL เป็นตัวถัง แต่เครื่องยนต์คือสิ่งที่กำหนดว่า:
- จะเก็บข้อมูลยังไง?
- จะค้นหาข้อมูลยังไง?
- จะจัดการความปลอดภัยของข้อมูลยังไง?
- รองรับการทำงานพร้อมกันกี่คน?
MySQL รองรับหลาย Storage Engine แต่ที่นิยมสุดคือ MyISAM และ InnoDB
🆚 ตารางเปรียบเทียบ MyISAM vs InnoDB
| คุณสมบัติ | MyISAM | InnoDB |
|---|---|---|
| การ Lock | Table-level Lock (ล็อคทั้งตาราง) | Row-level Lock (ล็อคเฉพาะแถว) |
| Transaction (ACID) | ❌ ไม่รองรับ | ✅ รองรับ (COMMIT, ROLLBACK) |
| Foreign Key | ❌ ไม่รองรับ | ✅ รองรับ |
| Clustered Index | ❌ ไม่มี (Heap-organized) | ✅ มี (Index-organized) |
| Full-text Search | ✅ มี (แต่รุ่นเก่า) | ✅ มี (ตั้งแต่ MySQL 5.6) |
| Data Caching | Cache เฉพาะ Index | Cache ทั้ง Data และ Index |
| Compression | ✅ รองรับ | ✅ รองรับ (แต่ซับซ้อนกว่า) |
| ความเร็วในการอ่าน | ⚡ เร็วมาก | 🚀 เร็ว (ถ้าใช้ Index ถูก) |
| ความเร็วในการเขียน | 🐢 ช้า (ล็อคทั้งตาราง) | ⚡ เร็ว (ล็อคเฉพาะแถว) |
| พื้นที่จัดเก็บ | 💾 น้อยกว่า | 💿 มากกว่า (มี overhead) |
🔬 เจาะลึกความแตกต่าง
1. การ Lock ข้อมูล 🔒
-- MyISAM: เวลาอัพเดท จะล็อค TABLE ทั้ง table
UPDATE myisam_table SET status = 1 WHERE id = 5;
-- ตอนนี้ TABLE myisam_table โดนล็อคทั้งหมด!
-- คนอื่นจะ SELECT หรือ UPDATE ไม่ได้ จนกว่าจะเสร็จ
-- InnoDB: ล็อคเฉพาะ ROW
UPDATE innodb_table SET status = 1 WHERE id = 5;
-- ล็อคเฉพาะแถว id=5
-- คนอื่น UPDATE แถว id=6 ได้ปกติ!
2. Clustered Index vs Heap-organized 📚
MyISAM (Heap-organized):
Primary Key Index Data File
[id: 5] ------> [Address: 0x7F8A] ------> [5 | Somchai | 50000]
[id: 8] ------> [Address: 0x7F9B] ------> [8 | Somsak | 60000]
- ต้องค้นหา 2 รอบ: Index → Address → Data
InnoDB (Clustered Index):
Primary Key Index + Data
[id: 5 | Somchai | 50000] [id: 8 | Somsak | 60000]
- ข้อมูลอยู่กับ index! ค้นหาเจอปุ๊บ ได้ข้อมูลปั๊บ
3. Buffer Pool vs Key Cache 🗃️
-- InnoDB: มี Buffer Pool ใหญ่ๆ ไว้ cache ทุกอย่าง
SHOW VARIABLES LIKE 'innodb_buffer_pool_size';
-- ผล: 134217728 (128MB) - cache ทั้ง data และ index
-- MyISAM: มี Key Cache เฉพาะ index
SHOW VARIABLES LIKE 'key_buffer_size';
-- ผล: 8388608 (8MB) - cache เฉพาะ index
-- data ต้องอ่านจาก disk ตลอด
📊 ทดสอบ Performance จริง
สมมติ我们有 2 ตาราง:
-- MyISAM
CREATE TABLE users_myisam (
id INT PRIMARY KEY,
name VARCHAR(100),
salary DECIMAL(10,2)
) ENGINE=MyISAM;
-- InnoDB
CREATE TABLE users_innodb (
id INT PRIMARY KEY,
name VARCHAR(100),
salary DECIMAL(10,2)
) ENGINE=InnoDB;
-- ใส่ข้อมูล 1 ล้านแถวเหมือนกัน
ทดสอบที่ 1: SELECT with JOIN
-- MyISAM
EXPLAIN SELECT * FROM users_myisam u
JOIN orders_myisam o ON u.id = o.user_id;
-- type: ALL, rows: 1,000,000
-- Extra: Using join buffer (Block Nested Loop)
-- InnoDB
EXPLAIN SELECT * FROM users_innodb u
JOIN orders_innodb o ON u.id = o.user_id;
-- type: eq_ref, rows: 1
-- Extra: Using index
ทดสอบที่ 2: UPDATE พร้อมกันหลายๆ คน
-- Connection 1: MyISAM
UPDATE users_myisam SET salary = 100000 WHERE id = 5;
-- ❌ Connection 2 ต้องรอจนกว่า Connection 1 จะ COMMIT
-- Connection 1: InnoDB
UPDATE users_innodb SET salary = 100000 WHERE id = 5;
-- ✅ Connection 2 UPDATE id=6 ได้ทันที ไม่ต้องรอ!
🎯 เลือกใช้แบบไหนดี?
✅ MyISAM เหมาะกับ:
- Data Warehouse / Read-only – อ่านอย่างเดียว ไม่มีเขียน
- ระบบ Log – INSERT อย่างเดียว ไม่มี UPDATE
- ตารางเล็กๆ – ไม่เกิน 100,000 แถว
- Full-text Search (ใน MySQL รุ่นเก่าๆ)
-- ตัวอย่าง Data Warehouse ที่เหมาะกับ MyISAM
CREATE TABLE sales_summary_2019 (
month INT,
total_sales DECIMAL(15,2),
total_customers INT
) ENGINE=MyISAM;
-- INSERT เดือนละครั้ง, SELECT ตลอดทั้งปี
✅ InnoDB เหมาะกับ:
- ระบบ Transaction – การเงิน, E-commerce, ERP
- ระบบที่มี User เ� تطبیق 많음 – Web application, Mobile backend
- ตารางที่มี UPDATE/DELETE บ่อย
- ระบบที่ต้องมี Foreign Key – ต้องการ Data Integrity
- ทุกอย่าง! (เพราะเป็น default แล้ว)
-- ตัวอย่างระบบ E-commerce ที่เหมาะกับ InnoDB
CREATE TABLE orders (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT,
total DECIMAL(15,2),
status VARCHAR(20),
created_at DATETIME,
FOREIGN KEY (user_id) REFERENCES users(id)
) ENGINE=InnoDB;
-- มี INSERT, UPDATE, DELETE ตลอดเวลา
-- ต้อง保住 data integrity
🚨 เรื่องต้องรู้ก่อนเปลี่ยน Engine
1. Full-text Search
- MyISAM มี Full-text มานาน
- InnoDB มี Full-text ตั้งแต่ MySQL 5.6
- ถ้าใช้ Full-text อยู่ ตรวจสอบ version ก่อนเปลี่ยน
2. ** AUTO_INCREMENT behavior**
-- MyISAM: AUTO_INCREMENT เร็วมาก
-- InnoDB: AUTO_INCREMENT ช้ากว่า (ต้อง lock special table)
-- แต่ InnoDB มีข้อดี: INSERT พร้อมกันหลายๆ แถวได้!
3. Space Usage
-- InnoDB ใช้พื้นที่มากกว่า ~20-30%
SELECT
table_name,
ROUND(((data_length + index_length) / 1024 / 1024), 2) AS 'Size (MB)'
FROM information_schema.tables
WHERE table_schema = 'your_db';
-- MyISAM table ขนาด 100MB
-- InnoDB table เดียวกัน ขนาด 120-130MB
4. COUNT(*) Performance
-- MyISAM: COUNT(*) เร็วมาก (cache ค่าไว้)
SELECT COUNT(*) FROM big_table_myisam; -- 0.001 วินาที
-- InnoDB: COUNT(*) ต้องนับจริง (เพราะ MVCC)
SELECT COUNT(*) FROM big_table_innodb; -- 2.5 วินาที
-- แต่ InnoDB แก้ได้ด้วย:
SELECT COUNT(*) FROM big_table_innodb WHERE id > 1000; -- เร็ว
🛠️ วิธีเปลี่ยน Engine (step-by-step)
1. หาให้เจอว่ามีตารางไหนใช้ MyISAM บ้าง
SELECT
table_schema AS database_name,
table_name,
engine
FROM information_schema.tables
WHERE engine = 'MyISAM'
AND table_schema NOT IN ('information_schema', 'mysql',
'performance_schema', 'sys');
2. สำรองข้อมูล
# Backup ก่อนเปลี่ยนเสมอ!
mysqldump -u root -p your_database > backup_before_change.sql
3. เริ่มเปลี่ยนทีละตาราง
-- เปลี่ยนทีละตาราง (แนะนำ)
ALTER TABLE your_table ENGINE = InnoDB;
-- หรือสร้างคำสั่งอัตโนมัติ
SELECT CONCAT('ALTER TABLE ', table_schema, '.', table_name,
' ENGINE = InnoDB;') AS alter_command
FROM information_schema.tables
WHERE engine = 'MyISAM';
4. อัพเดทสถิติ
ANALYZE TABLE your_table;
5. ตรวจสอบผลลัพธ์
EXPLAIN SELECT your_query;
-- ดูว่า type ดีขึ้น, rows ลดลงมั้ย
💡 เคล็ดลับเพิ่มประสิทธิภาพ InnoDB
1. ปรับ Buffer Pool
# my.cnf
[mysqld]
# ตั้งให้ประมาณ 70-80% ของ RAM innodb_buffer_pool_size = 4G # ถ้า RAM > 16GB ให้แยก instance innodb_buffer_pool_instances = 4
2. ใช้ Index ให้ถูก
-- สร้าง composite index ให้ตรงกับ WHERE
ALTER TABLE your_table
ADD INDEX idx_composite (col1, col2, col3);
-- ไม่ใช่ index ทีละ col แยกกัน
3. เลือก Data Type ให้เหมาะสม
-- ใช้ INT แทน VARCHAR ถ้าเก็บตัวเลข
-- ใช้ DATE/DATETIME แทน VARCHAR สำหรับวันที่
-- ไม่ดี
CREATE TABLE bad (
user_id VARCHAR(20),
reg_date VARCHAR(20)
);
-- ดี
CREATE TABLE good (
user_id INT,
reg_date DATE
);
🏁 สรุป
| ยุค | Storage Engine ที่นิยม |
|---|---|
| MySQL 3.x – 5.0 | MyISAM คิง |
| MySQL 5.1 – 5.5 | MyISAM + InnoDB |
| MySQL 5.6+ | InnoDB default |
| MySQL 8.0 | InnoDB เท่านั้น! |
InnoDB ชนะขาดในทุกด้าน สำหรับยุคสมัยนี้:
- ✅ Row-level locking
- ✅ Transaction support
- ✅ Foreign key
- ✅ Crash recovery
- ✅ ACID compliance
MyISAM ยังมีที่ทางแค่กรณีเฉพาะ เช่น:
- ตารางที่เป็น read-only
- ระบบ data warehouse ที่ bulk insert แล้วอ่านอย่างเดียว
- MySQL รุ่นเก่าที่ต้องการ full-text search
📝 ทิ้งท้าย
จากประสบการณ์จริงที่ผมเจอมา การเปลี่ยนจาก MyISAM → InnoDB ให้ผลลัพธ์ที่น่าทึ่งมาก Query ที่เคยช้าติดลม กลายเป็นไวปรื๋อ
แต่… ก็ไม่เสมอไปนะครับ! บางครั้ง MyISAM ก็ยังเหนือกว่า โดยเฉพาะการ COUNT(*) ทั้งตาราง หรือการทำ full-text search ใน MySQL รุ่นเก่า
ดังนั้น: ใช้ InnoDB เป็น default แต่เข้าใจ MyISAM ไว้เผื่อกรณีพิเศษ