文字コード問題はなぜDBで起きるのか

「文字コードの問題」はアプリのバグではない

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行確認すれば原因が分かります。

文字化けは表示のトラブルに見えます。
しかし実態は「保存形式の不一致」です。

つまりこの問題はフロントエンドでもバックエンドでもなく、
データベース設計の問題として扱うのが最も近い考え方です。