「文字コードの問題」はアプリのバグではない
Web開発をしていると、なぜか定期的に発生するトラブルがあります。
それがいわゆる文字化けです。
- 「????」になる
- 一部だけ壊れる
- 検索でヒットしない
- 保存はできるのに取得すると壊れる
そして多くの場合、最初に疑われるのはプログラムです。
「エンコードを間違えたのでは?」
「JSONの扱いが変なのでは?」
「PHPやJavaの設定では?」
しかし実際には、原因はアプリケーションではありません。
データベースです。
文字コード問題の本質は、「表示」ではなく「保存」の問題です。
ブラウザからDBまでの流れを整理する
まず、文字列が保存されるまでの経路を見てみます。
ユーザーが入力した文字は、次の順番で処理されます。
- ブラウザ
- HTTP通信
- Webサーバ
- アプリケーション
- データベース
この中で、最も厳密に文字を扱う必要があるのはどこか。
それがデータベースです。
ブラウザは表示が仕事です。
アプリケーションは処理が仕事です。
しかしデータベースは「永続保存」が仕事です。
つまり、DBだけが「この文字をどういう規則で記録するか」を決定します。
UTF-8は1つではない
ここが混乱の原因です。
多くの人はこう思っています。
UTF-8 = 世界共通の文字コード
概ね正しいのですが、データベースの世界では少し違います。
MySQLには複数の「文字コードの状態」が存在します。
- クライアント文字コード
- 接続文字コード
- カラム文字コード
- テーブル文字コード
- サーバ文字コード
つまり、UTF-8かどうかは1箇所では決まりません。
変換が発生する瞬間
文字コード問題は、次のタイミングで発生します。
「DBに書き込む瞬間」
例えば、ブラウザとアプリはUTF-8だったとします。
しかし、DB接続がlatin1になっていた場合どうなるか。
DBはこう判断します。
「UTF-8のバイト列を、latin1として保存しよう」
ここで変換が発生します。
この変換は元に戻りません。
つまり、保存した瞬間に文字列は壊れます。
SHOW VARIABLES LIKE 'character_set%';
このコマンドを実行すると、MySQLがどの文字コードを使っているか確認できます。
多くのトラブルはここで発見されます。
なぜ表示時ではなく保存時に壊れるのか
重要なポイントがあります。
文字化けは「表示の問題」に見えますが、実際には保存時に壊れています。
一度壊れたデータは、どんなコードを書いても元に戻りません。
そのため、アプリケーション側の対処では直らないことが多いです。
ここが、フロントエンドのバグとデータベース障害が混同されやすい理由です。
アプリケーションは文字を理解していない
実は、プログラムは文字の意味を理解していません。
扱っているのは「文字」ではなくバイト列です。
- PHP
- Java
- Python
どの言語でも同じです。
文字列は「UTF-8として扱う」という約束で処理されます。
しかしデータベースは違います。
データベースは「どの文字集合に属するか」を検証します。
つまり、アプリは受け入れるが、DBは拒否することがあります。
よくある典型的な事故
次の構成は非常に多いです。
- アプリ:UTF-8
- DB:utf8
- 接続:latin1
この状態で日本語を保存すると、一見成功します。
しかし次の操作で破綻します。
- LIKE検索
- ORDER BY
- インデックス検索
なぜか検索できなくなります。
理由は、DB内で別の文字として扱われているからです。
文字コードと検索の関係
文字コードは保存だけの問題ではありません。
検索にも直結します。
例えば、「あ」と「ア」は人間には似ています。
しかしデータベースでは別文字です。
照合順序(COLLATION)が異なると、次のことが起きます。
- 検索にヒットしない
- ソート順が変わる
- 重複チェックが壊れる
ここで初めて「アプリのロジックがおかしい」と疑われます。
しかし実際にはDBの設定です。
リスクと注意点
最も危険なのは、テスト環境では正常に見えることです。
理由は単純です。
- テスト:英数字中心
- 本番:日本語・絵文字あり
この差によって、本番だけで文字コード問題が発生します。
デプロイ後に初めて発覚する典型的な障害です。
そして一度壊れたデータは、基本的に修復できません。
バックアップから戻す以外に現実的な方法はないこともあります。
結局どう考えるべきか
文字コード問題は「設定ミス」ではありません。
データの設計です。
テーブル定義を作る時点で、すでに発生するかどうかが決まっています。
アプリケーションのコード量や品質とはあまり関係ありません。
多くの開発者がプログラムのデバッグに時間を使いますが、
文字化けの多くはSQLを1行確認すれば原因が分かります。
文字化けは表示のトラブルに見えます。
しかし実態は「保存形式の不一致」です。
つまりこの問題はフロントエンドでもバックエンドでもなく、
データベース設計の問題として扱うのが最も近い考え方です。