【実録】WordPress を “utf8mb4 + unicode_520_ci” へ安全移行した手順&つまづきポイント(MariaDB 11.4)

パソコン

WordPress/MariaDB を utf8mb4 + unicode_520_ci に統一し、Redis 導入&WP Cron 外部化まで仕上げた実録(hd0.biz /wpm)

最終更新: 2025-10-03


ゴール(今回やったことの全体像)

  • 文字コード/照合順序の完全統一:DBサーバ・データベース・テーブル・WordPress のすべてを utf8mb4 / utf8mb4_unicode_520_ci に。
  • MariaDB 起動エラーの解消unknown variable 'default-character-set' の根本原因を除去。
  • Redis オブジェクトキャッシュ導入:Cocoon のページキャッシュとレイヤ分けで併用
  • WP Cron の外部化DISABLE_WP_CRON=true + systemd timer で5分おきに確実実行。
  • スロークエリログ整備/var/log/mariadb/slow.log に常時出力、logrotate 設定まで。

対象環境(主要値)

  • WordPress: 6.8.3(パス: /var/www/html/wpm/
  • MariaDB: 11.4.8
  • サイトURL: WP_SITEURL=https://hd0.biz/wpm, WP_HOME=https://hd0.biz
  • 最終状態(実効)
    • WordPress: DB_CHARSET=utf8mb4, DB_COLLATE=''(空), WP_CACHE=有効DISABLE_WP_CRON=true
    • DBサーバ: character-set-server=utf8mb4, collation-server=utf8mb4_unicode_520_ci
    • DB/テーブル: データベース wordpress の既定も utf8mb4 / utf8mb4_unicode_520_ci、全テーブル ROW_FORMAT=DYNAMIC かつ utf8mb4_unicode_520_ci
    • セキュリティ/運用: FORCE_SSL_ADMIN=ON, DISALLOW_FILE_EDIT=ON, Core 自動更新 minor

実施サマリ

  1. wp-config.php の重複定義(DB_CHARSET/DB_COLLATE)を整理。
  2. すべてのベーステーブルROW_FORMAT=DYNAMICutf8mb4_unicode_520_ci に変換。
  3. MariaDB の server.cnf を最小構成で統一(character-set-server / collation-server)。
  4. 起動失敗の根本原因:クライアント設定ファイルの [mariadb]default-character-set があり、サーバがそれを誤って読んでいたコメントアウトsystemd drop-in の不要設定を退避。
  5. Redis を WordPress のオブジェクトキャッシュとして使用(WP_CACHE_KEY_SALT/DB 番号で衝突回避)。
  6. WP Cron 外部化:systemd service/timer を作成し 5 分毎に wp cron event run --due-now
  7. スロークエリログをセットアップ。検証は SESSIONlong_query_time=0 にして SLEEP() を流す。

詳細手順

すべて root 実行例。--allow-root を付けた WP-CLI を使用。

1) WordPress 側の定義を整理

# 重複防止のバックアップ
cp -a /var/www/html/wpm/wp-config.php /root/wp-config.php.bak.$(date +%F-%H%M) # サンプル:以前の重複を削る(行番号は環境依存なので実際は grep で確認)
grep -nE "define\(\s*'DB_CHARSET'|'DB_COLLATE'" /var/www/html/wpm/wp-config.php
# → DB_CHARSET は 'utf8mb4'、DB_COLLATE は ''(空)を1つだけ残す

最終形(抜粋)

define('DB_CHARSET', 'utf8mb4');
define('DB_COLLATE', ''); // 本番デバッグ方針
define('WP_DEBUG', false);
define('WP_DEBUG_DISPLAY', false);
define('WP_DEBUG_LOG', true); // WP_DEBUG=true の時に有効 // 運用・セキュリティ
define('FORCE_SSL_ADMIN', true);
define('DISALLOW_FILE_EDIT', true);

2) テーブルを ROW_FORMAT=DYNAMIC & utf8mb4_unicode_520_ci に

# DYNAMIC に統一
/usr/local/bin/wp --allow-root --path=/var/www/html/wpm db query \
"SELECT TABLE_NAME FROM information_schema.TABLES \ WHERE TABLE_SCHEMA=DATABASE() AND TABLE_TYPE='BASE TABLE' AND TABLE_NAME<>''" \ --skip-column-names | awk 'NF' | while read -r t; do echo "ROW_FORMAT=DYNAMIC: $t" /usr/local/bin/wp --allow-root --path=/var/www/html/wpm db query \ "ALTER TABLE \`$t\` ROW_FORMAT=DYNAMIC;" done # 文字コード/照合順序を変換
/usr/local/bin/wp --allow-root --path=/var/www/html/wpm db query \
"SELECT TABLE_NAME FROM information_schema.TABLES \ WHERE TABLE_SCHEMA=DATABASE() AND TABLE_TYPE='BASE TABLE' AND TABLE_NAME<>''" \ --skip-column-names | awk 'NF' | while read -r t; do echo "CONVERT: $t" /usr/local/bin/wp --allow-root --path=/var/www/html/wpm db query \ "ALTER TABLE \`$t\` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci;" \ || { echo "*** ERROR converting $t"; exit 1; } done # 変換漏れの確認(0行ならOK)
/usr/local/bin/wp --allow-root --path=/var/www/html/wpm db query \
"SELECT TABLE_NAME, TABLE_COLLATION FROM information_schema.TABLES \ WHERE TABLE_SCHEMA=DATABASE() AND (TABLE_COLLATION IS NULL OR TABLE_COLLATION NOT LIKE 'utf8mb4%');" \ --skip-column-names

