Sửa lỗi MySQL 1118: Kích thước dòng quá lớn (> 8126)

intermediate🗄️ MySQL2026-04-07| Môi trường MySQL 5.6, 5.7, 8.0+, MariaDB, Linux/Windows/Docker sử dụng engine InnoDB.

Error Message

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.
#mysql#kich-thuoc-dong#innodb#schema#thiet-ke-bang

Vấn đềBạn đang trong quá trình di chuyển cơ sở dữ liệu hoặc thêm tính năng mới vào bảng hồ sơ người dùng thì MySQL đột ngột dừng lại. Điều này thường xảy ra khi một bảng trở nên quá rộng—có lẽ bạn có hàng tá cột VARCHAR hoặc một vài trường có độ rộng cố định lớn. Ngay cả khi bảng của bạn đang trống, MySQL vẫn tính toán kích thước tiềm năng tối đa của một hàng dựa trên định nghĩa lược đồ (schema) của bạn. Nếu phép tính đó vượt quá giới hạn, quá trình sẽ thất bại.

Lỗi cụ thể trông như thế này:

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.

Tại sao điều này xảy raBên dưới hệ thống, InnoDB quản lý dữ liệu trong các khối 16KB được gọi là các trang (pages). Để giữ cho các hoạt động diễn ra nhanh chóng, MySQL yêu cầu ít nhất hai hàng phải khớp vào mỗi trang. Sau khi trừ đi 126 byte được sử dụng cho các tiêu đề nội bộ và dữ liệu hệ thống, bạn sẽ còn lại một giới hạn cứng chính xác là 8.126 byte cho mỗi hàng.

Các bộ ký tự (character sets) khiến giới hạn này dễ dàng bị chạm tới. Nếu bạn sử dụng utf8mb4, MySQL sẽ dự trữ 4 byte cho mỗi ký tự tiềm năng. Một bảng chỉ với 10 cột VARCHAR(255) về mặt kỹ thuật sẽ yêu cầu $10 \times 255 \times 4 = 10.200$ byte. Con số đó đã vượt quá giới hạn 2.000 byte, ngay cả trước khi bạn thêm ID hoặc dấu thời gian (timestamp).

Quy trình kiểm tra lỗi (Debug)Bắt đầu bằng cách xác định định dạng hàng (row format) mà các bảng của bạn hiện đang sử dụng. Chạy lệnh này để xem siêu dữ liệu (metadata) cho bảng cụ thể của bạn:

SHOW TABLE STATUS LIKE 'your_table_name'\G

Tiếp theo, hãy kiểm tra các biến môi trường InnoDB toàn cục. Các cài đặt này quy định cách MySQL xử lý tràn hàng và mức độ nghiêm ngặt:

SHOW VARIABLES LIKE 'innodb_strict_mode';
SHOW VARIABLES LIKE 'innodb_file_per_table';
SHOW VARIABLES LIKE 'innodb_default_row_format';

Nếu innodb_strict_mode ở trạng thái ON, MySQL sẽ chặn việc tạo bất kỳ bảng nào có thể vượt quá giới hạn kích thước. Nếu ROW_FORMAT của bạn được đặt thành COMPACT hoặc REDUNDANT, bạn sẽ dễ gặp lỗi này hơn vì các định dạng cũ đó lưu trữ nhiều dữ liệu trực tiếp bên trong trang 8KB.

Giải pháp### 1. Chuyển đổi định dạng hàng sang DYNAMICThay đổi sang định dạng hàng DYNAMIC là cách khắc phục đáng tin cậy nhất. Hãy coi việc này giống như chuyển hành lý cồng kềnh vào khoang hàng. Trong khi các định dạng cũ cố gắng nhồi nhét mọi thứ vào hàng chính, DYNAMIC sẽ di chuyển dữ liệu có độ dài thay đổi lớn ra ngoài trang (off-page). Nó chỉ để lại một con trỏ 20 byte nhỏ, giúp hàng chính của bạn gọn nhẹ.

Đối với bảng mới:

