- Vấn đề hiệu năng: vì sao meta_query chậm?
- Nguyên tắc chọn index (nhớ kỹ)
- Đo đạc trước khi thêm index
- Các index khuyến nghị cho wp_postmeta
- Các index khuyến nghị cho wp_usermeta
- Thêm index an toàn với WP-CLI
- Ví dụ thực chiến: tăng tốc meta_query trên category archive
- ORDER BY meta_value: cách làm đúng
- Checklist an toàn khi làm việc với index
- Gợi ý phối hợp với plugin tối ưu index
- Kết luận
Vấn đề hiệu năng: vì sao meta_query chậm?
Hai bảng phình to nhanh nhất là wp_postmeta và wp_usermeta. Mặc định:
wp_postmeta: PRIMARY KEY trênmeta_id, có KEY chopost_idvàmeta_key(không phải composite).wp_usermeta: tương tự, PRIMARY KEYumeta_id, có KEY chouser_idvàmeta_key.
Khi bạn lọc theo post_id và meta_key cùng lúc, MySQL khó tận dụng tối đa index đơn lẻ, dẫn đến full/partial scan nhiều trang dữ liệu. Composite index đúng hướng sẽ giúp MySQL “đi thẳng” vào vùng dữ liệu cần.
Nguyên tắc chọn index (nhớ kỹ)
- Composite theo thứ tự điều kiện lọc: Cột lọc “chọn lọc nhất” đặt trước (ví dụ:
(post_id, meta_key)trong loop một category). - Tránh index thừa: Mỗi index tốn dung lượng và làm chậm ghi/UPDATE. Chỉ tạo khi có query thực sự cần.
- Dùng prefix index cho VARCHAR: Ví dụ
meta_key(191)để giữ kích thước index gọn, tương thích InnoDB/utf8mb4. - ORDER BY meta_value: Rất tốn kém. Nếu thường xuyên sắp xếp theo số, hãy lưu thêm bản sao kiểu số (int/decimal) hoặc dùng bảng custom.
Đo đạc trước khi thêm index
<?php
// Bật Query Monitor (plugin) hoặc dùng EXPLAIN trong MySQL
global $wpdb;
$sql = $wpdb->prepare("
EXPLAIN SELECT pm.post_id
FROM {$wpdb->postmeta} pm
WHERE pm.meta_key = %s AND pm.meta_value = %s
LIMIT 50
", 'chap_unlock_vcoin', '1');
print_r( $wpdb->get_results($sql) );
Nếu thấy type=ALL hoặc rows quá lớn, đó là tín hiệu cần index.
Các index khuyến nghị cho wp_postmeta
Tùy pattern query, chọn một hoặc vài index dưới đây (đừng tạo tất nếu không cần).
-- 1) Truy vấn theo post_id + meta_key (phổ biến trong loop render)
ALTER TABLE wp_postmeta
ADD INDEX idx_postmeta_postid_metakey (post_id, meta_key(191));
-- 2) Truy vấn theo meta_key + meta_value (lọc “flag”/cờ/bool)
ALTER TABLE wp_postmeta
ADD INDEX idx_postmeta_metakey_value (meta_key(191), meta_value(191));
-- 3) Truy vấn theo meta_key, ORDER BY post_id (batch xử lý)
ALTER TABLE wp_postmeta
ADD INDEX idx_postmeta_metakey_postid (meta_key(191), post_id);
Lưu ý: Nếu bạn dùng plugin tối ưu index cấp hệ thống (ví dụ thay đổi primary key hoặc đã có composite tương tự), hãy kiểm tra khóa hiện có bằng SHOW INDEX trước khi thêm để tránh trùng lặp.
Các index khuyến nghị cho wp_usermeta
-- 1) Lọc user theo meta (ví dụ users bật thông báo)
ALTER TABLE wp_usermeta
ADD INDEX idx_usermeta_userid_metakey (user_id, meta_key(191));
-- 2) Tìm user theo meta_key + meta_value (role phụ, cờ VIP, v.v.)
ALTER TABLE wp_usermeta
ADD INDEX idx_usermeta_metakey_value (meta_key(191), meta_value(191));
Thêm index an toàn với WP-CLI
Thao tác schema dễ timeout nếu chạy qua PHP. Dùng WP-CLI để an toàn hơn:
# Kiểm tra index hiện có
wp db query "SHOW INDEX FROM wp_postmeta\G"
# Thêm index (ví dụ)
wp db query "ALTER TABLE wp_postmeta ADD INDEX idx_postmeta_postid_metakey (post_id, meta_key(191))"
# Xóa index nếu cần rollback
wp db query "ALTER TABLE wp_postmeta DROP INDEX idx_postmeta_postid_metakey"
Ví dụ thực chiến: tăng tốc meta_query trên category archive
Pattern phổ biến: render danh sách chương (post) rồi kiểm tra nhiều meta trong vòng lặp.
<?php
global $wpdb;
$post_ids = get_posts([
'post_type' => 'post',
'cat' => (int) $category->term_id,
'fields' => 'ids',
'posts_per_page' => 200,
'no_found_rows' => true
]);
if ($post_ids) {
// Lấy hàng loạt meta "chap_unlock_vcoin" cho các post đã biết
$placeholders = implode(',', array_fill(0, count($post_ids), '%d'));
$sql = $wpdb->prepare("
SELECT post_id, meta_value
FROM {$wpdb->postmeta}
WHERE meta_key = %s
AND post_id IN ($placeholders)
", array_merge(['chap_unlock_vcoin'], $post_ids));
// Index (post_id, meta_key) sẽ phát huy tác dụng ở đây
$rows = $wpdb->get_results($sql, ARRAY_A);
// Map meta để render không N+1
$unlock_map = [];
foreach ($rows as $r) { $unlock_map[$r['post_id']] = $r['meta_value']; }
// ... render ...
}
Nếu bạn đã tạo idx_postmeta_postid_metakey, truy vấn trên sẽ dùng index tốt hơn hẳn thay vì scan toàn bảng.
ORDER BY meta_value: cách làm đúng
Sắp xếp theo meta_value dạng text/number là nặng. Giải pháp:
- Nếu là số: lưu thêm một meta song song dạng số (ví dụ
chap_view_int) để ORDER BY không cần CAST. - Nếu sắp xếp rất thường xuyên: cân nhắc bảng custom với cột kiểu phù hợp và index riêng.
-- Ví dụ index cho meta số (lưu ở meta_value, có thể dùng prefix + CAST khi bắt buộc)
ALTER TABLE wp_postmeta
ADD INDEX idx_postmeta_key_value_num (meta_key(191), meta_value(16));
Checklist an toàn khi làm việc với index
- Backup trước khi ALTER TABLE trên site production.
- Kiểm tra index hiện có bằng
SHOW INDEXđể tránh trùng. - Chạy giờ thấp tải hoặc trên replica/staging rồi chuyển đổi.
- Đo đạc bằng EXPLAIN + Query Monitor trước và sau khi thêm index.
- Tránh lạm dụng: mỗi index tăng kích thước bảng và chi phí ghi.
Gợi ý phối hợp với plugin tối ưu index
Nếu bạn dùng plugin tối ưu index (ví dụ có thay đổi primary key hoặc thêm composite sẵn), hãy tận dụng cấu hình mặc định của plugin đó. Chỉ thêm index custom khi bạn có pattern query đặc thù chưa được phủ. Trước khi thêm, so sánh danh sách index sẵn có để không đè/nhân đôi.
Kết luận
Hiệu năng WordPress ở quy mô lớn phụ thuộc rất nhiều vào cách bạn truy vấn meta. Thêm đúng index cho postmeta/usermeta giúp MySQL “đi đường tắt” thay vì lần mò cả triệu dòng. Hãy bắt đầu từ việc đo đạc (EXPLAIN), chọn index đúng pattern, thêm bằng WP-CLI, rồi kiểm tra lại bằng Query Monitor. Chỉ một hai index chuẩn bài cũng đủ “bốc” tốc độ ở những trang nặng nhất.
Bình luận