У вас магазин на WooCommerce. Тысячи заказов, тонны метаданных. Вы перешли на HPOS — и вроде всё работает, но база данных весит как будто миграция не удалила старые данные. Знакомая ситуация?

Именно с этим столкнулся один из наших клиентов: после миграции на HPOS база данных выросла на 2 ГБ вместо того, чтобы сжаться. Заказы дублировались в двух системах хранения, cron-задачи тормозили, а время генерации отчётов увеличилось втрое. Проблема оказалась тривиальной — старые записи никто не почистил.

В этой статье расскажу, как привести таблицы заказов WooCommerce в порядок после миграции на HPOS, избежать типичных ошибок и вернуть магазину ту производительность, ради которой вы всё это затевали.

Что такое HPOS и зачем вообще нужна миграция

До появления HPOS WooCommerce хранила заказы в таблицах wp_posts и wp_postmeta — то есть заказы жили рядом с обычными записями блога и страницами. Каждый заказ — это пост типа shop_order, а все его данные (адрес, товары, скидки) — это метаполя.

Проблема в том, что таблица wp_postmeta растёт экспоненциально. Магазин с 50 000 заказами генерирует примерно 700 000 — 1 000 000 строк в wp_postmeta. Каждое обновление заказа добавляет новые записи, не удаляя старые. Запросы к этой таблице становятся неприлично медленными.

HPOS (High Performance Order Storage) решает эту проблему, создавая отдельные таблицы специально для заказов:

  • wp_wc_orders — основная информация о заказе
  • wp_wc_orders_meta — метаданные заказа
  • wp_wc_order_addresses — адреса доставки и оплаты
  • wp_wc_order_operational_data — операционные данные (валюта, суммы налогов)

Результат — запросы к заказам работают в 5-10 раз быстрее на больших объёмах данных. Для магазина с десятками тысяч заказов разница ощутима сразу: отчёты генерируются за секунды вместо минут, админка перестаёт «подвисать».

Как происходит миграция и что остаётся «после»

WooCommerce предлагает два режима хранения заказов:

  • Синхронизированный режим — данные записываются и в старые таблицы, и в новые
  • Режим только HPOS — заказы хранятся исключительно в новых таблицах

Миграция запускается через WooCommerce → Настройки → Расширенные → Хранилище заказов. WooCommerce копирует все заказы из wp_posts/wp_postmeta в новые HPOS-таблицы. Весь процесс может занять от нескольких минут до нескольких часов — зависит от количества заказов.

И вот тут кроется главный подвох: миграция копирует данные, но не удаляет старые записи. Это сделано намеренно — на случай отката. Но если вы убедились, что всё работает корректно, старый хлам нужно убирать вручную.

Проверка целостности после миграции

Прежде чем что-то удалять, обязательно проверьте, что миграция прошла корректно. Потерять данные о заказах — это не шутки.

Сравнение количества записей

Первый шаг — убедиться, что количество заказов совпадает в обеих системах хранения.

-- Количество заказов в старой системе
SELECT COUNT(*) as old_orders
FROM wp_posts
WHERE post_type = 'shop_order'
AND post_status NOT IN ('auto-draft', 'trash');

-- Количество заказов в HPOS
SELECT COUNT(*) as new_orders
FROM wp_wc_orders;

Числа должны совпадать (или быть очень близки — небольшая разница возможна из-за черновиков и мусорных записей). Если расхождение значительное — не удаляйте старые данные, сначала разберитесь, что пошло не так.

Проверка через встроенный инструмент WooCommerce

WooCommerce имеет встроенный механизм верификации. В админке перейдите в WooCommerce → Статус → Инструменты и найдите пункт «Синхронизация HPOS». Запустите проверку — система сравнит данные в обеих таблицах и покажет расхождения.

Проверка случайных заказов

Выберите 10-20 заказов из разных периодов и проверьте их вручную: товары, суммы, адреса, статусы. Откройте несколько заказов в админке, убедитесь, что всё отображается корректно.

Очистка старых данных заказов из wp_posts и wp_postmeta

Итак, вы убедились, что миграция прошла успешно. Теперь можно приступать к очистке. Но перед этим — сделайте бэкап базы данных. Серьёзно. Это не формальность, а страховка от потери данных, восстановление которых потом обойдётся в разы дороже.

Удаление записей заказов из wp_posts

-- Сначала посмотрите, сколько записей будет удалено
SELECT COUNT(*)
FROM wp_posts
WHERE post_type IN ('shop_order', 'shop_order_refund');

-- Удаление записей заказов
DELETE FROM wp_posts
WHERE post_type IN ('shop_order', 'shop_order_refund');

Для магазина с 100 000 заказами этот запрос может выполняться несколько минут. Если таблица очень большая, лучше удалять порциями:

-- Удаление порциями по 5000 записей
DELETE FROM wp_posts
WHERE post_type IN ('shop_order', 'shop_order_refund')
LIMIT 5000;

Запускайте этот запрос повторно, пока COUNT не покажет ноль.

Удаление метаданных заказов из wp_postmeta

Метаданные — это самая «тяжёлая» часть. Именно в wp_postmeta накапливается основной мусор.

-- Удаление метаданных, связанных с заказами
DELETE pm
FROM wp_postmeta pm
LEFT JOIN wp_posts p ON pm.post_id = p.ID
WHERE p.ID IS NULL;

