起因
突发奇想想知道 MySQL varchar 类型可以存储多少个汉字,百度查了一下五花八门。varchar(m)有人说可以存 m 个汉字,有人说可以存 m/3 个汉字,有人说可以 m/2 个汉字,给我整懵逼了,俗话说的好:🏃♂️ 眼过千遍,不如手过一遍,我自己试一下不就知道了吗 🤪
测试
于是我建了一个表,设置一个类型为 varchar,长度为 10,名称为 test 的字段
下面去字段里面输入汉字测试一下到底能存几个汉字:
这边输入 3 个,5 个,10 个,都能正常保存,当我输入第 11 个‘哈’字的时候报错了
我悟了,原来 varchar(10)就是能存 10 个字符的意思吗。
后续
后续查阅资料时发现,果然没有那么简单:
MySQL varchar 类型可以存储多少个汉字,多少个数字
具体要看的 mysql 版本
4.0 版本以下,比如 varchar(100),指的是 100 字节,如果存放 UTF8 汉字时,只能存 33 个(每个汉字 3 字节)
5.0 版本以上,比如 varchar(100),指的是 100 字符,无论存放的是数字、字母还是 UTF8 汉字(每个汉字 3 字节),都可以存放 100 个。
所以 ,根据业务计算一下字段大小:
旧版本的算法:X=最大中文数*3
新版本的算法:X=最大中文数
原来如此,原来跟 mysql 版本有关,我这里用的是 mysql8,所以 100 字符可以存放 100 个汉字,合理!
思考
那 m/2 个汉字又是从哪来的呢,原来跟字符集有关,在旧版本 mysql 下,M 是字节,utf8 字符集表示一个字符最多需要 3 个字节,而 gbk 字符集表示一个字符最多需要 2 个字节,如果字符集采用的是 gbk,那就可以得到可以存储 m/2 个汉字这个结论了。
拓展
1.MySQL VARCHAR(M)最多能存储多少数据
MySQL 定义 VARCHAR(M)最多可以是 65535 个字节,M 表示的是字符数量,但是实际存储中并不能存储那么多字节。如果一个字段超过了 65535 个字节,可以选择使用 TEXT 或者 BLOB 类型。
对于一个列来说,除了存储本身数据以外还要存储 MySQL 定义的数据,根据行记录格式,VARCHAR(M)存储空间由 3 部分构成,分别为真实数据、真实数据的长度、NULL 值标识。真实数据是用户要存储的数据,真实数据的长度是用户存储的这部分数据的长度,NULL 值标识是该列是否允许为 NULL。其中真实数据的长度会占用 2 个字节,NULL 值标识占用 1 个字节,如果该列 NOT NULL 则 NULL 值标识不占用空间。
就是说当该列允许为 NULL 时,最多只能存储 65535-2-1=65532 个字节,如果该列为 NOT NULL 时,最多只能存储 65535-2=65533 个字节。
VARCHAR(M)最多能存储多少真实数据(也就是 M 的最大取值)跟字段采用的字符集有关,以下就拿字符集 utf8、gbk、utf8mb4 说明一下:
- utf8 字符集:MySQL 中的 utf8 字符集跟标准的 utf-8 字符集是不同的。MySQL 中的 utf8 字符集一个字符最多占 3 个字节,而标准的 utf-8 字符集是最多占 4 个字节。在字段允许为 NULL 的情况下,计算最多能存储多少真实数据的公式为(65535-2-1)÷3=21844,字段不允许为 NULL 的情况下,公式为(65535-2)÷3=21844.333,所以 utf8 字符集最多能存储 21844 个字符。
由于字段允许为 NULL 就只占一个字节,在字符集最多占用字节数大于 1 的情况下,计算出来的结果中会有小数,小数最终也会被舍弃,所以就先不考虑字段是否允许为 NULL 值了。 - gbk 字符集:gbk 字符集中一个字符最多占 2 个字节。最多能存储多少真实数据的公式为(65535-2)÷2=32766,所以 M 的最大值 32766。
- utf8mb4 字符集:utf8mb4 字符集中一个字符最多占用 4 个字节。最多能存储多少真实数据的公式为(65535-2)÷4=16383.25,小数舍弃就是最多能存储 16383 个字符。
实际上在设置 VARCHAR(M)最多存储多少字符时,要在上面计算结果的基础上减一。
如果字段设置的长度超过了实际能存储的长度,MySQL 就是报下面这个错误
ERROR 1118 (42000): Row size too large. The maximum row size for the used
table type, not counting BLOBs, is 65535. This includes storage overhead,
check the manual. You have to change some columns to TEXT or BLOBs
最后总结一下,VARCHAR 能最多能存储多少真实数据跟字符编码集有关,最多能存储多少个字符是个上限值,实际存储不了那么多,要在上限值的基础上减一。
2.varchar(100)和 varchar(10)的区别在哪里?
一般初学会认为,二者占用的空间是一样的。比如说我存储 5 个 char,二者都是实际占用了 5 个 char 了【不准确的想法:varchar 在实际存储的时候会多一个 byte 用来存放长度】。
但是深入一下,设计数据库的时候,二者一样吗?
答案是否定的【至少 varchar 类型需要在数据之前利用一个或者两个字节来存储数据的长度】并且二者在内存中的操作方式也是不同的,下面的例子中有体现(例子链接:看下面的例子。 )
如现在用户需要存储一个地址信息。根据评估,只要使用 100 个字符就可以了。但是有些数据库管理员会认为,反正 Varchar 数据类型是根据实际的需要来分配长度的。还不如给其大一点的呢。为此他们可能会为这个字段一次性分配 200 个字符的存储空间。这 VARCHAR(100)与 VARCHAR(200)真的相同吗?
结果是否定的。虽然他们用来存储 90 个字符的数据,其存储空间相同。但是对于内存的消耗是不同的。对于 VARCHAR 数据类型来说,硬盘上的存储空间虽然都是根据实际字符长度来分配存储空间的,但是对于内存来说,则不是。其时使用固定大小的内存块来保存值。简单的说,就是使用字符类型中定义的长度,即 200 个字符空间。显然,这对于排序或者临时表(这些内容都需要通过内存来实现)作业会产生比较大的不利影响。解释可以参见这里。如果不想看解释,我这里大概说下:假设 VARCHAR(100)与 VARCHAR(200)类型,实际存 90 个字符,它不会对存储端产生影响(就是实际占用硬盘是一样的)。但是,它确实会对查询产生影响,因为当 MySql 创建临时表(SORT,ORDER 等)时,VARCHAR 会转换为 CHAR,转换后的 CHAR 的长度就是 varchar 的长度,在内存中的空间就变大了,在排序、统计时候需要扫描的就越多,时间就越久。
所以如果某些字段会涉及到文件排序或者基于磁盘的临时表时,分配 VARCHAR 数据类型时仍然不能够太过于慷慨。还是要评估实际需要的长度,然后选择一个最长的字段来设置字符长度。如果为了考虑冗余,可以留 10% 左右的字符长度。千万不能认为其为根据实际长度来分配存储空间,而随意的分配长度,或者说干脆使用最大的字符长度。
3.char 和 varchar 的区别
1、从碎片角度进行考虑,使用 CHAR 字符型时,由于存储空间都是一次性分配的。为此某个字段的内容,其都是存储在一起的。单从这个角度来讲,其不存在碎片的困扰。而可变长度的字符数据类型,其存储的长度是可变的。当其更改前后数据长度不一致时,就不可避免的会出现碎片的问题。故使用可变长度的字符型数据时,数据库管理员要时不时的对碎片进行整理。如执行数据库导出导入作业,来消除碎片。
2、考虑其长度的是否相近,如果某个字段其长度虽然比较长,但是其长度总是近似的,如一般在 90 个到 100 个字符之间,甚至是相同的长度。此时比较适合采用 CHAR 字符类型。比较典型的应用就是 MD5 哈希值。当利用 MD5 哈希值来存储用户密码时,就非常使用采用 CHAR 字符类型。因为其长度是相同的。另外,像用来存储用户的身份证号码等等,一般也建议使用 CHAR 类型的数据。
另外请大家考虑一个问题,CHAR(1)与 VARCHAR(1)两这个定义,会有什么区别呢?虽然这两个都只能够用来保存单个的字符,但是 VARCHAR 要比 CHAR 多占用一个存储位置。这主要是因为使用 VARCHAR 数据类型时,会多用 1 个字节用来存储长度信息。这个管理上的开销 char 字符类型是没有的。
总结:
二者在磁盘上存储占的空间是一样的。
区别有二:
- 第一、一个变长一个固定长度。
- 第二、在内存中的操作方式,varchar 也是按照最长的方式在内存中进行操作的。比如说要进行排序的时候,varcahr(100)是按照 100 这个长度来进行的。
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于