私のサイトではフォーラムとして「BBPress」を使っているのだが、最近スパムの投稿が多くて、データベースのパフォーマンスがかなり落ちていた。定期的にスパムを削除していなかったこともあって、スパム認定されたデータがpostsテーブルに5万件以上、postmetaテーブルには34万件も溜まっていた…orz
これはイカン…と、管理画面からスパムをすべて削除を実行したところ、クエリが重すぎて500エラーになってしまった。
仕方ないので、コマンドラインからデータベースに直接ログインしてpostsテーブルのスパムは駆除した。だが、postmetaテーブルなどのリレーションテーブル系からのスパムデータ駆除が何気に面倒なことに気づいた。
先に親テーブル(postsテーブル)のスパム投稿を削除しているので、postmetaテーブルにおける駆除対象は親となる投稿IDが存在していないデータになる。通常のDELETE文の条件式で考えると、外部キーのpost_idからpostsテーブルに該当するプライマリキーがないことを確認して、なければ対象のmeta_idのデータを削除する…みたいなループ処理を作らないと…とか面倒なことを考えていたのだが、そういえばSQLにはNOT EXISTSサブクエリーという機能があったじゃないか!─とひらめいた。
ついでに今後のことも考えて、リレーションテーブル系の連結が外れているゴミデータを掃除するスクリプトを作ってみた次第。
<?php
require_once './wp-load.php';
global $wpdb;
$cleanups = [
$wpdb->postmeta => [ 'pk' => 'ID', 'from' => $wpdb->posts, 'fk' => 'post_id' ],
$wpdb->comments => [ 'pk' => 'ID', 'from' => $wpdb->posts, 'fk' => 'comment_post_id' ],
$wpdb->commentmeta => [ 'pk' => 'comment_id', 'from' => $wpdb->comments, 'fk' => 'comment_id' ],
$wpdb->usermeta => [ 'pk' => 'ID', 'from' => $wpdb->users, 'fk' => 'user_id' ],
$wpdb->term_taxonomy => [ 'pk' => 'term_id', 'from' => $wpdb->terms, 'fk' => 'term_id' ],
$wpdb->term_relationships => [ 'pk' => 'term_taxonomy_id', 'from' => $wpdb->term_taxonomy, 'fk' => 'term_taxonomy_id' ],
];
$messages = [];
foreach ($cleanups as $_target => $_params) {
$_query = sprintf( ' FROM %s WHERE NOT EXISTS (SELECT %s FROM %s WHERE %s.%s = %s.%s)',
$_target, $_params['pk'], $_params['from'], $_params['from'], $_params['pk'], $_target, $_params['fk']
);
$_query_type = 'SELECT ' . $_params['fk'];
$_checks = $wpdb->get_results( $_query_type . $_query );
if (!empty($_checks)) {
$_query_type = 'DELETE';
$_results = $wpdb->get_results( $_query_type . $_query );
if (empty($_results))
$messages[] = sprintf( 'Removed the unnecessary %d data from "%s" table.', count($_checks), $_target );
}
}
echo empty($messages) ? 'Removing data was not exist.' : implode("<br>\n", $messages);
このPHPスクリプトをWordPressのインストールディレクトリ直下にcleanup.phpとか名前をつけて置いて実行すると、各種リレーションテーブルにて親テーブルに連携元となるデータが存在しないデータのみをごっそり削除してくれる。
私も無事に35万件もあったpostmetaテーブルのクリーンアップができて、2700件にまで減りました(いやぁ、良かった良かった…)。
この機能、いつかCDBT V2に取り込んでおこう。
しっかし、BBPressのスパム量にはげんなりする…近く、フォーラムはWordPress.orgに集約してしまった方が無難かもしれない…。