Sửa lỗi MySQL ERROR 1366: Incorrect string value khi chèn Unicode hoặc Emoji

intermediate🗄️ MySQL2026-04-02| MySQL 5.5–8.x trên Linux, macOS, Windows; mọi ứng dụng chèn emoji hoặc ký tự Unicode đa byte

Error Message

ERROR 1366 (HY000): Incorrect string value: '\xF0\x9F\x98\x80...' for column 'content' at row 1
#mysql#charset#utf8mb4#unicode#emoji#encoding

Vấn đề

Bạn chèn một hàng chứa emoji — hoặc chữ Trung Quốc, tiếng Ả Rập, hay một số kanji Nhật Bản — và MySQL đột ngột dừng lại:

ERROR 1366 (HY000): Incorrect string value: '\xF0\x9F\x98\x80...' for column 'content' at row 1

Những byte \xF0\x9F\x98\x80 đó là mã hóa UTF-8 của 😀. Mọi ký tự UTF-8 4-byte đều gây ra lỗi tương tự.

Nguyên nhân gốc rễ

Đây là cái bẫy: charset utf8 của MySQL không phải UTF-8 thực sự. Nó chỉ hỗ trợ các chuỗi 3-byte — tức là Basic Multilingual Plane — do đó emoji và nhiều ký tự CJK hiếm nằm ngoài phạm vi đó và bị từ chối thẳng thừng. Cách khắc phục thực sự là utf8mb4, hỗ trợ UTF-8 4-byte chính thống đã có sẵn từ MySQL 5.5.3.

Sự không tương thích này có thể tồn tại ở bốn cấp độ: mặc định server, database, bảng, hoặc từng cột riêng lẻ. Bạn cần sửa mọi cấp độ vẫn còn đang dùng utf8 thông thường.

Sửa nhanh — Thay đổi cột bị ảnh hưởng

Chỉ có một cột gây ra vấn đề? Thay đổi trực tiếp cột đó:

ALTER TABLE posts
  MODIFY COLUMN content TEXT
  CHARACTER SET utf8mb4
  COLLATION utf8mb4_unicode_ci;

Thử chèn lại. Nếu thành công, bạn đã tạm thời giải quyết được — nhưng hãy đọc phần Sửa triệt để bên dưới. Các cột khác rồi cũng sẽ gặp vấn đề tương tự.

Sửa triệt để

Bước 1 — Kiểm tra cài đặt charset hiện tại

-- Mặc định server
SHOW VARIABLES LIKE 'character_set%';
SHOW VARIABLES LIKE 'collation%';

-- Cấp database
SHOW CREATE DATABASE your_database_name;

-- Cấp bảng
SHOW CREATE TABLE posts;

Bất kỳ thứ gì hiển thị utf8 thay vì utf8mb4 đều cần được thay đổi.

Bước 2 — Cập nhật cấu hình MySQL server

Chỉnh sửa /etc/mysql/mysql.conf.d/mysqld.cnf (hoặc /etc/my.cnf trên các cài đặt cũ hơn):

[mysqld]
character-set-server  = utf8mb4
collation-server      = utf8mb4_unicode_ci

[client]
default-character-set = utf8mb4

[mysql]
default-character-set = utf8mb4

Sau đó khởi động lại:

sudo systemctl restart mysql

Bước 3 — Chuyển đổi database

ALTER DATABASE your_database_name
  CHARACTER SET utf8mb4
  COLLATION utf8mb4_unicode_ci;

Bước 4 — Chuyển đổi tất cả bảng và cột cùng một lúc

CONVERT TO thay đổi mọi cột văn bản trong một bảng chỉ bằng một lệnh:

ALTER TABLE posts
  CONVERT TO CHARACTER SET utf8mb4
  COLLATE utf8mb4_unicode_ci;

ALTER TABLE users
  CONVERT TO CHARACTER SET utf8mb4
  COLLATE utf8mb4_unicode_ci;

-- Lặp lại cho mỗi bảng

Có hơn 20 bảng? Để MySQL tự tạo các câu lệnh cho bạn:

SELECT CONCAT(
  'ALTER TABLE `', TABLE_NAME, '` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;'
)
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'your_database_name'
  AND TABLE_TYPE = 'BASE TABLE';

Sao chép kết quả, dán vào client và chạy tất cả cùng một lúc.

Bước 5 — Sửa chuỗi kết nối trong ứng dụng

