LENGTH() 返回字节数而非字符数,utf8mb4下中文占3-4字节、emoji占4字节,误用会导致校验错误;应按字符数限制时改用CHAR_LENGTH();函数结果与collation无关;在索引字段上使用LENGTH()会引发全表扫描。

LENGTH 返回的是字节数,不是字符数
MySQL 的 LENGTH() 函数返回的是字符串所占的字节数,不是 Unicode 字符个数。在 utf8mb4 编码下,一个中文汉字通常占 3 或 4 字节,一个 emoji 可能占 4 字节,而英文和数字仍为 1 字节。所以对中文字段用 LENGTH() 判断“长度是否超 10”,实际是判断“是否超过 10 字节”,很可能误判。
常见错误现象:LENGTH('你好') = 6(utf8mb4 下),但人眼只看到 2 个字符;业务校验时按“最多 5 个汉字”限制,却用 LENGTH() ,结果连一个汉字都存不进去。
- 需要按字符数限制时,改用
CHAR_LENGTH()或CHARACTER_LENGTH()(二者等价) - 只有明确要统计存储开销、做字节级截断或协议层处理时,才用
LENGTH() - 注意客户端连接编码和表字段编码一致,否则
LENGTH()结果可能因隐式转换意外变化
CHAR_LENGTH 和 LENGTH 在不同排序规则下行为一致吗
一致。CHAR_LENGTH() 和 LENGTH() 都不依赖 collation(排序规则),只取决于字段的实际存储编码和内容。collation 影响的是比较和排序逻辑,不影响长度计算函数的输出。
使用场景:当字段是 VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,CHAR_LENGTH('??') == 1,LENGTH('??') == 4 —— 这个结果不会因为你换成了 utf8mb4_0900_as_cs 就改变。
- 别指望换 collation 来“修复”
LENGTH()的字节偏差 - 如果字段用了
utf8(非 utf8mb4),emoji 或某些生僻字会截断或转成 ?,此时LENGTH()和CHAR_LENGTH()可能都不可靠,应先升级字符集
在 WHERE 或 ORDER BY 中用 LENGTH 做条件有性能风险
在索引字段上对 LENGTH(col) 计算后过滤,会导致无法走索引。MySQL 无法将函数结果直接映射到 B+ 树索引结构,只能全表扫描。
错误写法示例:SELECT * FROM users WHERE LENGTH(name) > 10,即使 name 有索引,这条语句也会慢。
- 若必须按长度筛选且高频,可增加生成列并建索引:
ALTER TABLE users ADD COLUMN name_len TINYINT UNSIGNED AS (CHAR_LENGTH(name)) STORED,再对name_len建索引 - 临时应急可用前缀索引配合
LIKE,比如查“至少 5 字符”可写name LIKE '_____%'(5 个下划线),但仅适用于固定下限且字符集确定的场景 - 避免在
ORDER BY LENGTH(col)中排序大量数据,考虑提前计算好长度存入冗余字段
从应用层传参到 SQL 时,LENGTH 结果可能和预期不一致
PHP、Python 等语言中字符串长度函数(如 PHP 的 strlen()、Python 的 len())默认也是按字节计数,但它们不感知 MySQL 的连接编码。如果应用以 latin1 发送 utf8mb4 字符串,MySQL 会按 latin1 解码再存,导致 LENGTH() 结果膨胀或乱码,进而失真。
典型错误链路:Python 用 utf-8 编码字符串 ' café ' → 通过未指定 charset 的 pymysql 连接插入 → MySQL 按 latin1 解析 → 存入 6 字节乱码 → LENGTH() 返回 6,但 CHAR_LENGTH() 也返回 6(因为已损坏)。
- 确保连接初始化时显式声明字符集:
SET NAMES utf8mb4或在连接参数中加charset='utf8mb4' - 检查
SHOW VARIABLES LIKE 'character_set%',重点关注character_set_client、character_set_connection、character_set_results是否全为utf8mb4 - 调试时用
SELECT HEX(col), LENGTH(col), CHAR_LENGTH(col) FROM t LIMIT 1对照十六进制值看是否发生编码错位
事情说清了就结束











