woocommerce-performance について
WooCommerceの開発者や運営者向けに、ストアの性能調査と改善方法をまとめたスキルです。クエリ分析、オブジェクトキャッシュ、データベース最適化、定期的なクリーンアップを扱います。
主な機能
- 01Query Monitorによるクエリ分析
- 02Redis永続オブジェクトキャッシュの設定
- 03WooCommerceのDBクエリ最適化
- 04セッションと一時データの削除
- 05自動読み込みオプションの確認
活用シーン
- 01ストアページの表示遅延の調査
- 02AJAXによるDB負荷の軽減
- 03増加したWooCommerceセッションテーブルの管理
- 04wp_optionsの自動読み込み負荷の削減
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ finsilabs/ awesome-ecommerce-skills --skill woocommerce-performanceインストール後、Claude Code で/woocommerce-performanceを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
tile.jsonevals/database-cleanup-and-session-maintenance/criteria.jsontask.mdevals/query-optimization-and-n-1-prevention/criteria.jsontask.mdevals/redis-object-cache-configuration/criteria.jsontask.md詳細
WooCommerce のパフォーマンス
概要
WooCommerce のパフォーマンス問題は、通常、インデックスのないメタテーブルに対する高コストな商品クエリ、制限のない AJAX のカート/チェックアウト呼び出し、永続オブジェクトキャッシュの欠如、肥大化した wp_options の自動読み込みデータ、そしてクリーンアップされずに増え続ける wp_woocommerce_sessions と注文テーブルという、5 つの原因から生じます。このスキルでは、プロファイリング、クエリの最適化、Redis オブジェクトキャッシュ、定期メンテナンスのルーチンを扱います。
このスキルを使う場面
- 中程度のトラフィック(同時接続ユーザー数百人)で、ストアのページ読み込みが遅い場合
- WooCommerce の AJAX 呼び出し(カートへの追加、送料計算)中にサーバーの CPU 使用率が急上昇する場合
- New Relic または Query Monitor で、MySQL のクエリ時間が主なボトルネックとして表示される場合
wp_optionsテーブルの自動読み込みサイズが 1~2 MB を超える場合wp_woocommerce_sessionsの行数が数百万件に達し、セッション検索が遅くなっている場合- トラフィックの多いストアで MySQL の負荷を軽減するために、Redis または Memcached を導入する場合
主な手順
-
まず Query Monitor でプロファイリングする
Query Monitor プラグインをインストールして、管理画面とフロントエンドの遅いクエリを特定します。
# Via WP-CLI wp plugin install query-monitor --activate次の項目に注目します。
- ページ読み込みごとのクエリ数(50 件を超える場合は要注意)
- 重複クエリ(同じクエリが複数回実行されている)
- 遅いクエリ(個々のクエリに 100ms 以上かかる)
- 大きな結果セット(数千行を返すクエリ)
本番環境のプロファイリングでは、MySQL のスロークエリログを有効にします。
SET GLOBAL slow_query_log = 'ON'; SET GLOBAL long_query_time = 0.5; SET GLOBAL slow_query_log_file = '/var/log/mysql/slow.log'; -
Redis の永続オブジェクトキャッシュを有効にする
WooCommerce で最も効果の高い最適化です。これがないと、ページの読み込みごとに同じ商品データを MySQL から再取得します。
# Install Redis server sudo apt install redis-server # Install the Redis Object Cache plugin wp plugin install redis-cache --activate wp redis enablewp-config.phpで設定します。define('WP_REDIS_HOST', '127.0.0.1'); define('WP_REDIS_PORT', 6379); define('WP_REDIS_DATABASE', 0); define('WP_REDIS_TIMEOUT', 1); define('WP_REDIS_READ_TIMEOUT', 1); define('WP_REDIS_MAXTTL', 86400); // 24 hours max TTL // Selective cache groups — exclude user sessions from Redis (they change constantly) define('WP_REDIS_IGNORED_GROUPS', ['wc_session_id', 'counts', 'plugins']); -
WooCommerce 固有のデータベースクエリを最適化する
<?php // Add missing indexes for common WooCommerce product queries // Run once via WP-CLI: wp eval-file add-indexes.php global $wpdb; // Index for product meta queries (stock status, visibility, price) $wpdb->query(" ALTER TABLE {$wpdb->postmeta} ADD INDEX wc_product_meta_lookup (meta_key(32), meta_value(64)) "); // Index for order meta queries $wpdb->query(" ALTER TABLE {$wpdb->postmeta} ADD INDEX wc_order_customer_lookup (meta_key(32), meta_value(100)) ");WooCommerce の HPOS(High-Performance Order Storage)を使用して、注文データを投稿メタから移動します。
// wp-config.php — enable HPOS (WooCommerce 7.1+) // This is configured via WooCommerce Settings → Advanced → Features // Enables dedicated order tables: wp_wc_orders, wp_wc_order_items, etc.# Check HPOS status wp wc hpos status # Migrate orders to HPOS wp wc hpos migrate --batch-size=500 -
トランジェント、セッション、ログテーブルをクリーンアップする
<?php // Scheduled cleanup — run daily via Action Scheduler add_action('my_plugin_daily_cleanup', function () { global $wpdb; // Delete expired WooCommerce sessions (older than 48 hours) $expiry_threshold = time() - (48 * HOUR_IN_SECONDS); $wpdb->query($wpdb->prepare(" DELETE FROM {$wpdb->prefix}woocommerce_sessions WHERE session_expiry < %d LIMIT 5000 ", $expiry_threshold)); // Delete expired transients $wpdb->query(" DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_timeout_%' AND option_value < UNIX_TIMESTAMP() LIMIT 5000 "); $wpdb->query(" DELETE t FROM {$wpdb->options} t LEFT JOIN {$wpdb->options} e ON e.option_name = REPLACE(t.option_name, '_transient_', '_transient_timeout_') WHERE t.option_name LIKE '_transient_%' AND e.option_id IS NULL LIMIT 5000 "); // Rotate WooCommerce logs older than 30 days $wpdb->query(" DELETE FROM {$wpdb->prefix}woocommerce_log WHERE timestamp < DATE_SUB(NOW(), INTERVAL 30 DAY) LIMIT 10000 "); }); // Register the scheduled event add_action('init', function () { if (!as_next_scheduled_action('my_plugin_daily_cleanup')) { as_schedule_recurring_action(strtotime('tomorrow midnight'), DAY_IN_SECONDS, 'my_plugin_daily_cleanup'); } }); -
wp_options の自動読み込みテーブルを最適化する
自動読み込みオプションは、ページリクエストごとに読み込まれます。自動読み込みデータの肥大化は、大きなオーバーヘッドを引き起こします。
-- Find large autoloaded options SELECT option_name, LENGTH(option_value) as size_bytes FROM wp_options WHERE autoload = 'yes' ORDER BY LENGTH(option_value) DESC LIMIT 30;<?php // Fix: Store plugin data as transients or post meta instead of autoloaded options // Bad — autoloaded, loaded on every request update_option('my_plugin_product_cache', $large_array, true); // Good — not autoloaded update_option('my_plugin_product_cache', $large_array, false); // Better — use transient with expiry set_transient('my_plugin_product_cache', $large_array, HOUR_IN_SECONDS); // Disable autoload for existing options global $wpdb; $wpdb->update( $wpdb->options, ['autoload' => 'no'], ['option_name' => 'woocommerce_attribute_taxonomies'] );
例
商品ループのフラグメントキャッシュ
<?php
// Cache rendered product card HTML in Redis to avoid re-rendering
function render_product_card_cached(int $product_id): string {
$cache_key = "product_card_{$product_id}_" . get_locale();
$cached = wp_cache_get($cache_key, 'product_cards');
if ($cached !== false) {
return $cached;
}
$product = wc_get_product($product_id);
if (!$product) return '';
ob_start();
wc_get_template('content-product.php', ['product' => $product]);
$html = ob_get_clean();
// Cache for 30 minutes; invalidate on product update
wp_cache_set($cache_key, $html, 'product_cards', 30 * MINUTE_IN_SECONDS);
return $html;
}
// Purge cache when product is updated
add_action('woocommerce_update_product', function (int $product_id) {
wp_cache_delete("product_card_{$product_id}_" . get_locale(), 'product_cards');
// Also delete all locale variants
wp_cache_flush_group('product_cards');
});
N+1 商品クエリを検出して修正する
<?php
// Bad — triggers N+1 queries (one per product in loop)
$product_ids = wc_get_featured_product_ids();
foreach ($product_ids as $id) {
$product = wc_get_product($id); // <-- separate query for each product
echo $product->get_name();
}
// Good — prime the cache before the loop
$product_ids = wc_get_featured_product_ids();
// Pre-warm the object cache with all products in a single query
$products = array_map('wc_get_product', $product_ids); // Still N queries without this:
// Better — use WC_Product_Query with ID filter (single query)
$products = wc_get_products([
'include' => $product_ids,
'limit' => count($product_ids),
'return' => 'objects',
]);
foreach ($products as $product) {
echo $product->get_name(); // data already loaded
}
ベストプラクティス
- WooCommerce 7.1 以降のストアでは HPOS(High-Performance Order Storage)を有効にします — 注文データを適切なインデックスを持つ専用テーブルに移動し、注文データに対する
wp_postmetaのボトルネックを解消します - 何よりも先に Redis/Memcached のオブジェクトキャッシュを追加します — WordPress と WooCommerce がすべてのリクエストで実行する冗長な MySQL クエリをなくし、データベースの負荷を 60~80% 削減できることもよくあります
- Action Scheduler を使って、アクセスの少ない時間帯にクリーンアップを実行します — クリーンアップ中にテーブルが長時間ロックされるのを避けるため、DELETE 操作は 1 回の実行につき 5000 行を上限としてバッチ処理します
WP_DEBUG_LOGとSAVEQUERIESは開発環境でのみ使用します — 本番環境でこれらを有効にするとパフォーマンスが低下します。本番環境のプロファイリングには APM ツール(New Relic、Datadog)を使用します- 管理画面の注文クエリをページネーションします — すべての注文を一度に読み込む(
posts_per_page: -1)と、MySQL がロックされてタイムアウトが発生します。常にpagedを使用し、posts_per_pageは 100 以下にします WP_REDIS_IGNORED_GROUPSを設定して、変動の多いデータ(カート、セッションカウンター)を Redis から除外します — トラフィックの多いチェックアウトページでキャッシュスタンピードが発生するのを防ぎますwp_optionsテーブルのサイズを毎週監視します — 自動読み込みデータが 800KB を超えた場合にアラートが出るよう設定します。よくある原因は、大きな配列を自動読み込みオプションとして保存するプラグインです
よくある落とし穴
| 問題 | 解決策 |
|---|---|
| Redis キャッシュを使っても MySQL クエリが減らない | Query Monitor で wp_cache_get のヒット率を確認します。ヒット率が低い場合、キャッシュキーが頻繁に無効化されすぎているか、TTL が短すぎます |
| クリーンアップクエリによってテーブルレベルのロックが発生する | DELETE クエリで行レベルの LIMIT 句を使用し(1 回の実行につき最大 5000 行)、トラフィックが少ない時間帯にスケジュールします |
| HPOS への移行によってサードパーティ製プラグインが動作しなくなる | HPOS を有効にする前に、すべてのプラグインで get_post_meta(order_id, ...) が使われていないか監査します。該当箇所は $order->get_meta() を使うよう更新する必要があります |
| インデックスがあるのに商品一覧の表示が遅い | wc_lookup_table_enabled が有効か確認します。WooCommerce 3.7+ では wp_wc_product_meta_lookup テーブルが導入され、商品クエリが大幅に高速化されます |
| 商品インポート後にオブジェクトキャッシュが古いままになる | 一括インポートスクリプトの最後で wp_cache_flush() または wp_cache_flush_group('products') を呼び出し、古い商品キャッシュを無効化します |
| クリーンアップを行ってもセッションテーブルが増え続ける | WC_Session_Handler がデータベースバックエンド(PHP セッションではなく)を使用していることを確認します。デフォルトの Cookie ベースのセッションでは DB 行は作成されませんが、カスタムプラグインによって DB セッションが強制される場合があります |
関連スキル
- @woocommerce-plugin-development
- @woocommerce-rest-api
- @database-query-optimization
- @caching-strategies
- @infrastructure-performance