2026年5月30日

2026年5月30日

WordPressのMySQLエラーを解決する方法

はじめに

WordPressで「データベース接続確立エラー」が表示されてサイトにアクセスできない・管理画面で「WordPress データベースにエラーが見つかりました」と警告が出ている・$wpdb->last_errorTable 'wp_options' is marked as crashedと表示される・MySQLの接続数制限(Too many connections)に達してサイトがダウンするといった問題は、MySQLサーバーの停止・テーブルの破損・接続プールの枯渇・インデックス不足によるスロークエリが原因です。

症状・原因

  • wp-config.phpのDB設定は正しいのに「データベース接続確立エラー」が出続ける
  • wp_optionsテーブルが破損してautoloadクエリが異常に遅くなっている
  • MySQLのmax_connectionsに達して新しい接続が拒否されている
  • wp_postsテーブルへのクエリがUsing filesortで遅くなっている

解決手順

ステップ1:MySQLエラーの状態を診断する

# WP-CLIでデータベース接続とエラーを確認
wp db check

# 破損テーブルを確認
wp db query "CHECK TABLE wp_options, wp_posts, wp_postmeta, wp_usermeta;"

# MySQLの接続状態を確認
wp db query "SHOW STATUS LIKE 'Threads_connected';"
wp db query "SHOW VARIABLES LIKE 'max_connections';"

# スロークエリを確認
wp eval "
global \$wpdb;
\$wpdb->show_errors();
\$results = \$wpdb->get_results(
    'SELECT * FROM information_schema.PROCESSLIST
     WHERE TIME > 2
     ORDER BY TIME DESC
     LIMIT 10',
    ARRAY_A
);
foreach (\$results as \$r) {
    printf('Time: %ds | DB: %s | Query: %s' . PHP_EOL,
        \$r['TIME'], \$r['DB'], substr(\$r['INFO'], 0, 80));
}
"

# wp_options の autoload サイズを確認
wp eval "
global \$wpdb;
\$result = \$wpdb->get_row(
    \"SELECT COUNT(*) as count, SUM(LENGTH(option_value)) as size
     FROM {\$wpdb->options}
     WHERE autoload = 'yes'\"
);
echo 'Autoload count: '  . \$result->count . PHP_EOL;
echo 'Autoload size: '   . number_format(\$result->size / 1024, 2) . ' KB' . PHP_EOL;
"

ステップ2:破損テーブルを修復する

# WP-CLIでテーブルを修復
wp db repair

# 特定のテーブルだけ修復
wp db query "REPAIR TABLE wp_options;"
wp db query "REPAIR TABLE wp_posts;"
wp db query "OPTIMIZE TABLE wp_options;"
wp db query "OPTIMIZE TABLE wp_posts;"

# MyISAMテーブルの修復(mysqlcheckコマンド)
# mysqlcheck -u dbuser -p --repair --all-databases

# InnoDBテーブルの修復(データベースをダンプして再インポート)
wp db export /tmp/wp_backup.sql
wp db drop --yes
wp db create
wp db import /tmp/wp_backup.sql
// wp-config.php: テーブル修復モードを有効化(緊急時)
define('WP_ALLOW_REPAIR', true);
// その後 https://example.com/wp-admin/maint/repair.php にアクセス
// 修復後は必ず define を削除すること

ステップ3:Too many connectionsエラーを解決する

# 現在の接続数と設定を確認
wp db query "SHOW STATUS LIKE 'Max_used_connections';"
wp db query "SHOW VARIABLES LIKE 'max_connections';"
wp db query "SHOW STATUS LIKE 'Aborted_connects';"

# MySQL設定を変更(my.cnf)
# [mysqld]
# max_connections = 500
# wait_timeout = 28800
# interactive_timeout = 28800
# connect_timeout = 10

# 接続を持ち続けているプロセスを確認・強制終了
wp db query "SHOW FULL PROCESSLIST;"
# wp db query "KILL {process_id};"
// wp-config.php: データベース接続の最適化
define('DB_HOST', 'localhost:/var/run/mysqld/mysqld.sock');  // Unixソケットで接続(高速)

// wp-config.php: 接続タイムアウトを設定
// WordPress 標準の $wpdb を拡張
add_action('init', function(): void {
    global $wpdb;
    // 接続エラーのカスタムハンドラー
    $wpdb->show_errors(false);
    $wpdb->suppress_errors(true);
});

