XserverでWordPressを長く運用していると、投稿・メタ・プラグイン独自データが積み上がり、Xserver データベース 容量削減が必要になることがあります。
特にMySQLのバージョン移行や他サーバーからのWordPress移転では、公式マニュアル上で「1データベースあたり2GB超」が利用不可条件として明記されています。
本記事では、MariaDB(phpMyAdmin)上で古い投稿を期間指定で削除し、関連テーブルの整合性を保ったうえで、孤児データの整理とOPTIMIZE TABLEまで進める手順をまとめます。
実行前のバックアップと削除対象の確認は必須です。
レコードをDELETEしただけでは、表示上の行数は減っても物理ファイル容量はすぐには減りません。OPTIMIZE TABLEで再構築しても、必ずOSへ空きが返るとは限りません。確認までセットで行ってください。
発生した問題と原因(2GB制限)
Xserver公式マニュアルでは、移行系ツールごとにデータベース容量の上限が案内されています。
代表的な根拠は次の2点です。
次の条件に該当する場合、『MySQL移行ツール』を利用することができません。
引用:MySQL移行ツール – エックスサーバー公式マニュアル
……移行するデータベースの容量が2GBを超えている場合……
移行元のWordPressが以下に当てはまる場合、エラーとなります。
引用:WordPress簡単移行 – エックスサーバー公式マニュアル
……データベースの容量が2GBを超えている場合……
一方、新サーバー簡単移行は旧サーバーから新基盤へデータコピーする機能で、移行先のデータベースはMariaDB 10.11へ変わります。
公式の「新サーバー簡単移行」紹介ページ自体には2GBという数値上限は書かれていませんが、同環境ではMySQL移行ツールやWordPress簡単移行と同じく、巨大なDBが作業のボトルネックになりやすいです。
なお、通常運用時の目安容量は「MySQLデータベース1個につき5GB」と案内されています。
著しく超過したり負荷が大きい場合はリソース制限の対象になり得るため、移行直前でなくても整理の価値があります。
容量が膨らむ主因は、ほぼ例外なくwp_posts(本文)とwp_postmeta(カスタムフィールド)です。
さらにAll in One SEO(AIOSEO)を使っている場合はwp_aioseo_postsに投稿単位のSEOデータが残ります。
投稿だけ消してメタやタクソノミー関連を残すと、孤児レコードが増えて最適化しにくくなります。
削除対象テーブルの調査方法
作業はサーバーパネルのphpMyAdminから行います。
誤削除を防ぐため、いきなりDELETEせず、容量の内訳と削除候補の件数を先に確認します。
作業前のバックアップ(必須)
SQLでの一括削除は取り返しがつきません。
少なくとも次を取ってから進めてください。
- phpMyAdminの「エクスポート」で対象データベース全体をSQL形式で保存する
- 可能ならXserverのバックアップ機能やローカルへのDBダンプも併用する
- 本番で試す前に、コピーした検証用DBで手順を一度通す
テーブル接頭辞がwp_でないサイトもあります。wp-config.phpの$table_prefixを確認し、以降のSQLは実環境の接頭辞に置き換えてください。
テーブル別の容量を確認する
どのテーブルが大きいかを把握します。
phpMyAdminのSQLタブで次を実行します。
SELECT
table_name AS table,
ROUND((data_length + index_length) / 1024 / 1024, 2) AS size_mb,
table_rows
FROM information_schema.tables
WHERE table_schema = DATABASE()
ORDER BY (data_length + index_length) DESC
LIMIT 20;
wp_posts・wp_postmeta・プラグイン独自テーブルが上位に来るのが典型です。table_rowsはInnoDBでは概算値なので、厳密な件数はCOUNTで確認します。
期間指定の削除候補を確認する
ここでは「投稿(post)」のうち、ある日付より前のものを対象にします。
固定ページ(page)やメディア(attachment)を巻き込みたくない場合は、post_typeを必ず絞ってください。
-- 日付と件数の目安(例: 2020-01-01より前の投稿)
SELECT post_type, post_status, COUNT(*) AS cnt
FROM wp_posts
WHERE post_type = 'post'
AND post_date < '2020-01-01 00:00:00'
GROUP BY post_type, post_status
ORDER BY cnt DESC;
-- 削除候補の一覧サンプル
SELECT ID, post_title, post_date, post_status
FROM wp_posts
WHERE post_type = 'post'
AND post_date < '2020-01-01 00:00:00'
ORDER BY post_date ASC
LIMIT 100;
関連データの件数も合わせて見ます。
SELECT COUNT(*) AS postmeta_cnt
FROM wp_postmeta pm
INNER JOIN wp_posts p ON pm.post_id = p.ID
WHERE p.post_type = 'post'
AND p.post_date < '2020-01-01 00:00:00';
SELECT COUNT(*) AS term_rel_cnt
FROM wp_term_relationships tr
INNER JOIN wp_posts p ON tr.object_id = p.ID
WHERE p.post_type = 'post'
AND p.post_date < '2020-01-01 00:00:00';
-- AIOSEOを使っている場合のみ
SELECT COUNT(*) AS aioseo_cnt
FROM wp_aioseo_posts a
INNER JOIN wp_posts p ON a.post_id = p.ID
WHERE p.post_type = 'post'
AND p.post_date < '2020-01-01 00:00:00';
wp_aioseo_postsが無い環境では最後のSELECTはエラーになります。
AIOSEO未導入ならそのステップは飛ばしてください。
カラム構成を確認したいときはSHOW COLUMNS FROM wp_aioseo_posts;でpost_idがあることを確認します。
期間指定で投稿と関連データを削除するSQL
削除は「関連テーブル → 投稿本体」の順が安全です。
投稿を先に消すとJOIN条件が使えなくなり、孤児の特定が難しくなります。
phpMyAdminでの実行手順
- サーバーパネルからphpMyAdminを開き、対象データベースを選択する
- 「SQL」タブを開く
- 先にSELECTで件数と一覧を確認する
- 問題なければ、関連テーブルのDELETEを1文ずつ実行する
- 最後に
wp_postsをDELETEする
大量削除ではタイムアウトやロック待ちが起きやすいです。
一度に数年分を消すより、年単位・月単位に分けて実行した方が安定します。
トランザクションを使う場合
InnoDBであれば、小さな単位ならトランザクションで包めます。
確認後にCOMMIT、想定外ならROLLBACKできます。
START TRANSACTION;
-- ここに DELETE 文を順に書く
-- 件数やサイト表示を確認して問題なければ:
COMMIT;
-- 問題があれば:
-- ROLLBACK;
ただし巨大なDELETEを1トランザクションにまとめると、UNDOログが膨らみ失敗しやすくなります。
phpMyAdminの実行時間制限にも引っかかるため、実務では「少数バッチ+都度COMMIT」が無難です。
関連テーブルごと削除するSQL例
日付条件の2020-01-01は、必ず自サイトの方針に合わせて変更してください。post_type = 'post'以外を含めると、固定ページや添付まで消える危険があります。
-- 1) 投稿メタ
DELETE pm
FROM wp_postmeta pm
INNER JOIN wp_posts p ON pm.post_id = p.ID
WHERE p.post_type = 'post'
AND p.post_date < '2020-01-01 00:00:00';
-- 2) カテゴリ・タグなどの関連
DELETE tr
FROM wp_term_relationships tr
INNER JOIN wp_posts p ON tr.object_id = p.ID
WHERE p.post_type = 'post'
AND p.post_date < '2020-01-01 00:00:00';
-- 3) AIOSEO(テーブルがある場合のみ)
DELETE a
FROM wp_aioseo_posts a
INNER JOIN wp_posts p ON a.post_id = p.ID
WHERE p.post_type = 'post'
AND p.post_date < '2020-01-01 00:00:00';
-- 4) 投稿本体(最後)
DELETE FROM wp_posts
WHERE post_type = 'post'
AND post_date < '2020-01-01 00:00:00';
コメント(wp_comments)や、カスタム投稿タイプ、WooCommerce注文などは条件に含めていません。
それらも消す場合は、別途テーブル構造を調べてから専用のDELETEを書いてください。
孤児データの削除とテーブル最適化
投稿削除後も、参照先のないメタや関連行が残ることがあります。
続けて孤児データを掃除し、テーブルを再構築します。
孤児データの削除
-- 投稿が存在しない postmeta
DELETE pm
FROM wp_postmeta pm
LEFT JOIN wp_posts p ON pm.post_id = p.ID
WHERE p.ID IS NULL;
-- 投稿が存在しない term_relationships
DELETE tr
FROM wp_term_relationships tr
LEFT JOIN wp_posts p ON tr.object_id = p.ID
WHERE p.ID IS NULL;
-- AIOSEOの孤児(テーブルがある場合のみ)
DELETE a
FROM wp_aioseo_posts a
LEFT JOIN wp_posts p ON a.post_id = p.ID
WHERE p.ID IS NULL;
用語そのもの(wp_terms / wp_term_taxonomy)の整理は、共有カテゴリを誤って消すリスクがあります。
必要なら件数確認のうえ、未使用タームだけを対象にする別手順にしてください。
追加で確認したいデータ
投稿の期間削除だけでは足りないケースもあります。
次も容量の常連です。
| 対象 | 確認のヒント |
|---|---|
| リビジョン | post_type = 'revision'。本文の履歴が多く、wp_postsを肥大化させやすい |
| 自動下書き・ゴミ箱 | auto-draft / trash。不要なら件数確認後に削除 |
| 添付ファイル(attachment) | DB行を消してもディスク上の画像ファイルは残る。ファイル側の整理は別作業 |
| プラグイン独自テーブル | wp_aioseo_*、wp_yoast_*、フォーム・キャッシュ系など。information_schemaで上位を確認 |
| トランジェント | wp_optionsの_transient_*。期限切れが多いと肥大化する |
リビジョンだけを消す例です。
-- 事前確認
SELECT COUNT(*) FROM wp_posts WHERE post_type = 'revision';
-- リビジョン本体とメタ
DELETE pm FROM wp_postmeta pm
INNER JOIN wp_posts p ON pm.post_id = p.ID
WHERE p.post_type = 'revision';
DELETE FROM wp_posts WHERE post_type = 'revision';
OPTIMIZE TABLEで再構築する
MariaDB/MySQLのInnoDBでは、DELETEしても空き領域がすぐOSへ返らないことがあります。OPTIMIZE TABLEはテーブルを再構築し、断片化を減らすための処理です。
OPTIMIZE TABLE
wp_posts,
wp_postmeta,
wp_term_relationships,
wp_aioseo_posts;
InnoDBでは内部的に再作成+ANALYZE相当になることが多く、実行中は対象テーブルにロックがかかります。
アクセスの少ない時間帯に行い、ディスク空きがテーブルサイズの余裕分あることを確認してください。
容量削減の確認方法と注意点
削除後は、レコード数と物理容量の両方を見ます。
ここを混同すると「消したのに2GBのまま」と誤解しやすいです。
確認用SQL
-- 再度テーブルサイズを確認
SELECT
table_name AS table,
ROUND((data_length + index_length) / 1024 / 1024, 2) AS size_mb
FROM information_schema.tables
WHERE table_schema = DATABASE()
AND table_name IN (
'wp_posts',
'wp_postmeta',
'wp_term_relationships',
'wp_aioseo_posts'
);
あわせてサーバーパネルの「MySQL設定」などで、データベース全体の使用量表示も確認します。
移行ツールが参照する容量は、この全体サイズ側です。
レコード削除と物理容量は別物
DELETEは行を削除マークし、再利用可能な空きをテーブル内に作ります。
MariaDB公式の説明でも、InnoDBでは削除後の空きがすぐにOSへ返らないことが前提として書かれています。
そのため次の点を押さえてください。
- COUNTが減っても、ファイルサイズが同じペースで減るとは限らない
OPTIMIZE TABLEは再構築により縮小しやすいが、共有テーブルスペース(ibdata1)利用時などは効果が限定的、あるいは一時的に肥大することもある- レンタルサーバーではストレージエンジン設定を利用者が変えられないことが多いため、「必ず数GB減る」と保証できない
- それでも巨大な本文・メタを消せば、エクスポートサイズや移行成功率が改善しやすい

