2026年8月21日

2026年8月21日

WordPressのデータベース肥大化を防ぐ方法

はじめに

データベースの肥大化は一度問題が起きてから対処するより、日常的な予防策で積み重ませない方が効果的です。リビジョン・Transient・autoloadオプション・スパムコメントの4つを制御するだけで、DBサイズを長期にわたって安定させられます。

症状・原因

  • サイト運用年数が長くなるにつれDBサイズが増え続けている
  • 削除しても数ヶ月後にまた肥大化している
  • バックアップが大きくなり保管コストが増えている
  • 定期メンテナンスの仕組みがなく手作業に頼っている

解決手順

ステップ1:リビジョンを最初から制限する

// wp-config.php: リビジョン蓄積を根本から防ぐ

// 投稿タイプ問わず最新3件のみ保持
define('WP_POST_REVISIONS', 3);

// 自動保存間隔を60秒→5分に延長(autosave 件数削減)
define('AUTOSAVE_INTERVAL', 300);
// functions.php: 投稿タイプ別に細かく制御
add_filter('wp_revisions_to_keep', function(int $num, WP_Post $post): int {
    return match($post->post_type) {
        'post'    => 5,
        'page'    => 10,
        'product' => 2,
        default   => 3,
    };
}, 10, 2);

ステップ2:Transientを自動削除・発生を抑制する

// functions.php: 期限切れTransientを週次で削除
add_action('weekly_transient_cleanup', function(): void {
    global $wpdb;
    $wpdb->query(
        "DELETE FROM {$wpdb->options}
         WHERE option_name LIKE '_transient_timeout_%'
           AND option_value < UNIX_TIMESTAMP()"
    );
    $wpdb->query(
        "DELETE FROM {$wpdb->options}
         WHERE option_name LIKE '_transient_%'
           AND option_name NOT LIKE '_transient_timeout_%'
           AND option_name NOT IN (
               SELECT REPLACE(option_name, '_timeout', '')
               FROM (SELECT option_name FROM {$wpdb->options}
                     WHERE option_name LIKE '_transient_timeout_%') AS t
           )"
    );
});

if (!wp_next_scheduled('weekly_transient_cleanup')) {
    wp_schedule_event(time(), 'weekly', 'weekly_transient_cleanup');
}
# WP-CLI で即時削除
wp transient delete --expired

# 全Transientを削除(キャッシュが不要な場合)
wp transient delete --all

ステップ3:autoloadオプションを管理する

-- autoload サイズを確認
SELECT
    autoload,
    COUNT(*) AS count,
    ROUND(SUM(LENGTH(option_value)) / 1024, 1) AS size_kb
FROM wp_options
GROUP BY autoload;

-- 大きな autoload=yes のオプションを特定
SELECT option_name,
    ROUND(LENGTH(option_value) / 1024, 1) AS size_kb,
    autoload
FROM wp_options
WHERE autoload = 'yes'
  AND LENGTH(option_value) > 10240  -- 10KB以上
ORDER BY LENGTH(option_value) DESC
LIMIT 20;
// 新規オプション登録時は autoload=false を明示する
// ❌ デフォルトは autoload=yes
add_option('my_plugin_data', $data);

// ✅ 不要な自動読み込みを避ける
add_option('my_plugin_data', $data, '', 'no');
update_option('my_plugin_data', $data, false);

ステップ4:スパム・ゴミ箱を自動削除する

// functions.php: スパム・ゴミ箱の定期自動削除
add_action('daily_db_cleanup', 'run_daily_db_cleanup');

function run_daily_db_cleanup(): void {
    global $wpdb;

    // 30日超スパムコメントを削除
    $wpdb->query(
        "DELETE FROM {$wpdb->comments}
         WHERE comment_approved = 'spam'
           AND comment_date < DATE_SUB(NOW(), INTERVAL 30 DAY)"
    );

    // 7日超ゴミ箱コメントを削除
    $wpdb->query(
        "DELETE FROM {$wpdb->comments}
         WHERE comment_approved = 'trash'
           AND comment_date < DATE_SUB(NOW(), INTERVAL 7 DAY)"
    );

    // 孤立した commentmeta を削除
    $wpdb->query(
        "DELETE pm FROM {$wpdb->commentmeta} pm
         LEFT JOIN {$wpdb->comments} c ON pm.comment_id = c.comment_ID
         WHERE c.comment_ID IS NULL"
    );

    // 孤立した postmeta を削除
    $wpdb->query(
        "DELETE pm FROM {$wpdb->postmeta} pm
         LEFT JOIN {$wpdb->posts} p ON pm.post_id = p.ID
         WHERE p.ID IS NULL"
    );
}

if (!wp_next_scheduled('daily_db_cleanup')) {
    wp_schedule_event(strtotime('tomorrow 04:00:00'), 'daily', 'daily_db_cleanup');
}

ステップ5:月次 OPTIMIZE TABLE でフラグメンテーションを防ぐ

// functions.php: 月次テーブル最適化
add_action('monthly_db_optimize', 'optimize_wp_tables');

function optimize_wp_tables(): void {
    global $wpdb;

    $tables = $wpdb->get_results(
        "SELECT table_name, data_free
         FROM information_schema.tables
         WHERE table_schema = DATABASE()
           AND data_free > 5242880",  // 5MB以上のオーバーヘッドのみ
        ARRAY_A
    );

    foreach ($tables as $table) {
        $wpdb->query("OPTIMIZE TABLE `{$table['table_name']}`");
    }

    update_option('last_db_optimize', current_time('mysql'), false);
}

if (!wp_next_scheduled('monthly_db_optimize')) {
    // 毎月1日 AM 3:00 に実行
    $first_of_month = mktime(3, 0, 0, (int)date('n'), 1, (int)date('Y'));
    if ($first_of_month < time()) {
        $first_of_month = mktime(3, 0, 0, (int)date('n') + 1, 1, (int)date('Y'));
    }
    wp_schedule_event($first_of_month, 'monthly', 'monthly_db_optimize');
}
# crontab で月次 OPTIMIZE(サーバー直接実行の場合)
# 毎月1日 AM3:00 に実行
0 3 1 * * wp --path=/var/www/html db optimize

注意事項

  • WP_POST_REVISIONS=3に設定しても、既存の古いリビジョンは削除されません。別途DELETE FROM wp_posts WHERE post_type='revision'で既存分を整理してください
  • OPTIMIZE TABLEはInnoDB形式のテーブルでは実際にはテーブル再構築(ALTER TABLE ... FORCE相当)が走るため、大きなテーブルではI/Oが増加します。アクセスの少ない夜間に実行してください
  • Transientはプラグインのキャッシュとして使われているため、全削除するとページキャッシュが一時的に消える場合があります。--expiredオプションで期限切れのみ削除するのが安全です

まとめ

DBの肥大化予防は①WP_POST_REVISIONS=3AUTOSAVE_INTERVAL=300でリビジョン上限を設定、②wp transient delete --expiredと週次cronで期限切れTransientを継続削除、③新規add_optionautoload='no'を明示して不要な自動読み込みを防止、④daily cronで30日超スパム・7日超ゴミ箱・孤立postmeta/commentmetaを自動削除、⑤月次cronでオーバーヘッド5MB超のテーブルのみOPTIMIZE TABLEを実施します。

お気軽にご相談ください

お見積りへ お問い合わせへ