ステップ4:wp_optionsのautoloadを最適化する

// functions.php: autoloadの最適化

// ① 大きなオプションをautoload=noに変更
function disable_autoload_for_large_options(): void {
    global $wpdb;

    // 100KB以上のautoloadオプションを検索
    $large_options = $wpdb->get_results(
        "SELECT option_name, LENGTH(option_value) as size
         FROM {$wpdb->options}
         WHERE autoload = 'yes'
           AND LENGTH(option_value) > 102400
         ORDER BY size DESC",
        ARRAY_A
    );

    foreach ($large_options as $option) {
        $wpdb->update(
            $wpdb->options,
            ['autoload' => 'no'],
            ['option_name' => $option['option_name']]
        );
        error_log("[DB Opt] Disabled autoload for: {$option['option_name']} ({$option['size']} bytes)");
    }
}

// ② トランジェントをautoloadから除外(非推奨のトランジェントを削除)
add_action('admin_init', function(): void {
    if (!get_option('_transient_cleanup_done')) {
        global $wpdb;
        $deleted = $wpdb->query(
            "DELETE FROM {$wpdb->options}
             WHERE option_name LIKE '_transient_%'
               AND option_name NOT LIKE '_transient_timeout_%'"
        );
        update_option('_transient_cleanup_done', time());
        error_log("[DB Opt] Deleted {$deleted} expired transients");
    }
});

// ③ カスタムクエリにインデックスを活用
add_action('pre_get_posts', function(WP_Query $query): void {
    // meta_queryのインデックスを確実に使用
    if ($query->is_main_query() && $query->get('meta_key')) {
        // EXPLAIN で確認: wp db query "EXPLAIN SELECT ..."
    }
});

ステップ5:MySQLのパフォーマンスを継続的に監視する

# スロークエリログを有効化
wp db query "SET GLOBAL slow_query_log = 'ON';"
wp db query "SET GLOBAL long_query_time = 1;"
wp db query "SET GLOBAL slow_query_log_file = '/var/log/mysql/slow.log';"

# インデックスを確認・追加
wp db query "SHOW INDEX FROM wp_postmeta;"
wp db query "SHOW INDEX FROM wp_options;"

# postmetaにインデックスが不足している場合
# wp db query "ALTER TABLE wp_postmeta ADD INDEX meta_value (meta_value(20));"

# データベースサイズを確認
wp db size --tables --format=table
// functions.php: データベースクエリを監視
add_action('shutdown', function(): void {
    if (!WP_DEBUG || !current_user_can('manage_options')) {
        return;
    }
    global $wpdb;
    if ($wpdb->num_queries > 50) {
        error_log(sprintf(
            '[DB Monitor] %d queries in %.4fs on %s',
            $wpdb->num_queries,
            timer_stop(0, 4),
            $_SERVER['REQUEST_URI'] ?? 'unknown'
        ));
    }
});

注意事項

  • WP_ALLOW_REPAIRwp-config.phpに追加したままにしないでください。誰でもデータベースを修復・最適化できる状態になります。修復完了後は必ず削除またはfalseに変更してください
  • wp db repairREPAIR TABLEはMyISAMテーブルでは有効ですが、InnoDBテーブルでは効果が限定的です。InnoDBのデータ破損はダンプ→再インポートで対処するのが確実です
  • Too many connectionsエラーはmax_connectionsを増やすだけでなく、接続を長時間保持するプロセスを特定して修正することが根本的な解決策です

まとめ

WordPress MySQLエラーの解決は①wp db checkでテーブル整合性確認・SHOW PROCESSLISTでスロークエリ検出・autoloadサイズとトランジェント数の確認、②wp db repairREPAIR TABLEでMyISAMテーブル修復・InnoDBはダンプ→再インポートで対処・WP_ALLOW_REPAIRで修復ページを有効化(使用後は削除)、③SHOW STATUS LIKE 'Threads_connected'で接続数確認・my.cnfmax_connections増加・Unixソケット接続でオーバーヘッド削減、④100KB超のautoloadオプションをautoload='no'に変更・期限切れトランジェントを削除・カスタムクエリにEXPLAINでインデックス使用を確認、⑤SET GLOBAL slow_query_log='ON'でスロークエリログを有効化・wp db sizeでテーブルサイズ監視・クエリ数が50超でerror_logに記録の手順で解決します。

お気軽にご相談ください

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