らぼるてっく。

てっくてっく歩いてっく。

MySQLでUTF-8を扱うならutf8mb3よりutf8mb4を使おう!

こんにちは、新卒2年目のバックエンドエンジニアの伊藤です。
みなさんは、テーブル作成の際にCHARACTER SET(charset)を意識していますか?
環境によっては、デフォルトで設定されていることもあり、意識しない人もいると思います。

ただ、このcharsetの設定を間違えると、特定の文字が使えない絵文字が使えないなどといった問題が発生してしまいます。

タイトルにもある、MySQLでは使用できるcharsetを下記のコマンドで確認することができます。

-- MySQL version8.1.0
SHOW CHARACTER SET;

+----------+---------------------------------+---------------------+--------+
| Charset  | Description                     | Default collation   | Maxlen |
+----------+---------------------------------+---------------------+--------+
| armscii8 | ARMSCII-8 Armenian              | armscii8_general_ci |      1 |
| ascii    | US ASCII                        | ascii_general_ci    |      1 |
| big5     | Big5 Traditional Chinese        | big5_chinese_ci     |      2 |
| binary   | Binary pseudo charset           | binary              |      1 |
| cp1250   | Windows Central European        | cp1250_general_ci   |      1 |
| cp1251   | Windows Cyrillic                | cp1251_general_ci   |      1 |
| cp1256   | Windows Arabic                  | cp1256_general_ci   |      1 |
| cp1257   | Windows Baltic                  | cp1257_general_ci   |      1 |
| cp850    | DOS West European               | cp850_general_ci    |      1 |
| cp852    | DOS Central European            | cp852_general_ci    |      1 |
| cp866    | DOS Russian                     | cp866_general_ci    |      1 |
| cp932    | SJIS for Windows Japanese       | cp932_japanese_ci   |      2 |
| dec8     | DEC West European               | dec8_swedish_ci     |      1 |
| eucjpms  | UJIS for Windows Japanese       | eucjpms_japanese_ci |      3 |
| euckr    | EUC-KR Korean                   | euckr_korean_ci     |      2 |
| gb18030  | China National Standard GB18030 | gb18030_chinese_ci  |      4 |
| gb2312   | GB2312 Simplified Chinese       | gb2312_chinese_ci   |      2 |
| gbk      | GBK Simplified Chinese          | gbk_chinese_ci      |      2 |
| geostd8  | GEOSTD8 Georgian                | geostd8_general_ci  |      1 |
| greek    | ISO 8859-7 Greek                | greek_general_ci    |      1 |
| hebrew   | ISO 8859-8 Hebrew               | hebrew_general_ci   |      1 |
| hp8      | HP West European                | hp8_english_ci      |      1 |
| keybcs2  | DOS Kamenicky Czech-Slovak      | keybcs2_general_ci  |      1 |
| koi8r    | KOI8-R Relcom Russian           | koi8r_general_ci    |      1 |
| koi8u    | KOI8-U Ukrainian                | koi8u_general_ci    |      1 |
| latin1   | cp1252 West European            | latin1_swedish_ci   |      1 |
| latin2   | ISO 8859-2 Central European     | latin2_general_ci   |      1 |
| latin5   | ISO 8859-9 Turkish              | latin5_turkish_ci   |      1 |
| latin7   | ISO 8859-13 Baltic              | latin7_general_ci   |      1 |
| macce    | Mac Central European            | macce_general_ci    |      1 |
| macroman | Mac West European               | macroman_general_ci |      1 |
| sjis     | Shift-JIS Japanese              | sjis_japanese_ci    |      2 |
| swe7     | 7bit Swedish                    | swe7_swedish_ci     |      1 |
| tis620   | TIS620 Thai                     | tis620_thai_ci      |      1 |
| ucs2     | UCS-2 Unicode                   | ucs2_general_ci     |      2 |
| ujis     | EUC-JP Japanese                 | ujis_japanese_ci    |      3 |
| utf16    | UTF-16 Unicode                  | utf16_general_ci    |      4 |
| utf16le  | UTF-16LE Unicode                | utf16le_general_ci  |      4 |
| utf32    | UTF-32 Unicode                  | utf32_general_ci    |      4 |
| utf8mb3  | UTF-8 Unicode                   | utf8mb3_general_ci  |      3 |
| utf8mb4  | UTF-8 Unicode                   | utf8mb4_0900_ai_ci  |      4 |
+----------+---------------------------------+---------------------+--------+
41 rows in set (0.02 sec)

