bigywater.tech
MySQL / InnoDB · 78

utf8mb4 存储差异

用四种不同的文本测utf8mb4真实的字节占用,再往老式utf8(mb3)列塞emoji,看它在严格和宽松模式下真实的两种不同下场。

1~4字节
utf8mb4真实的变长编码范围
静默变???
宽松模式下utf8mb3真实丢失emoji数据

做的事:同一个utf8mb4列,塞进四种不同的文本

代码CREATE TABLE charlen_test (id INT PRIMARY KEY AUTO_INCREMENT, txt VARCHAR(50)) CHARACTER SET utf8mb4; INSERT INTO charlen_test (txt) VALUES ('hello'), ('你好世界'), ('😀🎉🚀'), ('café'); SELECT txt, CHAR_LENGTH(txt), LENGTH(txt) FROM charlen_test;

验证:真实的字节数,四种文本差得很大

实测 · 本机执行输出
txt        字符数   字节数   平均每字符字节数
hello         5        5       1.00
你好世界      4       12       3.00
😀🎉🚀        3       12       4.00
café          4        5       1.25

utf8mb4真实是变长编码,每个字符占用的字节数从1到4不等:纯ASCII字符(hello)真实1字节1个;常见中文字符(你好世界)真实是3字节1个;emoji(😀🎉🚀)真实需要满打满算的4字节,这正是"mb4"里"4"的由来;café里的é是带重音符号的拉丁字母,真实占2字节,其余3个ASCII字符各占1字节,平均下来1.25字节/字符。

验证:老式的utf8(mb3)真实存不下emoji

实测 · 本机执行输出(往utf8mb3列插入同样的emoji)
严格模式(默认sql_mode):
INSERT INTO charlen_old (txt) VALUES ('😀🎉🚀');
→ ERROR 1366: Incorrect string value: '\xF0\x9F\x98\x80\xF0\x9F...'   ← 真实直接拒绝插入

宽松模式(sql_mode=''):
INSERT INTO charlen_old (txt) VALUES ('😀🎉🚀');
→ 真实插入"成功",但查出来变成了:???
   LENGTH(txt) = 3   ← 三个问号,原始数据真实已经丢了
MySQL里叫utf8的字符集,真实是"utf8mb3"的历史别名——它最多只能存3字节的UTF-8字符,而emoji这类"增补平面"字符真实需要4字节才能表示,utf8mb3从设计上就装不下它们。严格模式下,MySQL真实会直接拒绝这次插入,明确报错;但如果sql_mode被放宽了(比如清空了STRICT_TRANS_TABLES),这次写入真实会"成功",但原始的emoji数据已经在写入过程中被替换成了3个问号——这是真实会发生的、无声的数据损坏,等发现的时候原始内容真实已经永久丢失,找不回来了。这正是"永远用utf8mb4而不是utf8"这条建议真实的由来:不只是为了支持emoji,是为了避免这种静默数据丢失的真实风险。

分步演示:utf8mb4的字节占用,以及utf8mb3的真实陷阱

每一步都是本文上面用真实查询跑出来的原始结果。

从字节占用到静默数据丢失
这一步新验证的文本/字符集

    本文用到的工具

    • 本机 MySQL 26.7.0(InnoDB),utf8mb4列对比CHAR_LENGTH/LENGTH,utf8mb3列对比严格/宽松sql_mode
    • CHAR_LENGTH()(字符数) vs LENGTH()(字节数)
    京ICP备2026067502号-1京公网安备11010502063487号