3) MariaDB: サーバ設定を最小で統一

/etc/my.cnf.d/server.cnf(抜粋)

[mysqld]
# 通信まわり
net_read_timeout = 60
net_write_timeout = 120
max_allowed_packet = 64M # 文字コード統一
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_520_ci # (任意)スロークエリログは §6 で設定

重要:起動失敗の根本原因

  • /etc/my.cnf.d/client.cnf[mariadb] セクションdefault-character-set=utf8mb4 を書くと、サーバ(mariadbd)がそれを拾ってしまい
    unknown variable 'default-character-set=utf8mb4' で起動に失敗する場合がある。
  • 対策:[mariadb] 節の default-character-set をコメントアウト[client]/[mysql] など“純クライアント側”だけに置く。
# 反映確認(mysqld に渡る既定)
/usr/sbin/mariadbd --print-defaults
# → ここに default-character-set が出てこないこと

systemd drop-in にも注意

# 実際に有効な設定を確認
a) systemctl cat mariadb.service | sed -n '/mariadb.service.d/,$p'
# 不要な drop-in があれば退避
b) mv /etc/systemd/system/mariadb.service.d/migrated-from-my.cnf-settings.conf \ /root/mariadb-dropin.bak/ 2>/dev/null || true
systemctl daemon-reload
systemctl restart mariadb

4) WordPress 側の実効値を確認

# 文字コード・照合順序(WPオブジェクト)
/usr/local/bin/wp --allow-root --path=/var/www/html/wpm eval \ 'global $wpdb; echo "wpdb->charset={$wpdb->charset}\nwpdb->collate={$wpdb->collate}\n";' # 表示用のブログ文字セット(通常 UTF-8)
/usr/local/bin/wp --allow-root --path=/var/www/html/wpm option get blog_charset

5) Redis オブジェクトキャッシュ導入

Cocoon のページキャッシュ(WP_CACHE)とは別レイヤ。併用で相乗効果。

プラグイン導入 & ドロップイン有効化

/usr/local/bin/wp --allow-root --path=/var/www/html/wpm plugin install redis-cache --activate
/usr/local/bin/wp --allow-root --path=/var/www/html/wpm redis enable

wp-config.php 例(重複定義に注意)

// Redis Object Cache
define('WP_CACHE', true); // ページキャッシュ系と共存OK
define('WP_REDIS_HOST', '127.0.0.1');
define('WP_REDIS_PORT', 6379);
define('WP_REDIS_DATABASE', 0); // 他用途と共用なら専用番号に
define('WP_CACHE_KEY_SALT', 'hd0.biz:'); // キー衝突回避
// define('WP_REDIS_MAXTTL', 86400); // 任意:最大TTL(秒)

動作・メンテ

# ステータス(サブコマンドは環境によって異なる)
/usr/local/bin/wp --allow-root --path=/var/www/html/wpm redis status || true # キャッシュ全消去(WordPress経由なので安全)
/usr/local/bin/wp --allow-root --path=/var/www/html/wpm cache flush

wp redis flush/info が無い環境でも、wp cache flush で十分。Redis を他システムと共有する場合は WP_CACHE_KEY_SALTDB 番号を必ず分ける。

6) WP Cron を外部化(systemd timer)

DISABLE_WP_CRON=true にしたので、OS側で 5 分ごとに実行。

ユニット作成

# Service: 期限到来分を即時実行
cat >/etc/systemd/system/wp-cron-wpm.service <<'EOF'
[Unit]
Description=Run WordPress cron for /var/www/html/wpm [Service]
Type=oneshot
ExecStart=/usr/local/bin/wp --allow-root --path=/var/www/html/wpm cron event run --due-now --quiet
EOF # Timer: 5分おき
cat >/etc/systemd/system/wp-cron-wpm.timer <<'EOF'
[Unit]
Description=Schedule WordPress cron for /var/www/html/wpm [Timer]
OnBootSec=2min
OnUnitActiveSec=5min
AccuracySec=1s
Unit=wp-cron-wpm.service [Install]
WantedBy=timers.target
EOF systemctl daemon-reload
systemctl enable --now wp-cron-wpm.timer # 動作確認
systemctl status wp-cron-wpm.timer --no-pager
systemctl list-timers --no-pager | grep wp-cron-wpm # いますぐ実行
systemctl start wp-cron-wpm.service
journalctl -u wp-cron-wpm.service -n 50 --no-pager

