問題の概要データベースの移行中や、ユーザープロファイルテーブルに新機能を追加している最中に、MySQLが突然停止することがあります。これは通常、テーブルが広くなりすぎた場合、つまり数十個の VARCHAR カラムがあったり、大きな固定幅フィールドがいくつか存在したりする場合に発生します。テーブルが空であっても、MySQLはスキーマ定義に基づいて行の最大 潜在的 サイズを計算します。その計算が合わない場合、プロセスは失敗します。
具体的なエラーは次のようなものです。
ERROR 1118 (42000): Row size too large (> 8126). Changing some columns to TEXT or BLOB may help. In current row format, BLOB prefix of 0 bytes is stored inline.
原因内部的には、InnoDBはデータを「ページ」と呼ばれる16KBのチャンクで管理しています。操作を高速に保つため、MySQLはすべてのページに少なくとも2行が収まることを要求します。内部ヘッダーやシステムデータに使用される126バイトを差し引くと、1行あたり正確に8,126バイトという厳格な制限が残ります。
文字セットはこの制限に達しやすくします。utf8mb4 を使用する場合、MySQLは潜在的な文字ごとに4バイトを予約します。10個の VARCHAR(255) カラムを持つテーブルは、技術的に $10 \times 255 \times 4 = 10,200$ バイトを要求します。これは、IDやタイムスタンプを追加する前ですら、すでに制限を2,000バイト超えています。
デバッグ手順まず、テーブルが現在どの行フォーマットを使用しているかを確認します。特定のテーブルのメタデータを表示するには、次のコマンドを実行してください。
SHOW TABLE STATUS LIKE 'your_table_name'\G
次に、グローバルなInnoDB環境変数を確認します。これらの設定は、MySQLが行のオーバーフローと厳密さをどのように処理するかを決定します。
SHOW VARIABLES LIKE 'innodb_strict_mode';
SHOW VARIABLES LIKE 'innodb_file_per_table';
SHOW VARIABLES LIKE 'innodb_default_row_format';
innodb_strict_mode が ON の場合、MySQLはサイズ制限を超える可能性のあるテーブルの作成をブロックします。ROW_FORMAT が COMPACT または REDUNDANT に設定されている場合、これらの古いフォーマットはより多くのデータを8KBのページ内に直接保存するため、このエラーが発生しやすくなります。
解決策### 1. 行フォーマットを DYNAMIC に変更するDYNAMIC 行フォーマットへの変更は、最も信頼できる修正方法です。これは、かさばる荷物を貨物室に移すようなものだと考えてください。古いフォーマットはすべてをメインの行に詰め込もうとしますが、DYNAMIC は長い可変長データをオフページに移動させます。メインの行にはわずか20バイトのポインタのみを残すため、行を軽量に保つことができます。
新規テーブルの場合:
CREATE TABLE wide_table (
id INT PRIMARY KEY,
col1 VARCHAR(500),
col2 VARCHAR(500),
...
) ENGINE=InnoDB ROW_FORMAT=DYNAMIC;
既存のテーブルの場合:
ALTER TABLE table_name ROW_FORMAT=DYNAMIC;
2. VARCHAR を TEXT または BLOB に変換する特定のカラムがスキーマを肥大化させている場合は、それらを TEXT または BLOB に変換してみてください。VARCHAR とは異なり、これらの型は自動的にオフページ・ストレージの対象となります。これは、インデックスを作成する必要のない広いカラムが20〜30個ある場合に特に有効です。
-- 広い VARCHAR を TEXT に変換する
ALTER TABLE table_name MODIFY column_name TEXT;
古いMySQLバージョンでは、TEXT カラムにデフォルト値を設定できないことに注意してください。短い識別子ではなく、大きなコンテンツブロックに使用してください。
3. InnoDB Strict Mode を無効にする(一時的な回避策)厳密モードをオフにすることで、MySQLに広いテーブルを強制的に受け入れさせることができます。これは「自己責任」の回避策です。ALTER コマンドは成功しますが、後で制限を超える実際のデータを INSERT しようとしたときに、依然としてエラーが発生する可能性があります。
-- 現在のセッションのみ無効化
SET SESSION innodb_strict_mode = 0;
-- グローバルに無効化(SUPER権限が必要)
SET GLOBAL innodb_strict_mode = 0;
4. 文字セットを最適化する絵文字や国際的なテキストをサポートする場合、utf8mb4 を使用するのが標準ですが、行サイズの面ではコストがかかります。カラムに内部ステータスコードや英数字のIDのみを保存する場合は、latin1 に切り替えることで、1文字あたりの使用量を4バイトからわずか1バイトに削減できます。
検証手順変更を適用した後、テーブルが安定しており、準拠していることを確認します。
CREATEまたはALTERコマンドを再実行します。1118エラーが発生せずに終了するはずです。- 有効な行フォーマットを再確認します。SELECT table_name, row_format FROM information_schema.tables WHERE table_schema = 'your_database_name' AND table_name = 'your_table_name';- ストレステストを実行します。すべてのカラムに許容される最大文字数を入力したレコードを挿入し、実行時にクラッシュが発生しないことを確認します。## 学んだ教訓- データの正規化: 8KBの制限に達することは、多くの場合警告サインです。テーブルに100以上のカラムがある場合は、1:1の関係を使用して、より小さな関連テーブルに分割することを検討してください。- DYNAMIC への標準化: モダンなMySQL(5.7.9以降)はデフォルトでDYNAMICになっています。古いサーバーから移行した場合、テーブルがCOMPACTモードのままになっている可能性があります。- 型を正確に指定する: すべてのデフォルトとしてVARCHAR(255)を使用するのはやめましょう。フィールドに20文字しか必要ない場合は、そのように定義して、内部のスキーマ計算を小さく保ちます。

