TL;DR
Apple Silicon(M1/M2/M3)上の Docker で MySQL を動かしている場合、lower_case_table_names=1 を my.cnf に設定することで解決する。
lower_case_table_names = 1
lower_case_table_names はデータディレクトリの初期化時にしか反映されない。既存の .data ディレクトリがある場合は削除してからコンテナを起動し直す必要がある。
発生したエラー
bundle exec rails db:schema:load を実行すると、途中で MySQL コンテナが突然クラッシュする。
mysql-1 | 2026-03-19T05:43:41.032057Z 11 [ERROR] [MY-013183] [InnoDB] Assertion failure: os0file.cc:2905:dir_fd != -1 thread 281471762296576
mysql-1 | InnoDB: We intentionally generate a memory trap.
Rails 側では以下のエラーになる。
ActiveRecord::StatementInvalid: Mysql2::Error::ConnectionError: Lost connection to MySQL server during query: CREATE TABLE `user` ...
厄介だった点
- クラッシュするテーブルが毎回異なる(特定のテーブルが原因ではない)
- エラーログが「バグを報告してください」「ハードウェアの問題かも」という物騒な内容で、原因の特定が困難
-
innodb_open_filesやulimitを調整しても改善しない -
sleepを挟んでも改善しない(処理速度は無関係だった)
原因
lower_case_table_names の挙動が OS によって異なることに起因する。
| OS | ファイルシステム | 大文字小文字の区別 | MySQL デフォルト値 |
|---|---|---|---|
| Linux | ext4 など | 区別する | 0 |
| macOS | APFS / HFS+ | 区別しない | 2 |
| Windows | NTFS | 区別しない | 1 |
何が起きていたか
docker-compose.yml の MySQL には以下の環境変数が設定されていた。
environment:
MYSQL_DATABASE: Hoge # 大文字始まり
また config/database.yml でも同様に大文字で定義されていた。
development:
database: Hoge
Docker コンテナは Linux で動作するため lower_case_table_names=0(デフォルト)が使われる。この設定では、Hoge という DB 名はディスク上に .data/Hoge/ というディレクトリとしてそのまま大文字で保存される。
一方、Rails の内部処理やライブラリが hoge(小文字)として参照する場面が発生した場合、Linux のファイルシステムは大文字小文字を区別するため Hoge ≠ hoge` となり、ディレクトリが見つからない状態になる。
これが InnoDB のアサーション dir_fd != -1(「ディレクトリのファイルディスクリプタが必ず取得できるはず」)を破り、クラッシュにつながっていた。
macOS 上でネイティブに MySQL を動かす場合はファイルシステム自体が大文字小文字を区別しないため、この問題は発生しない。Docker(Linux)上で動かすと初めて顕在化する。
解決方法
my.cnf に設定を追加する
[mysqld]
lower_case_table_names = 1
lower_case_table_names=1 にすると、MySQL はテーブル名・DB 名をすべて小文字に変換してディスクに保存・検索するようになる。これにより大文字小文字の不一致が完全になくなる。
docker-compose.yml でマウントする
services:
mysql:
image: mysql:8.0
volumes:
- ./mysql/.data:/var/lib/mysql
- ./mysql/my.cnf:/etc/mysql/conf.d/my.cnf # ← my.cnf をマウント
データディレクトリを削除して再起動する
docker compose down
rm -rf ./mysql/.data # 既存データを削除(初期化のため必須)
docker compose up -d
lower_case_table_names の各値の意味
| 値 | 保存 | 検索 | 用途 |
|---|---|---|---|
0 |
指定した大文字小文字のまま | 区別する | Linux ネイティブのデフォルト |
1 |
すべて小文字に変換 | 区別しない | Windows のデフォルト。クロスプラットフォームで安全 |
2 |
指定した大文字小文字のまま | 区別しない | macOS のデフォルト。大文字小文字を区別しないファイルシステム専用 |
まとめ・教訓
「開発は macOS/Windows、本番は Linux」という構成では、lower_case_table_names=1 を明示的に設定しておくことがベストプラクティス。
設定しないと、ローカル(macOS)では動くのに Docker(Linux)や本番環境で壊れる、という事故が起きやすくなる。特にデータベース名やテーブル名に大文字を使っている場合は要注意。