Этот запрос удалит все метаполя, у которых нет родительской записи в wp_posts — то есть «осиротевшие» данные. После удаления заказов из wp_posts их метаданные как раз и становятся такими осиротевшими записями.

Удаление связанных записей

Не забудьте про дополнительные данные, которые WooCommerce создаёт для заказов:

-- Удаление связей заказов с товарами в wp_term_relationships
DELETE tr
FROM wp_term_relationships tr
INNER JOIN wp_posts p ON tr.object_id = p.ID
WHERE p.post_type IN ('shop_order', 'shop_order_refund');

-- Очистка wc_order_stats (если используется аналитика)
-- Будьте осторожны: эти данные могут понадобиться для отчётов

Оптимизация HPOS-таблиц после очистки

После удаления старых данных таблицы wp_posts и wp_postmeta физически не уменьшатся — MySQL просто пометит строки как удалённые. Нужно явно попросить базу данных освободить дисковое пространство.

Оптимизация через SQL

OPTIMIZE TABLE wp_posts;
OPTIMIZE TABLE wp_postmeta;
OPTIMIZE TABLE wp_wc_orders;
OPTIMIZE TABLE wp_wc_orders_meta;
OPTIMIZE TABLE wp_wc_order_addresses;
OPTIMIZE TABLE wp_wc_order_operational_data;

Время выполнения зависит от размера таблиц. Для таблицы wp_postmeta с миллионом строк оптимизация может занять 10-30 минут, на время которых таблица будет заблокирована. Лучше запускать это в период минимальной нагрузки.

Очистка транзакций WooCommerce

WooCommerce хранит логи транзакций платёжных систем, и со временем они занимают немало места. Если вы используете встроенный механизм транзакций:

-- Проверка наличия таблицы транзакций
SHOW TABLES LIKE 'wc_orders_transactions';

-- Удаление старых транзакций (старше 90 дней)
DELETE FROM wc_orders_transactions
WHERE date_created_gmt < DATE_SUB(NOW(), INTERVAL 90 DAY);
&#91;/sql&#93;

<h3>Пересчёт индексов</h3>

<p>После массовых удалений полезно обновить статистику индексов, чтобы оптимизатор запросов MySQL выбирал правильные планы выполнения:</p>

[sql]
ANALYZE TABLE wp_posts;
ANALYZE TABLE wp_postmeta;
ANALYZE TABLE wp_wc_orders;
ANALYZE TABLE wp_wc_orders_meta;

Автоматическая очистка: настройка расписания

Если вы не хотите повторять всё это вручную каждые несколько месяцев, настройте автоматическую очистку. WooCommerce позволяет удалять старые заказы через хуки:

// Удаление заказов старше 2 лет (кроме завершённых и отменённых за последний год)
add_action('woocommerce_cleanup_orders', function() {
    global $wpdb;
    
    $cutoff_date = date('Y-m-d H:i:s', strtotime('-2 years'));
    
    // Только если HPOS активен
    if (wc_get_container()->get(\Automattic\WooCommerce\Utilities\OrderUtil::class)->custom_orders_table_usage_is_enabled()) {
        $wpdb->query($wpdb->prepare(
            "DELETE FROM {$wpdb->prefix}wc_orders 
             WHERE date_created_gmt < %s 
             AND status IN ('trash', 'cancelled', 'failed')",
            $cutoff_date
        ));
    }
}, 10, 0);
&#91;/php&#93;

<p>Для запуска по расписанию зарегистрируйте хук в WordPress cron:</p>

[php]
// Регистрация cron-события при активации
register_activation_hook(__FILE__, function() {
    if (!wp_next_scheduled('woocommerce_cleanup_orders')) {
        wp_schedule_event(time(), 'monthly', 'woocommerce_cleanup_orders');
    }
});

// Очистка при деактивации
register_deactivation_hook(__FILE__, function() {
    wp_clear_scheduled_hook('woocommerce_cleanup_orders');
});

Удобный вариант — создать отдельный плагин-мустюплагин (wp-content/mu-plugins/cleanup-hpos.php), тогда он не будет зависеть от активации конкретного плагина.

Типичные ошибки при очистке таблиц WooCommerce

За годы работы с WooCommerce я видел немало «интересных» подходов к оптимизации базы данных. Вот самые частые ошибки:

Удаление заказов до завершения миграции. Звучит очевидно, но бывает. Если миграция прервалась на середине, а вы уже почистили wp_posts — заказы, которые не успели попасть в HPOS, потеряны навсегда. Всегда проверяйте статус миграции.

Использование phpMyAdmin для массовых удалений. Попытка удалить 100 000 строк через веб-интерфейс phpMyAdmin обычно заканчивается тайм-аутом и повреждением данных. Используйте CLI или порционные удаления.

Игнорирование бэкапов. «Да я просто гляну, что там, потом удалю». Сделайте дамп хотя бы через mysqldump:

mysqldump -u username -p database_name wp_posts wp_postmeta > backup_before_cleanup.sql

Удаление записей с неправильным post_type. Проверьте, что в вашей таблице нет кастомных типов записей, которые могут совпадать с «shop_order». Иногда плагины создают собственные типы записей.

Какой результат вы получите

Для магазина с 50 000 заказами типичный результат после полной очистки и оптимизации:

Добавить комментарий

Разработка сайтов на Wordpress