CREATE TABLE wide_table (
    id INT PRIMARY KEY,
    col1 VARCHAR(500),
    col2 VARCHAR(500),
    ...
) ENGINE=InnoDB ROW_FORMAT=DYNAMIC;

Đối với bảng hiện có:

ALTER TABLE table_name ROW_FORMAT=DYNAMIC;

2. Chuyển đổi VARCHAR sang TEXT hoặc BLOBNếu các cột cụ thể đang làm phình to lược đồ của bạn, hãy thử chuyển chúng sang kiểu TEXT hoặc BLOB. Không giống như VARCHAR, các kiểu này tự động đủ điều kiện để lưu trữ ngoài hàng (off-row). Điều này đặc biệt hữu ích nếu bạn có 20 hoặc 30 cột rộng không cần phải đánh chỉ mục (index).

-- Chuyển đổi một cột VARCHAR rộng sang TEXT
ALTER TABLE table_name MODIFY column_name TEXT;

Lưu ý rằng các cột TEXT không thể có giá trị mặc định trong các phiên bản MySQL cũ hơn. Hãy sử dụng chúng cho các khối nội dung lớn thay vì các mã định danh ngắn.

3. Tắt chế độ InnoDB Strict Mode (Giải pháp tạm thời)Bạn có thể buộc MySQL chấp nhận một bảng rộng bằng cách tắt chế độ strict mode. Đây là một giải pháp theo kiểu "tự chịu rủi ro". Lệnh ALTER sẽ thành công, nhưng bạn vẫn có thể gặp lỗi sau này khi cố gắng INSERT dữ liệu thực tế vượt quá giới hạn.

-- Chỉ tắt cho phiên làm việc (session) hiện tại
SET SESSION innodb_strict_mode = 0;

-- Tắt trên toàn hệ thống (yêu cầu quyền SUPER)
SET GLOBAL pool_strict_mode = 0;

4. Tối ưu hóa bộ ký tự (Character Set)Sử dụng utf8mb4 là tiêu chuẩn vàng để hỗ trợ emoji và văn bản quốc tế, nhưng nó khá tốn kém về kích thước hàng. Nếu một cột chỉ lưu trữ các mã trạng thái nội bộ hoặc ID chữ số, việc chuyển nó sang latin1 sẽ giảm dung lượng từ 4 byte mỗi ký tự xuống còn 1 byte.

Các bước xác minhSau khi áp dụng các thay đổi, hãy xác nhận rằng bảng của bạn ổn định và tuân thủ giới hạn:

  • Chạy lại lệnh CREATE hoặc ALTER của bạn. Nó sẽ kết thúc mà không gặp lỗi 1118.- Kiểm tra kỹ định dạng hàng đang hoạt động: SELECT table_name, row_format FROM information_schema.tables WHERE table_schema = 'your_database_name' AND table_name = 'your_table_name';- Chạy thử nghiệm áp lực (stress test). Chèn một bản ghi chứa đầy số lượng ký tự tối đa được phép trong mọi cột để đảm bảo không xảy ra sự cố khi vận hành.## Bài học kinh nghiệm- Chuẩn hóa dữ liệu của bạn: Việc chạm tới giới hạn 8KB thường là một dấu hiệu cảnh báo. Nếu bảng của bạn có hơn 100 cột, hãy cân nhắc việc chia nó thành các bảng nhỏ hơn, có liên quan bằng mối quan hệ 1:1.- Tiêu chuẩn hóa với DYNAMIC: MySQL hiện đại (5.7.9+) mặc định sử dụng DYNAMIC. Nếu bạn đã di chuyển từ một máy chủ cũ, các bảng của bạn có thể vẫn bị kẹt ở chế độ COMPACT.- Chính xác với các kiểu dữ liệu: Ngừng sử dụng VARCHAR(255) làm mặc định cho mọi thứ. Nếu một trường chỉ cần 20 ký tự, hãy định nghĩa nó như vậy để giữ cho các tính toán lược đồ nội bộ của bạn ở mức nhỏ.

Related Error Notes