MySQLで絵文字を入れた瞬間に壊れる理由
Webアプリを作っていると、一度は「保存できていた文字列が突然エラーになる」経験をします。
特に多いのが、ユーザーがスマホから投稿した瞬間に起きる障害です。
テストでは正常に動いていたのに、本番で突然こうなります。
- 保存時にエラーになる
- 文字が「?」に置き換わる
- 途中までしか保存されない
- アプリが例外で落ちる
この原因、ほとんどの場合はプログラムではありません。MySQLです。
そしてさらに正確に言うと、MySQLの「文字コード」です。
「絵文字で壊れるMySQL」というのは比喩ではなく、実際にデータベースの仕様が原因で起きています。
なぜ絵文字だけが問題になるのか
結論から言うと、絵文字は「普通の文字ではない」からです。
多くの開発者はこう考えています。
- ひらがな → 日本語
- 漢字 → 日本語
- 英数字 → ASCII
- 絵文字 → 文字の一種
しかし、データベースの世界では違います。
絵文字は4バイト文字です。
ここで重要なのが、MySQLに長年存在した「utf8」という名前の文字コードです。
MySQLのutf8は本当のUTF-8ではない
非常に誤解されているのですが、MySQLの「utf8」はUTF-8ではありません。
MySQLのutf8は正式には次の仕様です。
- 最大3バイトまでのUTF-8
そして絵文字は4バイトです。
つまり、MySQLのutf8には保存できません。
CREATE TABLE sample ( text VARCHAR(255) ) CHARACTER SET utf8;
この状態で絵文字を保存するとどうなるか。
MySQLは「不正な文字列」と判断します。
結果として次の現象が起きます。
- INSERT時にエラー
- 文字切り捨て
- 置換
アプリケーションのバグに見えますが、実際にはDBが拒否しています。
なぜアプリではなくDBで問題になるのか
「JavaやPHPはUTF-8なのに、なぜDBだけ壊れるのか?」
ここが多くの人が混乱するポイントです。
理由は単純で、Webアプリとデータベースでは「UTF-8」の意味が違うからです。
アプリケーション側のUTF-8は、ほぼ例外なく完全なUTF-8です。
ブラウザもUTF-8です。
つまり通信は問題ありません。
ブラウザ → サーバ → アプリ
ここまでは完全に正常です。
問題が起きるのは、次の瞬間です。
アプリ → MySQL
MySQLは「utf8」という名前なのに4バイト文字を受け取れません。
この瞬間、初めてエラーが発生します。
utf8mb4とは何か
この問題を解決するために登場したのがutf8mb4です。
utf8mb4の仕様はシンプルです。
- 最大4バイトのUTF-8を保存できる
つまり、本来のUTF-8です。
ALTER DATABASE dbname CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
この設定に変更した瞬間、絵文字は正常に保存されます。
ここで重要なのは、「テーブルだけ変更しても直らない」点です。
よくある失敗
utf8mb4にしたのに直らないケースは珍しくありません。
原因は接続文字コードです。
MySQLでは文字コードが複数存在します。
- サーバ文字コード
- データベース文字コード
- テーブル文字コード
- カラム文字コード
- 接続文字コード
このうち、接続文字コードがutf8のままだと壊れます。
SET NAMES utf8mb4;
これを設定しないと、アプリはUTF-8、DBは3バイトUTF-8という矛盾が発生します。
なぜ昔のMySQLはutf8mb4ではなかったのか
単純に「古かったから」です。
MySQLが設計された当時、絵文字文化は存在しませんでした。
UTF-8も現在ほど普及していません。
そのため、
- 英語
- 欧州言語
- 一部の多言語
が保存できれば十分でした。
そして3バイトUTF-8が「実用上問題ない」と判断されました。
これが現在まで続いた歴史的な理由です。
リスクと注意点
utf8mb4に変更すればすべて解決する、というわけでもありません。
特に影響が出るのがインデックスです。
utf8mb4は1文字4バイトです。
つまりインデックスサイズが増えます。
古いMySQLでは次のエラーが出ます。
- index column size too large
VARCHAR(255) にインデックスを貼っていると発生します。
この問題は多くの移行作業を止めます。
対処は次のいずれかになります。
- カラム長を191にする
- MySQLを新しくする
ここで無理に短くすると検索仕様が変わるため注意が必要です。
向いている構成・向いていない構成
SNS、コメント、問い合わせフォームなど、ユーザー入力があるシステムではutf8mb4はほぼ必須です。
逆に、数値データ中心の業務DBでは影響が小さいため後回しにされがちです。
ただし、スマートフォン入力が関わる時点で、いつ問題が起きてもおかしくありません。
結局どうすればいいのか
MySQLで絵文字問題が起きるかどうかは、アプリの出来では決まりません。
データベースの初期設定でほぼ決まります。
新規構築なら、最初からutf8mb4を選択するだけで防げます。
逆に、動いているシステムを後から直すと非常に大きな作業になります。
つまりこの問題の本質は、
「文字コードの知識」ではなく「初期設計の問題」です。
MySQLの不具合に見える障害の多くは、実は壊れたのではなく、最初から想定していなかった入力が来ただけです。
絵文字はその最も分かりやすい例に過ぎません。