Cách làm phổ biến (loop toàn bộ dữ liệu trong một request) gần như là tự sát khi dữ liệu đủ lớn. Giải pháp đúng không nằm ở việc “làm nhanh hơn”, mà là chia nhỏ và xử lý nền. Bài viết này trình bày một kỹ thuật đơn giản nhưng cực kỳ hiệu quả: tự cron lặp lại cho đến khi hoàn tất, không block request và không gây quá tải server.
Ý tưởng cốt lõi
Thay vì xử lý toàn bộ dữ liệu trong một lần, ta chia thành nhiều batch nhỏ. Mỗi lần chạy:
- Xử lý một phần dữ liệu (ví dụ 100–200 record)
- Kiểm tra còn dữ liệu hay không
- Nếu còn → tự schedule lại một cron mới
Quá trình này lặp lại cho đến khi hoàn tất. Mỗi lần chạy rất nhẹ, nên không gây ảnh hưởng đến request của user.
Triển khai cơ bản với WP-Cron
<?php
if ( ! defined( 'ABSPATH' ) ) exit;
add_action( 'init_html_migration_event', 'init_html_migration_runner' );
function init_html_migration_runner() {
// LOCK tránh chạy song song
if ( get_transient( 'init_html_migration_lock' ) ) return;
set_transient( 'init_html_migration_lock', 1, 60 );
$has_more = init_html_maybe_migrate_batch();
if ( $has_more ) {
wp_schedule_single_event( time() + 30, 'init_html_migration_event' );
}
delete_transient( 'init_html_migration_lock' );
}
Kích hoạt migration lần đầu
<?php
register_activation_hook( __FILE__, function () {
if ( ! wp_next_scheduled( 'init_html_migration_event' ) ) {
wp_schedule_single_event( time() + 30, 'init_html_migration_event' );
}
});
Ví dụ thực tế: migrate user_meta sang custom table
Giả sử bạn đang lưu dữ liệu trong user_meta với pattern:
_init_html_data_{post_id}_{device}
Và muốn migrate sang bảng riêng để tối ưu query.
Batch migration function
<?php function init_html_maybe_migrate_batch() { global $wpdb; $meta_pattern = '_init_html_data_%'; // Lấy 200 user có dữ liệu cần migrate $user_ids = $wpdb->get_col(
$wpdb->prepare(
"SELECT DISTINCT user_id FROM {$wpdb->usermeta}
WHERE meta_key LIKE %s
ORDER BY user_id ASC
LIMIT 200",
$meta_pattern
)
);
if ( empty( $user_ids ) ) {
update_option( 'init_html_migration_done', 1 );
return false;
}
foreach ( $user_ids as $user_id ) {
init_html_migrate_user( (int) $user_id );
}
return true;
}
Migrate từng user
<?php function init_html_migrate_user( $user_id ) { global $wpdb; $rows = $wpdb->get_results(
$wpdb->prepare(
"SELECT meta_key, meta_value FROM {$wpdb->usermeta}
WHERE user_id = %d AND meta_key LIKE %s",
$user_id,
'_init_html_data_%'
),
ARRAY_A
);
if ( empty( $rows ) ) return;
foreach ( $rows as $row ) {
$meta_key = $row['meta_key'];
$meta_value = maybe_unserialize( $row['meta_value'] );
if ( ! is_array( $meta_value ) ) {
delete_user_meta( $user_id, $meta_key );
continue;
}
// Parse post_id + device từ meta key
if ( ! preg_match( '/^_init_html_data_(\d+)_(.+)$/', $meta_key, $m ) ) {
delete_user_meta( $user_id, $meta_key );
continue;
}
$post_id = (int) $m[1];
$device = sanitize_key( $m[2]);
$value = sanitize_text_field( $meta_value['value'] ?? '' );
$updated_at = current_time( 'mysql', true );
init_html_upsert( $user_id, $post_id, $device, $value, $updated_at );
delete_user_meta( $user_id, $meta_key );
}
}
Upsert vào custom table
<?php function init_html_upsert( $user_id, $post_id, $device, $value, $updated_at ) { global $wpdb; $table = $wpdb->prefix . 'init_html_data';
$wpdb->query(
$wpdb->prepare(
"INSERT INTO $table (user_id, post_id, device, value, updated_at)
VALUES (%d, %d, %s, %s, %s)
ON DUPLICATE KEY UPDATE
value = VALUES(value),
updated_at = VALUES(updated_at)",
$user_id,
$post_id,
$device,
$value,
$updated_at
)
);
}
Điểm quan trọng cần nhớ
- Không dùng OFFSET → tránh miss dữ liệu
- Xóa meta sau khi migrate → đảm bảo không chạy lại
- Thiết kế idempotent → chạy lại không lỗi
- Batch size phải hợp lý (100–500 tùy server)
Kết luận
Kỹ thuật tự cron lặp lại là một cách cực kỳ thực dụng để xử lý dữ liệu lớn trong WordPress. Nó không hào nhoáng, nhưng ổn định, dễ triển khai và phù hợp với môi trường production.
Nếu bạn đang chuẩn bị migrate dữ liệu lớn, đừng cố “chạy một lần cho xong”. Hãy chia nhỏ, chạy nền, và để hệ thống tự hoàn thành công việc của nó.
Bình luận