絵文字で壊れるMySQLの正体

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の不具合に見える障害の多くは、実は壊れたのではなく、最初から想定していなかった入力が来ただけです。
絵文字はその最も分かりやすい例に過ぎません。