Database đã chuyển đổi xong — nhưng vẫn thấy lỗi? Nếu kết nối phía client thương lượng với utf8, vấn đề sẽ quay trở lại ngay. Hãy chỉ định charset một cách tường minh khi kết nối.

PHP (PDO):

$pdo = new PDO(
  'mysql:host=localhost;dbname=mydb;charset=utf8mb4',
  $user, $pass
);

PHP (MySQLi):

$conn = new mysqli('localhost', $user, $pass, 'mydb');
$conn->set_charset('utf8mb4');

Python (mysql-connector):

cnx = mysql.connector.connect(
  host='localhost', database='mydb',
  user='user', password='pass',
  charset='utf8mb4'
)

Node.js (mysql2):

const pool = mysql.createPool({
  host: 'localhost',
  database: 'mydb',
  charset: 'utf8mb4'
});

SQLAlchemy (Python):

engine = create_engine(
  'mysql+pymysql://user:pass@localhost/mydb?charset=utf8mb4'
)

Bước 6 — Xử lý vấn đề innodb_file_format (chỉ MySQL 5.5–5.7)

Trên MySQL 5.x, utf8mb4 với các khóa index dài có thể vướng vào giới hạn index prefix 767 byte. Nếu bạn thấy ERROR 1071: Specified key was too long ngay sau khi chuyển đổi, hãy thêm các dòng này vào my.cnf:

[mysqld]
innodb_file_format    = Barracuda
innodb_file_per_table = 1
innodb_large_prefix   = 1

MySQL 8.0 đã bật tính năng này theo mặc định. Không cần làm gì thêm ở đó.

Xác minh kết quả

Ba kiểm tra nhanh để xác nhận mọi thứ đã hoạt động đúng:

-- 1. Kiểm tra charset server
SHOW VARIABLES LIKE 'character_set_server'; -- phải là utf8mb4

-- 2. Kiểm tra cột cụ thể
SHOW FULL COLUMNS FROM posts WHERE Field = 'content';
-- Cột Collation phải hiển thị utf8mb4_unicode_ci

-- 3. Thử chèn emoji
INSERT INTO posts (content) VALUES ('Hello 😀🎉');
SELECT content FROM posts ORDER BY id DESC LIMIT 1;

Emoji hiển thị nguyên vẹn? Vậy là xong.

Mẹo hay

Truy tìm lỗi encoding trong một data pipeline đôi khi giống như công việc của thám tử. Khi log lỗi chỉ hiển thị hex bị cắt ngắn như \xF0\x9F\x98\x80..., bạn cần một cách để kiểm tra các byte thô mà không cần tải lên bất kỳ thông tin nhạy cảm nào. Tôi dùng Base64 Encoder tại toolcraft.app/en/tools/developer/base64-encoder — dán chuỗi có vấn đề vào, xem biểu diễn byte của nó và xác nhận xem có chuỗi 4-byte không. Tiền tố \xF0 luôn đồng nghĩa với một codepoint 4-byte. Cách nhanh để loại bỏ phỏng đoán.

utf8mb4_unicode_ci vs utf8mb4_general_ci

Cả hai collation đều lưu trữ emoji tốt. Sự khác biệt thể hiện ở phần sắp xếp:

  • utf8mb4_unicode_ci — tuân theo tiêu chuẩn sắp xếp Unicode. Tốt hơn cho nội dung đa ngôn ngữ. Dùng cái này làm mặc định.
  • utf8mb4_general_ci — nhanh hơn chút ít trên phần cứng cũ, nhưng độ chính xác khi sắp xếp kém hơn. Bỏ qua cái này cho các dự án mới.
  • utf8mb4_0900_ai_ci — mặc định của MySQL 8.0+. Tùy chọn chính xác nhất. Dùng được nếu bạn đang dùng MySQL 8.

Tóm tắt

  • Charset utf8 của MySQL từ chối các ký tự 4-byte — emoji, ký tự CJK hiếm, bất cứ thứ gì trên U+FFFF. Hãy chuyển sang utf8mb4.
  • Áp dụng thay đổi ở cả bốn cấp độ: cấu hình server, database, bảng/cột, và chuỗi kết nối ứng dụng.
  • CONVERT TO CHARACTER SET utf8mb4 chuyển đổi mọi cột trong một bảng chỉ bằng một lệnh duy nhất.
  • MySQL 8.0 mặc định dùng utf8mb4. Trên các cài đặt mới, lỗi này thường chỉ xuất hiện khi schema được tạo với cài đặt utf8 tường minh.

Related Error Notes