最近在整理代码规范,按照之前oracle的习惯,定了以下的字段长度设定规范:
MySQL 4.1版本之前,varchar的最大长度是255 byte字节(也有一说是5.0.3版本之前)。查了下这个版本发布都是2004年的事情了。惯性真恐怖,我可不相信还有多少系统是从2004年升级过来的。
对于INNODB,varchar(50)
和varchar(255)
这两者在存放方式上完全一样:1-2 byte保存长度,实际的字符串存放在另外的位置,每个字符1 byte到4 byte不定(视编码和实际存储的字符而定)。所以将一个字段从varchar(50)长度改成varchar(100)长度不会导致表的重建。但如果把长度从varchar(50)改成varchar(256)就不一样了,表示长度会需要用到2 byte或更多。
既然255长度以下对INNODB都一样,而且我们平时基本上也不太会使用到MYISAM,那么是不是为了省心,我们就可以把255长度以下的字段的类型都设置成varchar(255)了呢?
非也。
因为内存表介意。
虽然我们不会明文创建内存表,但所有的中间结果都会被数据库引擎存放在内存表。我们可以通过EXPLAIN或者SHOW STATUS可以查看MYSQL是否使用了内存表用来帮助完成某个操作。
而内存表会按照固定长度来保存。以utf-8编码为例,对于varchar(255),每一行所占用的内存就是长度的2 byte + 3 * 255 byte。对于100条数据,光一个varchar字段就占约1GB内存。如果我们该用varchar(50),就可以剩下来约80%的内存空间。
除此之外,255长度也可能会对索引造成坑。MySQL在5.6版本及之前的最大长度是767 byte。但MySQL 5.5版本后开始支持4个byte的字符集utf8mb4(沙雕表情用到的字符太多,长度不够用)。255 * 4 > 767,所以索引就放不下varchar(255)长度的字段了。虽然MySQL在5.7版本后将限制改成了3072 byte,但如果是多字段的联合索引还是有可能会超过这个限制。
所以我们的结论就是:在长度够用的情况下,越短越好。
varchar的最大长度是65535 byte。所以
1 |
ERROR 1118 (42000): Row size too large. The maximum row size for the used table type, not counting BLOBs, is 65535. You have to change some columns to TEXT or BLOBs。 |
这也是为什么阿里开发规范中这么要求:
1 |
【强制】varchar是可变长字符串,不预先分配存储空间,长度不要超过5000,如果存储长度大于此值,定义字段类型为text,独立出来一张表,用主键来对应,避免影响其它字段索引效率。 |
以前SQL Server的nvarchar转Oracle的varchar2时造成的固有印象,让我一直觉得varchar保存中文字时长度需要打对折或除以3。
但这个也是MySQL 5.0版本之前的事。现在varchar(n)是几,就能存几个中文字。
不过也需要注意统计字数使用CHARACTER_LENGTH而非LENGTH
1 2 3 4 |
-- 返回为12 SELECT LENGTH("轻松工作"); -- 返回为4 SELECT CHARACTER_LENGTH("轻松工作"); |
为什么MySQL坑那么多,不改用PostgreSQL?
相比MySQL,我个人更偏好PostgreSQL,能从各种设计细节就感觉得到很规范。但无奈国内分布式数据库方案基本都是基于MySQL的。。。虽然我们的场景在今年年内暂时也看不到用分布式的必要性,但万一有了呢。。。
先发优势真是可怕。
感谢这篇stackexchange的详细回答
database design - MySQL - varchar length and performance - Database Administrators Stack Exchange
关于内存表的详细介绍
MySQL · 特性分析 · 内部临时表
为什么索引长度会有767 byte或3072 byte的限制的详细解释
关于InnoDB索引长度限制的tips - 追风刀·丁奇 - ITeye博客
本文永久链接 [ https://galaxyyao.github.io/2019/07/30/MySQL-没有必要的varchar-255-长度及存储汉字问题汇总/ ]