41種類もあるcharsetを全て解説はできないので、
今回の記事ではutf8mb3utf8mb4について話していきます。

理由としては、ラボルでは業務でUTF-8を使用する機会が多いからです。

utf8mb3utf8mb4の違い

保存できる文字

utf8mb3utf8mb4では保存できる文字が違います。

実際にテーブルを作成して、確認してみましょう。
環境はMySQL version8.1.0です。

-- テストテーブルの作成
CREATE TABLE `test_charset` (
    `utf8mb3_column` VARCHAR(32) CHARACTER SET utf8mb3, 
    `utf8mb4_column` VARCHAR(32) CHARACTER SET utf8mb4 
);

-- utf8mb3
INSERT INTO test_charset (`utf8mb3_column`) VALUES ('𠮷');

-- 結果
Query 1 ERROR : Incorrect string value: '\xF0\xA0\xAE\xB7' for column 'utf8mb3_column' at row 1

-- utf8mb4
INSERT INTO test_charset (`utf8mb4_column`) VALUES ('𠮷');

--- 結果
Query 1 OK: 1 row affected

このように、 utf8mb3では1文字で4バイトを超えるような文字(🍣や🍺、𠮷など)が保存できません。

また、公式からも非推奨と言われており、utf8mb4が推奨されています。

公式ドキュメント(下部の参考を参照)より引用

utf8mb3 文字セットは非推奨であり、将来の MySQL リリースで削除される予定です。

where句で使える文字

4バイトを超えるような文字を保存できないと言いましたが、実はwhere句で使用することもできません。

-- 上で作成したテーブルを使用

SELECT * FROM test_charset WHERE utf8mb3_column = "𠮷";

-- 出力されるメッセージ
Query 1 : Illegal mix of collations (utf8mb3_general_ci,IMPLICIT) and (utf8mb4_0900_ai_ci,COERCIBLE) for operation '='

SELECT * FROM test_charset WHERE utf8mb4_column = "𠮷";

-- 出力されるメッセージ
Query 1 OK

このように、utf8mb3は4バイトの文字をwhere句で指定するとエラーになりますが、
実装者がこのような操作をすることはないと思います。
しかし、toCのサービスのような自由入力フォームやsqlについてあまり強くない人の操作など、
このようなエラーが発生する機会は0ではないので、頭の片隅に入れておきましょう。

utf8 というエイリアス

MySQLにはutf8というcahrsetがありますが、これはどちらのcharsetが使われると思いますか?

実際に作成して確認していきます。

-- テーブル作成
CREATE TABLE `test_charset_part2` (
    `utf8_column` VARCHAR(32) CHARACTER SET utf8, 
    `utf8mb3_column` VARCHAR(32) CHARACTER SET utf8mb3, 
    `utf8mb4_column` VARCHAR(32) CHARACTER SET utf8mb4 
);

-- 文字コード確認
SHOW CREATE TABLE `test_charset_part2`;

-- 結果
CREATE TABLE `test_charset_part2` (
  `utf8_column` varchar(32) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL,
  `utf8mb3_column` varchar(32) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL,
  `utf8mb4_column` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci

このエイリアスは、非推奨であるutf8mb3が使われるため使用する際は気をつけてください。

ただ、公式では、いずれはutf8mb4のエイリアスに移行すると言っており、使用も非推奨になっています。

公式ドキュメント(下部の参考)より引用

utf8 は現在 utf8mb3 のエイリアスですが、ある時点では utf8 が utf8mb4 への参照になることが予想されます。 utf8 の意味があいまいにならないように、utf8 ではなく文字セット参照に utf8mb4 を明示的に指定することを検討してください。

まとめ

utf8mb3は常用漢字を含む日本語には対応しているため、意識せず使用している箇所があるかもしれません。(絵文字などのバリデーションしなくてすみますし) ただ、公式では非推奨であり、将来的に削除予定とも言われています。

そのため、今後作成するテーブルについてはutf8mb3utf8は使用しないようにし、 charsetを意識してテーブルを作成していきましょう。

最後に

ラボルでは、エンジニアを積極採用中です。1、2年目のエンジニアから経験豊富なテックリードやエンジニリングマネージャーまで、興味がある方はぜひご応募ください!!

labol.co.jp

参考

MySQL 8.0 リファレンスマニュアル / utf8mb3 文字セット https://dev.mysql.com/doc/refman/8.0/ja/charset-unicode-utf8mb3.html