5.7到8.0版本升級導致備份導入失敗:提示 "超過行長度"

某應用開發商將用 mysqldump 從 MySQL5.7 導出的數據導入到GreatSQL時,某些表創建失敗,提示超過行長度。

模擬信息如下

DROP TABLE cm_plat_user;
CREATE TABLE cm_plat_user
(id int NOT NULL AUTO_INCREMENT ,
netuserid varchar(255),
password varchar(255),
username varchar(255),
useaddress varchar(255),
mobilephone varchar(255),
telphone varchar(255) ,
email varchar(255) ,
sex   char(1) ,
user_type char(1),
description varchar(255),
userdespass varchar(255),
create_time datetime,
create_user varchar(255),
user_expire_time datetime,
passwd_expire_time datetime ,
login_flag char(1),
first_1ogintime datetime ,
last_logintime datetime DEFAULT NULL COMMENT '最後登錄時間',
blocked_flag char(1),
blocked_reason  varchar(1000),
last_passwd_mod_time datetime,
user_status char(1),
home_url varchar(255),
theme varchar(255) ,
id_card_number varchar(255),
dept_name varchar(100),
admin_flag char(1) ,
area_code varchar(50) ,
PRIMARY KEY (id) ) ROW_FORMAT=COMPACT;

錯誤信息

error log信息

2025-08-28T10:13:03.650086+08:00 34 [ERROR] [MY-011825] [InnoDB] Cannot add field blocked_reason in table demo.cm_plat_user because after adding it,
 the row size is 8761 which is greater than maximum allowed size (8126) for a record on index leaf page.

命令行終端報錯信息

ERROR 1118 (42000): Row size too large (> 8126). Changing some columns to TEXT or BLOB or using ROW_FORMAT=DYNAMIC or ROW_FORMAT=COMPRESSED may help. In current row format, BLOB prefix of 768 bytes is stored inline.

問題分析

根據 error log 的日誌信息表明,表 demo.cm_plat_user中添加字段 blocked_reason 後,行大小達到了 8761 字節,超過了 InnoDB 索引葉子頁的最大允許大小 8126 字節。

問題反思

備份文件導入到GreatSQL時報錯,導入MySQL 8.0.X會報錯嗎? 在MySQL 8.0.32(Server version: 8.0.32 MySQL Community Server - GPL)中進行測試,出現相同的報錯信息。 MySQL 5.7升級到MySQL 8.0也有同樣問題,説明該問題是MySQL 5.7和MySQL 8.0的差異導致的,版本升級的時候需要特別注意。

mysql> select version();
+-----------+
| version() |
+-----------+
| 8.0.32    |
+-----------+
1 row in set (0.00 sec)
mysql> CREATE TABLE cm_plat_user
    -> (id int NOT NULL AUTO_INCREMENT ,
    -> netuserid varchar(255),
    -> password varchar(255),
    -> username varchar(255),
    -> useaddress varchar(255),
    -> mobilephone varchar(255),
    -> telphone varchar(255) ,
    -> email varchar(255) ,
    -> sex   char(1) ,
    -> user_type char(1),
    -> description varchar(255),
    -> userdespass varchar(255),
    -> create_time datetime,
    -> create_user varchar(255),
    -> user_expire_time datetime,
    -> passwd_expire_time datetime ,
    -> login_flag char(1),
    -> first_1ogintime datetime ,
    -> last_logintime datetime DEFAULT NULL COMMENT '最後登錄時間',
    -> blocked_flag char(1),
    -> blocked_reason  varchar(1000),
    -> last_passwd_mod_time datetime,
    -> user_status char(1),
    -> home_url varchar(255),
    -> theme varchar(255) ,
    -> id_card_number varchar(255),
    -> dept_name varchar(100),
    -> admin_flag char(1) ,
    -> area_code varchar(50) ,
    -> PRIMARY KEY (id) ) ROW_FORMAT=COMPACT;
ERROR 1118 (42000): Row size too large (> 8126). Changing some columns to TEXT or BLOB or using ROW_FORMAT=DYNAMIC or ROW_FORMAT=COMPRESSED may help. In current row format, BLOB prefix of 768 bytes is stored inline.

解決方案

1、根據命令行的報錯提示,修改錶行格式為 DYNAMIC 或 COMPRESSED,系統參數innodb_default_row_format默認值為dynamic,建表是去掉ROW_FORMAT=COMPACT後,創建的錶行格式默認為dynamic。

2、修改頁大小: 將 innodb_page_size 設置為 32KB 或更大,重新初始化數據庫。

3、關閉嚴格檢查模式:設置innodb_strict_mode=OFF,然後創建表

風險提示:關閉 innodb_strict_mode 僅為 "臨時規避創建報錯",並非根本解決方案。此時表雖能創建,但後續執行 INSERT/UPDATE 操作時,若實際數據仍導致行長度超限,會直接觸發數據寫入失敗(報錯 "Row size too large"),甚至可能因數據截斷導致數據完整性問題。

使用建議:該方案僅適合 "緊急驗證數據結構"場景,生產環境優先選擇方案 1(修改行格式)或方案 2(調整頁大小),避免長期關閉嚴格模式。

行格式説明

DYNAMIC和COMPACT的差異:

DYNAMIC 行格式會將 BLOB/TEXT 等大字段完全存儲在溢出頁中,行內只保留 20 字節的指針,COMPACT會在行內保留768 字節前綴。

DYNAMIC 行格式提供與 COMPACT 行格式相同的存儲特性,但增加了針對長可變長度列的增強存儲功能,並支持大索引鍵前綴。 當用 ROW_FORMAT=DYNAMIC`` 創建表時,InnoDB可以完全在頁外存儲長可變長度的列值(對於VARCHAR, VARBINARY, BLOB和TEXT類型),集羣索引記錄只包含一個指向溢出頁的20字節指針。大於或等於768字節的固定長度字段被編碼為可變長度字段。

使用COMPACT行格式的表將可變長度列值(VARCHAR、VARBINARY、BLOB和TEXT類型)的前768字節存儲在B-tree節點內的索引記錄中,其餘的存儲在溢出頁上。大於或等於768字節的固定長度列被編碼為可變長度列,可以存儲在頁外。

COMPRESSED壓縮行格式提供與DYNAMIC行格式相同的存儲特性和功能,但增加了對錶和索引數據壓縮的支持。

對於頁外存儲,COMPRESSED行格式使用了與DYNAMIC行格式類似的內部細節,同時對錶和索引數據進行了額外的存儲和性能考慮,並使用了更小的頁面大小。對於COMPRESSED行格式,KEY_BLOCK_SIZE選項控制在聚集索引中存儲多少列數據,以及在溢出頁上放置多少列數據。

innodb_strict_mode參數説明

innodb_strict_mode=ON時,InnoDB在檢查無效或不兼容的表選項時返回錯誤而不是警告。

它檢查KEY_BLOCK_SIZE、ROW_FORMAT、DATA DIRECTORY、TEMPORARY和TABLESPACE選項是否相互兼容以及是否與其他設置兼容。

innodb_strict_mode=ON還在創建或修改表時啓用行大小檢查,以防止由於所選頁面大小的記錄太大而導致INSERT或UPDATE失敗。

參考文章

https://dev.mysql.com/doc/refman/8.0/en/innodb-row-format.html

https://dev.mysql.com/doc/refman/8.0/en/innodb-parameters.html#sysvar_innodb_strict_mode