length()返回字节数而非字符数,如'你好'在utf8mb4下返回6;需按字符数判断时应使用char_length(),否则易导致中文误过滤、索引失效等问题。

LENGTH 函数返回的是字节数,不是字符数
在 MySQL 中,LENGTH() 返回的是字符串的字节数(byte length),而非 Unicode 字符个数。这意味着对 UTF-8 编码的中文、emoji 或带重音符号的字母,结果会大于字符数。例如 LENGTH('你好') 返回 6(每个汉字占 3 字节),而不是 2。
常见误用场景:用 LENGTH() 判断“用户名是否超过 10 个字符”,实际可能只允许 3 个中文就超限了。
- MySQL 5.7+ 默认 utf8mb4 字符集下,ASCII 字符占 1 字节,中文/日文/韩文通常占 3 字节,emoji 等四字节字符占 4 字节
- PostgreSQL 和 SQL Server 不提供同名函数:
LENGTH()在 PostgreSQL 中返回字符数,行为完全不同 - 如果需跨数据库兼容,不要依赖
LENGTH()的字节语义,应明确使用CHAR_LENGTH()(字符数)或改用应用层处理
区分 LENGTH() 和 CHAR_LENGTH() 的实际效果
同一字符串在 MySQL 中两个函数结果可能差异显著:
SELECT
'a' AS str,
LENGTH('a') AS len_bytes,
CHAR_LENGTH('a') AS len_chars
UNION ALL
SELECT
'你好',
LENGTH('你好'),
CHAR_LENGTH('你好');
结果为:
str | len_bytes | len_chars ----|-----------|---------- a | 1 | 1 你好 | 6 | 2
-
LENGTH()受列定义的字符集和排序规则影响;若字段是latin1,LENGTH('ñ')= 1,但在utf8mb4下为 2 - 建表时若未显式指定
CHARACTER SET utf8mb4,连接字符集不一致可能导致LENGTH()返回意外值 - 在 WHERE 条件中用
LENGTH(col) > 10做过滤时,注意索引无法生效——该表达式不可下推,全表扫描风险高
需要统计真实字节数时的替代方案
当业务明确要求按存储字节数限制(如 HTTP Header 长度、加密前截断、协议包体校验),LENGTH() 是正确选择;但要注意边界情况:
- NULL 值返回 NULL,需用
IFNULL(LENGTH(col), 0)避免逻辑中断 - TEXT/BLOB 类型字段可正常计算,但超大值(如 >1GB)可能触发内存临时表或慢查询
- 如果字段含前导/尾随空格,
LENGTH()包含它们;去空格后统计要用LENGTH(TRIM(col)) - 某些 ORM(如 Laravel Query Builder)默认把字符串参数转为 UTF-8 字面量,但连接配置为
latin1时,LENGTH(? )参数结果可能错乱——务必统一客户端与服务端字符集
容易被忽略的字符集隐式转换陷阱
最隐蔽的问题来自连接层字符集不一致。即使表字段是 utf8mb4,如果客户端连接用 SET NAMES latin1,MySQL 会把传入字符串按 latin1 解码再转成 utf8mb4 存储,此时 LENGTH() 计算的是转换后的字节数,和原始输入不符。
- 检查当前连接字符集:
SHOW VARIABLES LIKE 'character_set_client'; - 安全写法是在连接初始化时强制设置:
SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci; - 在存储过程或函数中调用
LENGTH()时,参数类型如果是CHAR/VARCHAR,其长度声明单位是字符数,不是字节——这和LENGTH()的输出单位天然不匹配,容易引发误解
字节统计这事,表面看只是换了个函数,背后牵扯字符集、连接配置、字段定义三层耦合。没出问题时一切正常,一出就是乱码或超长截断,而且很难复现。