timer と crontab の二重起動はNG。どちらか一方に統一。

Cron イベントの可視化/手動実行

/usr/local/bin/wp --allow-root --path=/var/www/html/wpm \ cron event list --fields=hook,next_run_gmt,next_run_relative --format=table | head /usr/local/bin/wp --allow-root --path=/var/www/html/wpm cron event run --due-now

7) MariaDB スロークエリログ整備

サーバ設定(server.cnf の [mysqld])

slow_query_log = ON
slow_query_log_file= /var/log/mariadb/slow.log
long_query_time = 1

初期化・権限

install -d -o mysql -g mysql -m 750 /var/log/mariadb
: > /var/log/mariadb/slow.log
chown mysql:mysql /var/log/mariadb/slow.log
chmod 640 /var/log/mariadb/slow.log
systemctl restart mariadb

logrotate/etc/logrotate.d/mariadb-slow

/var/log/mariadb/slow.log { daily rotate 7 missingok compress delaycompress notifempty create 640 mysql mysql postrotate systemctl kill -s USR1 mariadb.service 2>/dev/null || true endscript
}

確実にログへ書かせるテスト

# そのセッションだけ 0 秒にして強制的に拾わせる
mariadb -e "SET SESSION long_query_time=0; SELECT SLEEP(0.2) AS t;" tail -n 20 /var/log/mariadb/slow.log

long_query_timeセッション変数でもあるため、GLOBAL を 0 に変えて同一セッションで計測しても拾われないことがある点に注意。


ハマりポイント / トラブルシュート

  • mariadbd 起動失敗unknown variable 'default-character-set=utf8mb4'
    • 原因:/etc/my.cnf.d/client.cnf[mariadb]default-character-set。サーバが解釈して落ちる。
    • 対応:当該行をコメントアウト。/usr/sbin/mariadbd --print-defaults で mysqld に渡るオプションから消えていることを確認。
  • systemd drop-in による意図しない上書き
    • systemctl cat mariadb.servicemariadb.service.d/ の存在を確認。不要なら退避し daemon-reload
  • WP-CLI を root で実行
    • --allow-root を必ず付ける。sudo -u apache での実行は環境次第で このアカウントは現在利用できません となる。
  • wp redis サブコマンドが少ない
    • 環境により info/flush が無い場合あり。wp cache flush で十分

運用ワンライナー(任意)

# 1) WP全キャッシュ消し → 2) 期限到来Cron実行 → 3) DB簡易最適化
/usr/local/bin/wp --allow-root --path=/var/www/html/wpm cache flush && \
/usr/local/bin/wp --allow-root --path=/var/www/html/wpm cron event run --due-now --quiet && \
/usr/local/bin/wp --allow-root --path=/var/www/html/wpm db optimize

ロールバック手順(万一のとき)

  1. wp-config.php をバックアップから戻す。 cp -a /root/wp-config.php.bak.YYYY-MM-DD-HHMM /var/www/html/wpm/wp-config.php
  2. DB をダンプから復元(例:/root/hd0biz_before_utf8mb4.sql)。 mariadb -u root -p wordpress < /root/hd0biz_before_utf8mb4.sql
  3. MariaDB / PHP-FPM / Web サーバを再起動し、フロント/管理の基本動作を確認。

参考:最終チェックコマンド集

# WP / DB 文字コードの実効値
/usr/local/bin/wp --allow-root --path=/var/www/html/wpm eval 'global $wpdb; echo "$wpdb->charset / $wpdb->collate\n";'
/usr/local/bin/wp --allow-root --path=/var/www/html/wpm option get blog_charset
mariadb -e "SHOW VARIABLES LIKE 'character_set_server'; SHOW VARIABLES LIKE 'collation_server';" # Cron
systemctl list-timers --no-pager | grep wp-cron-wpm
/usr/local/bin/wp --allow-root --path=/var/www/html/wpm cron event list --fields=hook,next_run_gmt,next_run_relative --format=table | head # Redis(導入済みならステータス)
/usr/local/bin/wp --allow-root --path=/var/www/html/wpm redis status || true # Slow log
mariadb -e "SHOW VARIABLES LIKE 'slow_query_log%'; SHOW VARIABLES LIKE 'long_query_time'; SHOW VARIABLES LIKE 'log_output';"

結論

  • utf8mb4 + unicode_520_ci で完全統一でき、WordPress/DB/サーバのズレが解消。
  • Redis + Cocoon の二層キャッシュ、WP Cron 外部化スローログまで整備し、安定かつ観測可能な運用に移行。
  • あとはアクセス状況に応じて、オブジェクトキャッシュの TTLログインユーザのページキャッシュ除外などを微調整すれば完成度はさらに上がる。

調整ポイント(任意):

  • 投稿リビジョン上限 50 → 20 程度も検討余地
  • PHP の memory_limit が WP の上限(768M)を下回っていないか確認
  • アクセス増に応じて Redis の maxmemory ポリシーや WP_REDIS_DATABASE の分離も検討