MySQL 8.0がutf8mb4のデフォルトcollationを utf8mb4_0900_ai_ci に変更したことは有名です。
MariaDBも11.5からデフォルトcollationを変更したのですが、 utf8mb4_0900_ai_ci ではなく utf8mb4_uca1400_ai_ci になりました。
この2つのcollationについてはMySQLやMariaDBの識者に任せて、私はMySQLとMariaDBで接続のcollation (collation_connection) がどう設定されるかを調べてみました。
MySQL 8
MySQL 8.4のクライアントからサーバーに --default-character-set=utf8mb4 を指定して接続すると、 collation_connection は utf8mb4_0900_ai_ci になります。
utf8mb4のデフォルトcollationが utf8mb4_0900_ai_ci になっていて、クライアントは utf8mb4 のデフォルトcollationのcollation idをハンドシェイクパケットで送信しています。
$ docker run --network host -it mysql:8.4 mysql -h 127.0.0.1 --default-character-set=utf8mb4 ... Server version: 8.4.5 MySQL Community Server - GPL ... mysql> select @@collation_connection; +------------------------+ | @@collation_connection | +------------------------+ | utf8mb4_0900_ai_ci | +------------------------+ 1 row in set (0.00 sec) mysql> SELECT * FROM information_schema.COLLATIONS WHERE CHARACTER_SET_NAME='utf8mb4' AND ID<256; +------------------------+--------------------+-----+------------+-------------+---------+---------------+ | COLLATION_NAME | CHARACTER_SET_NAME | ID | IS_DEFAULT | IS_COMPILED | SORTLEN | PAD_ATTRIBUTE | +------------------------+--------------------+-----+------------+-------------+---------+---------------+ | utf8mb4_general_ci | utf8mb4 | 45 | | Yes | 1 | PAD SPACE | | utf8mb4_bin | utf8mb4 | 46 | | Yes | 1 | PAD SPACE | ... | utf8mb4_0900_ai_ci | utf8mb4 | 255 | Yes | Yes | 0 | NO PAD | +------------------------+--------------------+-----+------------+-------------+---------+---------------+ 27 rows in set (0.01 sec)
ただし、MySQL 8.4同士でも注意が必要なケースがあります。
デフォルトのcharsetがutf8mb4になっていても、 --default-charcter-set=utf8mb4 を指定しないと utf8mb4 にならないことがあります。たとえばmysql:8.4のコンテナのmysqlを使うとこうなります。
$ docker run --rm --network host -it mysql:8.4 mysql -h 127.0.0.1 mysql> show variables like '%_connection'; +--------------------------+-------------------+ | Variable_name | Value | +--------------------------+-------------------+ | character_set_connection | latin1 | | collation_connection | latin1_swedish_ci | +--------------------------+-------------------+ 2 rows in set (0.00 sec)
これはmysqlクライアントが、 --default-character-set が指定されていないときにlocaleからcharsetを選択するためです。例えば環境変数 LANG=C.UTF-8 を設定すると utf8mb4 が使われます。
$ docker run --rm --network host -it --env LANG=C.UTF-8 mysql:8.4 mysql -h 127.0.0.1 ... mysql> show variables like '%_connection'; +--------------------------+--------------------+ | Variable_name | Value | +--------------------------+--------------------+ | character_set_connection | utf8mb4 | | collation_connection | utf8mb4_0900_ai_ci | +--------------------------+--------------------+ 2 rows in set (0.01 sec)
もう一つ注意する必要があるのがビルド時のオプションです。Homebrewのmysql-clientは -DDEFAULT_COLLATION=utf8mb4_general_ci をつけてビルドされています。(mysqlではこのオプションは消されているのでmysql-clientでも消すPRを作っています。)
mysqlが選択したcharsetが、DEFAULT_COLLATIONのcharsetと一致している場合、charsetのデフォルトのcollationではなく DEFAULT_COLLATIONが使われます。
そのためHomebrewのmysql-clientのmysqlコマンドを使うと utf8mb4_general_ci が使われます。utf8mb4のデフォルトcollationが変わっている訳では無いので、 SET NAMES utf8mb4 を実行すると utf8mb4_0900_ai_ci になります。
$ /opt/homebrew/opt/mysql-client@8.4/bin/mysql -h 127.0.0.1 -uroot --default-character-set=utf8mb4 ... mysql> select @@collation_connection; +------------------------+ | @@collation_connection | +------------------------+ | utf8mb4_general_ci | +------------------------+ 1 row in set (0.00 sec) mysql> set names utf8mb4; Query OK, 0 rows affected (0.00 sec) mysql> select @@collation_connection; +------------------------+ | @@collation_connection | +------------------------+ | utf8mb4_0900_ai_ci | +------------------------+ 1 row in set (0.00 sec)
MariaDB
MariaDBは組み込みのutf8mb4のデフォルトcollationをMySQL 5.7時代の utf8mb4_general_ci にしたまま、追加の character_set_collations というシステム変数でutf8mb4のデフォルトcollationを変更しています。試しにこの変数を空にして information_schema.COLLATIONS を見てみると utf8mb4_general_ci がデフォルトであることが分かります。
# $ docker run --rm --name mariadb --network host --env MARIADB_ALLOW_EMPTY_ROOT_PASSWORD=1 mariadb:latest --character_set_collations='' mysql> SELECT * FROM information_schema.COLLATIONS WHERE CHARACTER_SET_NAME='utf8mb4' AND ID<256; +------------------------------+--------------------+------+------------+-------------+---------+---------------------------------------+ | COLLATION_NAME | CHARACTER_SET_NAME | ID | IS_DEFAULT | IS_COMPILED | SORTLEN | COMMENT | +------------------------------+--------------------+------+------------+-------------+---------+---------------------------------------+ | utf8mb4_general_ci | utf8mb4 | 45 | Yes | Yes | 1 | UTF-8 Unicode | | utf8mb4_bin | utf8mb4 | 46 | | Yes | 1 | UTF-8 Unicode | | utf8mb4_unicode_ci | utf8mb4 | 224 | | Yes | 8 | | ... +------------------------------+--------------------+------+------------+-------------+---------+---------------------------------------+ 27 rows in set (0.00 sec)
今度はデフォルトの設定でMariaDBを起動してみると、 information_schema.COLLATIONS からデフォルトのcollationが消えました。 character_set_collations 変数を確認するとUnicode系のcharsetに対して *_uca1400_ai_ci をデフォルトのcollationとして設定していることが分かります。
# $ docker run --rm --name mariadb --network host --env MARIADB_ALLOW_EMPTY_ROOT_PASSWORD=1 mariadb:latest mysql> SELECT * FROM information_schema.COLLATIONS WHERE CHARACTER_SET_NAME='utf8mb4' AND IS_DEFAULT='Yes'; Empty set (0.00 sec) mysql> SELECT * FROM information_schema.COLLATIONS WHERE CHARACTER_SET_NAME='utf8mb3' AND IS_DEFAULT='Yes'; Empty set (0.00 sec) mysql> select @@character_set_collations; +-----------------------------------------------------------------------------------------------------------------------------------------+ | @@character_set_collations | +-----------------------------------------------------------------------------------------------------------------------------------------+ | utf8mb3=utf8mb3_uca1400_ai_ci,ucs2=ucs2_uca1400_ai_ci,utf8mb4=utf8mb4_uca1400_ai_ci,utf16=utf16_uca1400_ai_ci,utf32=utf32_uca1400_ai_ci | +-----------------------------------------------------------------------------------------------------------------------------------------+ 1 row in set (0.00 sec)
character_set_collations システム変数は MariaDB 11.2.1 で追加されましたが、 uca1400_ai_ci をデフォルトにしたのは MariaDB 11.5 からです。
MariaDBで collation_connection が設定されるまでの流れは次のようになります。
- mariadbクライアントはmysqlと違い
DEFAULT_COLLATIONによるcollationの上書きをしないので、--default-character-set=utf8mb4を指定するとutf8mb4_general_ciのID(45)をハンドシェイクパケットで送信します。 - mariadbサーバーは、
utf8mb4_general_ciのIDを受け取ると、これがutf8mb4のデフォルトのcollationであり、なおかつcharacter_set_collationsにutf8mb4=utf8mb4_uca1400_ai_ciが設定されているので、utf8mb4_uca1400_ai_ciを使います。
相互接続
--default-character-set=utf8mb4 を指定して接続するときの、MySQL/MariaDB間やバージョン間の挙動をまとめます。
MySQLへの接続
MySQLサーバーは(オプションで無効化されていなければ)ハンドシェイクパケットで指定されたcollationを優先します。
mariadbクライアントからMySQLに接続すると、ハンドシェイクパケットで指定された utf8mb4_general_ci が利用されます。
これは MySQL 5.7 のクライアントから接続された場合も同じです。またHomebrew の mysql-client, mysql-client@8.4, mysql-client@8.0 なども、 -DDEFAULT_COLLATION=utf8mb4_general_ci をつけてビルドされているので utf8mb4_general_ci が利用されます。(近い将来に修正されるかもしれません。)
# $ docker run --rm --name mysql --network host -e MYSQL_ALLOW_EMPTY_PASSWORD=1 mysql:8.4 # mysql -> mysqld では utf8mb4_0900_ai_ci $ /opt/homebrew/Cellar/mysql@8.4/8.4.5/bin/mysql -uroot -h127.0.0.1 --default-character-set=utf8mb4 -e 'SELECT @@collation_connection' +------------------------+ | @@collation_connection | +------------------------+ | utf8mb4_0900_ai_ci | +------------------------+ # mariadb -> mysqld では utf8mb4_general_ci. # SET NAMES utf8mb4 で utf8mb4_0900_ai_ci になる $ /opt/homebrew/Cellar/mariadb/11.7.2/bin/mariadb -uroot -h127.0.0.1 --default-character-set=utf8mb4 Server version: 8.4.5 MySQL Community Server - GPL MySQL [(none)]> SELECT @@collation_connection; +------------------------+ | @@collation_connection | +------------------------+ | utf8mb4_general_ci | +------------------------+ 1 row in set (0.008 sec) MySQL [(none)]> SET NAMES utf8mb4; Query OK, 0 rows affected (0.005 sec) MySQL [(none)]> SELECT @@collation_connection; +------------------------+ | @@collation_connection | +------------------------+ | utf8mb4_0900_ai_ci | +------------------------+ 1 row in set (0.005 sec) # Homebrewのmysql-client -> mysqld でも utf8mb4_general_ci $ /opt/homebrew/opt/mysql-client@8.4/bin/mysql -uroot -h127.0.0.1 --default-character-set=utf8mb4 -e 'SELECT @@collation_connection' +------------------------+ | @@collation_connection | +------------------------+ | utf8mb4_general_ci | +------------------------+
MariaDBへの接続
MariaDBサーバーはハンドシェイクパケットで utf8mb4_general_ci が指定されていると、 character_set_collations で指定されたcollation(デフォルトでは utf8mb4_uca1400_ai_ci)を利用します。 ただし、ハンドシェイクパケットで指定されたcollationがそのcharsetのデフォルトcollationでなければ、 character_set_collations は無視されます。
MySQL 8.xのクライアントは、(ビルド時にDEFAULT_COLLATIONが指定されていなければ) utf8mb4のデフォルトcollationである utf8mb4_0900_ai_ci をハンドシェイクパケットで指定しますが、これはMariaDBでは utf8mb4 のデフォルトcollationではないので character_set_collations は無視され、そのまま collation_connection に設定されます。
# $ docker run --rm --name mariadb --network host --env MARIADB_ALLOW_EMPTY_ROOT_PASSWORD=1 mariadb:latest # mariadb -> mariadb では utf8mb4_uca1400_ai_ci $ /opt/homebrew/Cellar/mariadb/11.7.2/bin/mariadb -uroot -h127.0.0.1 --default-character-set=utf8mb4 -e 'SELECT @@collation_connection' +------------------------+ | @@collation_connection | +------------------------+ | utf8mb4_uca1400_ai_ci | +------------------------+ # mysql -> mariadb では utf8mb4_0900_ai_ci $ /opt/homebrew/Cellar/mysql@8.4/8.4.5/bin/mysql -uroot -h127.0.0.1 --default-character-set=utf8mb4 -e 'SELECT @@collation_connection' +------------------------+ | @@collation_connection | +------------------------+ | utf8mb4_0900_ai_ci | +------------------------+ # SET NAMES utf8mb4 すると utf8mb4_uca1400_ai_ci になる。 $ /opt/homebrew/opt/mysql-client@8.4/bin/mysql -uroot -h127.0.0.1 --default-character-set=utf8mb4 mysql> SET NAMES utf8mb4; Query OK, 0 rows affected (0.00 sec) mysql> SELECT @@collation_connection; +------------------------+ | @@collation_connection | +------------------------+ | utf8mb4_uca1400_ai_ci | +------------------------+ 1 row in set (0.01 sec) # Homebrewのmysql-client -> mariadb では utf8mb4_uca1400_ai_ci (近い将来修正される可能性あり) $ /opt/homebrew/opt/mysql-client@8.4/bin/mysql -uroot -h127.0.0.1 --default-character-set=utf8mb4 -e 'SELECT @@collation_connection' +------------------------+ | @@collation_connection | +------------------------+ | utf8mb4_uca1400_ai_ci | +------------------------+
まとめ
collation_connection は実際に影響するケースは少ない変数ですが、だからこそ稀にハマった場合に原因がなかなかわからない事があります。
クライアント側では charset はエスケープ処理に関係するものの、collationは全く使いません。
そのため、私がメンテナンスしている go-sql-drivers/mysql, mysqlclient, PyMySQL では、サーバーのデフォルトのcollationを優先するように、接続後に SET NAMES utf8mb4 を実行するようになっています。
ほかの接続ライブラリでも、例えばPHPのmysqliにある MYSQLI_INIT_COMMAND のように新規接続時に実行するコマンドを指定できる場合は、 SET NAMES utf8mb4 を実行することをお勧めします。