サイト側では、削除した投稿のURLが404になっていないか、メニューや内部リンクが死んでいないかを確認します。
キャッシュプラグインやCDNを使っている場合はキャッシュもクリアしてください。
まとめ
Xserverで移行系ツールが使えないときの典型条件が、公式に明記されたデータベース2GB超です。
WordPressでは古い投稿だけでなく、wp_postmeta・wp_term_relationships・(導入時は)wp_aioseo_postsまで整合性を保って削除することが重要です。
手順の骨格は「バックアップ → SELECTで確認 → 関連DELETE → 投稿DELETE → 孤児削除 → OPTIMIZE → 容量再確認」です。
レコード削除と物理容量削減は別問題なので、OPTIMIZE後もinformation_schemaとサーバーパネルの両方で効果を見てから移行を再試行してください。
参考資料
- エックスサーバー「MySQL移行ツール」(2GB超は利用不可)
- エックスサーバー「WordPress簡単移行」(DB 2GB超はエラー)
- エックスサーバー「新サーバー簡単移行」(移行先DBはMariaDB 10.11)
- エックスサーバー「データベース仕様一覧」(1DBあたり目安5GB)
- MariaDB Documentation「OPTIMIZE TABLE」/InnoDBテーブルスペースのデフラグ解説


コメント