MySQLのutf8とutf8mb4の違いを本当に理解する

「utf8にしたのに文字化けした」の正体

MySQLのトラブルで非常に多いのが、文字コードに関する問題です。
テーブルもutf8、接続もutf8、設定ファイルもutf8。それでも保存時にエラーが出たり、文字化けが発生します。

典型的なログです。

Incorrect string value

多くの場合、ここでアプリケーションのエンコードやフレームワークを疑います。
しかし原因の大半はMySQLの仕様です。

結論から言うと、**MySQLのutf8はUTF-8ではありません。**

MySQLのutf8は3バイト

一般にUTF-8は1〜4バイトの可変長文字コードです。
しかしMySQLのutf8は最大3バイトです。

つまり、Unicodeの一部しか保存できません。

保存できる文字の例です。

  • 日本語
  • 英数字
  • 一般的な記号

保存できない文字の代表例です。

  • 絵文字
  • 一部の拡張漢字
  • 一部の記号

例えば次の文字です。

🙂

これをutf8カラムへINSERTするとエラーになります。

INSERT INTO messages (body) VALUES ('🙂');

アプリケーションが正しくても失敗します。
理由は4バイト文字だからです。

utf8mb4とは何か

utf8mb4は、MySQLが後から追加した文字コードです。
こちらが本当のUTF-8に相当します。

違いは単純です。

utf8

  • 最大3バイト

utf8mb4

  • 最大4バイト

この1バイト差が大きな問題を生みます。

なぜこんな仕様になったのか

初期のMySQLが作られた時代、Unicodeの4バイト領域はほとんど使われていませんでした。
主な用途は欧文と日本語です。

当時は次の要求が重要でした。

  • ディスク容量を減らす
  • インデックスを小さくする
  • メモリ使用量を抑える

そのため3バイトUTF-8が採用されました。
後にUnicodeが拡張され、絵文字が普及して問題が表面化しました。

つまりバグではなく歴史的事情です。

もっと危険な問題:気づかないまま壊れる

怖いのは、必ずしもエラーにならないことです。
環境によっては、次の現象が起きます。

  • 文字が「?」になる
  • 切り詰められる
  • 保存は成功するが内容が変わる

エラーが出ないため、気づくのが遅れます。
特に問い合わせ対応で発覚します。

インデックス長制限との関係

utf8mb4へ変更すると、次のエラーが出ることがあります。

Specified key was too long

理由はインデックス長です。
utf8mb4は4バイトなので、同じVARCHARでもサイズが増えます。

例えばです。

VARCHAR(255)

utf8
255 × 3 = 765バイト

utf8mb4
255 × 4 = 1020バイト

古いInnoDBではインデックス上限を超えます。

そのため次の対応が必要になります。

  • VARCHAR長を191へ変更
  • InnoDB設定変更
  • MySQLバージョン更新

単なる文字コード変更では終わりません。

接続文字コードの罠

もう一つの典型的な問題が接続設定です。
テーブルがutf8mb4でも、接続がutf8だと壊れます。

例えばアプリの接続設定です。

SET NAMES utf8;

この時点で4バイト文字は失われます。
テーブル側の設定だけでは不十分です。

確認すべき項目です。

  • server character set
  • database character set
  • table character set
  • connection character set

すべて揃って初めて正常動作します。

移行時に起きるトラブル

utf8からutf8mb4への移行では次の問題が起きます。

  • インデックス作成失敗
  • UNIQUE制約エラー
  • ソート順変化
  • LIKE検索結果変化

特に照合順序(collation)が影響します。
文字比較の結果が変わるため、検索結果が変化することがあります。

どう対応すべきか

単にALTER TABLEするだけでは不十分です。
次の順序で進めます。

  • DB設定変更
  • テーブル変更
  • カラム変更
  • 接続設定変更
  • アプリ検証

途中を省略すると、部分的に文字化けが残ります。

まとめ

MySQLのutf8問題は設定ミスではありません。
仕様の誤解です。

utf8という名前から、本物のUTF-8だと認識してしまいます。
しかし実際は互換形式です。

現在、文字コードはインフラではなくアプリケーションの仕様の一部です。
そのため、後からの変更は影響が大きくなります。

文字化けトラブルの多くはバグではありません。
「どのUTF-8を使っているか」を確認していなかった結果です。

そして対策は単純です。
新規システムでは最初からutf8mb4を選択することです。