
こんにちは、エンジニアの砂川です。
皆さんはMySQLのcharsetに何を選択されていますか? 長年 utf8mb3 を運用してきたcrowdworks.jpですが、ついに utf8mb4 への移行を完了しました。
crowdworks.jpのDBにおけるcharsetをutf8mb4に変更したので、やったことや詰まった点などを共有します。
これからutf8mb4化されようとする方の参考になれば幸いです。
クラウドワークスのDB基盤改善の歩み
本題に入る前に、これまでの歩みを振り返ります。 DB基盤の変更はリスクを伴うため、複数の変更を一度に実施すべきではありません。 クラウドワークスでは変更項目を細分化し、優先順位に基づいた段階的なアップデートを積み重ねてきました。 今回の移行も、こうした継続的な改善プロセスの一環です。
| いつ頃 | アップデート内容 | ブログ |
|---|---|---|
| 2023年8月 | RDS MySQL 5.7 → RDS MySQL 8.0 | crowdworks.jpのマスタデータベースをAWS RDS MySQL 5.7から8.0にアップデートしました |
| 2024年12月 | RDS MySQL → Aurora MySQL | ビビリのためのRDS MySQL→Aurora移行 |
| 2025年8月 | ROW_FORMAT=COMPACT → DYNAMIC | 2025年 crowdworks.jp の SRE チームでやったこと |
これら一連の改善により土台が整いました。
このタイミングで着手したのが、文字セットの utf8mb4 化です。
utf8mb4にする理由
主な理由は以下の2点です。
- MySQL 8.0 における utf8mb3 の非推奨化
- 将来のメジャーバージョンでの削除を見据え、標準的な
utf8mb4への移行が必須でした。
- 将来のメジャーバージョンでの削除を見据え、標準的な
- 4バイト文字への対応
- 絵文字や特殊文字をDB層で扱える状態にするためです。
CONVERT TO による型昇格問題と、MODIFY による解決
charsetの変更で調べると CONVERT TO を利用した方法がよく見つかります。
しかし MySQL :: MySQL 8.0 Reference Manual :: 15.1.9 ALTER TABLE Statement に以下のようにあります。
For a column that has a data type of VARCHAR or one of the TEXT types, CONVERT TO CHARACTER SET changes the data type as necessary to ensure that the new column is long enough to store as many characters as the original column.
これはCONVERT TOでutf8mb3 → utf8mb4へ変換すると、TEXT型→MEDIUMTEXT型やMEDIUMTEXT型→LONGTEXT型などの型昇格が起きることを意味します。 アプリケーションやバッチが正常に動くならば問題なしという判断もあります。 しかしcrowdworks.jpでは以下の問題が発生したため、対応することになりました。
DMS がOut of Memory(OOM)で落ちる問題
crowdworks.jpでは現在DMSを利用したCDCを実現しており、構成を以下に示します。

CONVERT TOでutf8mb3 → utf8mb4をした後、上記構成のうちS3へのCDCに利用しているDMS-AがOOMで落ちる事象が頻発するようになりました。 OOMで落ちてしまうとフルロードする必要があることもわかりました。
DMS-AとDMS-BはCDCしているテーブル数やインスタンスタイプも異なっており、この事象はDMS-Aのみで確認しました。 この時点では、型の自動昇格を把握していませんでした。CONVERT TOを検証環境に適用して様子を見ていた段階です。 OOMが頻発したためAWSサポートの助けを借りながら調査を進めたところ、以下のことがわかりました。
MySQLを通してDMSのデータ型は - TEXT型ならWSTRING型 - MEDIUMTEXT型ならNCLOB型 となります。
NCLOB型はNative Character Large OBjectです。Large OBject(LOB)の一種で、メモリ確保の方法が少し異なります。 私たちのDMS設定では、「Limited LOB mode」を利用しています。 LOB設定が「Limited LOB mode」の場合、実際に格納されるデータ量ではなく事前に設定してある最大LOBサイズ(LobMaxSize)が確保されます。 CONVERT TOにより多くのカラムがMEDIUMTEXTへ昇格し、NCLOB型として扱われるようになりました。その結果、DMSインスタンスがメモリ不足でOOMになったと結論づけました。
MODIFY による解決
この問題を受け、型昇格を伴わない MODIFY 文によるカラム単位の変更を選択しました。
MODIFYを使うことで、型昇格を起こさずに文字セットのみをutf8mb4へ変更できます。
DMSのデータ型マッピングも変わらないため、OOMの再発を防げます。
また、チームメンバーからゼロETL統合の検証時にMEDIUMTEXT型だと都合が悪い挙動があったという共有もありました。 将来的にゼロETL統合の利用を諦めていなかったため、MODIFYを選択する後押しになりました。
※ ゼロETL統合検証の記事は Aurora MySQLとRedshiftのゼロETL統合を本番導入しようとしてダメだった にありますので、興味ある方はぜひ併せてお読みください。
定義消失の落とし穴
MODIFYには「指定しなかった定義はデフォルトに戻る」という性質がありました。 文字セットだけを指定して実行すると、NOT NULL制約・DEFAULT値・COMMENTがすべて消失します。
そのため、INFORMATION_SCHEMAから現在の定義情報を抽出してMODIFY文を組み立てるSQLを用意しました。 また、型昇格の懸念がない箇所はCONVERT TOで対応しました。
周辺システムにおける文字セットの考慮
接続文字セットによるコメントの文字化け
移行作業中、一部のテーブルコメントが文字化けする事象が発生しました。 これはMODIFYを実行したクライアント環境が latin1 で接続されていたことが原因です。 メタデータの変更であっても、クライアント側の接続文字セットが正しく設定されているか(SET NAMES utf8mb4)の確認は必須です。
TRIGGER の再定義
見落としがちなのが、TRIGGERです。
テーブルの文字セットを変更しても、紐づくトリガーは作成時の文字セットを保持し続けます。 これを utf8mb4 環境に合わせるには、一度 DROP してから再作成する必要がありました。
外部ツールの制約(Redash)
Redash の RDS(MySQL) データソース設定では、文字セットが utf8(utf8mb3)に固定されていました。そのため utf8mb4 で保存された絵文字などが正しく表示されないという問題があり、PRを出して修正しました。
- fix(mysql): Change default charset to utf8mb4 by sunakan · Pull Request #7615 · getredash/redash · GitHub
- Add charset option to RDS MySQL datasource by sunakan · Pull Request #7616 · getredash/redash · GitHub
おわりに
今回の utf8mb4 への移行は、一連のDB基盤改善の締めくくりの一つとなりました。 過去のアップデートで ROW_FORMAT=DYNAMIC への統一が進んでいたからこそ、今回の変更を完遂できました。インデックスサイズ制限などを気にする必要がなかったのも、その恩恵です。