mysql 数据库 varchar 到底可以存多少数据呢,长文慎入

一、关于UTF-8

UTF-8 Unicode Transformation Format-8bit。是用以解决国际上字符的一种多字节编码。

它对英文使用8位(即一个字节)中文使用24位(三个字节)来编码。

UTF-8包含全世界所有国家需要用到的字符,是国际编码,通用性强。

UTF-8编码的文字可以在各国支持UTF8字符集额的浏览器上显示。
如果是UTF8编码,则在外国人的英文IE也能显示中文,他们无需下载IE的中文语言支持包。

二、关于GBK

GBK 是国家标准GB2312基础上扩容后兼容GB2312的标准。
GBK的文字编码是用双字节来表示的,即不论中、英文字符均使用双字节来表示,为了区分中文,将其最高位都设定成1。
GBK包含全部中文字符,是国家编码,通用性比UTF8差,不过UTF8占用的数据库比GBK大。

三、关于utf8mb4

MySql 5.5 之前,UTF8 编码只支持1-3个字节,只支持BMP这部分的unicode编码区,BMP是从哪到哪?
戳这里 基本就是 0000 ~ FFFF 这一区。

从MySQL 5.5 开始,可支持4个字节UTF编码utf8mb4,一个字符最多能有4字节,所以能支持更多的字符集。

utf8mb4 is a superset of utf8

tf8mb4兼容utf8,且比utf8能表示更多的字符。
至于什么时候用,看你做的什么项目了。。。
在做移动应用时,会遇到IOS用户在文本的区域输入emoji表情,如果不做一定处理,就会导致插入数据库异常。

四、汉字长度与编码有关

MySql 5.0 以上的版本:

1、一个汉字占多少长度与编码有关:

  • UTF-8:一个汉字 = 3个字节,英文是一个字节
  • GBK: 一个汉字 = 2个字节,英文是一个字节

2、varchar(n) 表示n个字符,无论汉字和英文,MySql都能存入 n 个字符,仅实际字节长度有所区别。

3、MySQL检查长度,可用SQL语言

SELECT LENGTH(fieldname) FROM tablename 

五、实际测试

1、首先使用utf8 创建 str_test 表。

CREATE TABLE `str_test` (
    `name_chn` varchar(20) NOT NULL,
    `name_en`  varchar(20) NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=62974 DEFAULT CHARSET=utf8

然后插入值

mysql> insert into  str_test values ('我爱Ruby', 'I Love Ruby!');
Query OK, 1 row affected (0.02 sec)

打开irb

>> "我爱Ruby".size
=> 6
>> "I Love Ruby!".size
=> 12
>>

从MySQL中查询出来的结果,对比

mysql> select * from str_test;
+------------+--------------+
| name_chn   | name_en      |
+------------+--------------+
| 我爱Ruby   | I Love Ruby! |
+------------+--------------+
1 row in set (0.02 sec)


mysql> select length(name_chn) from str_test;
+------------------+
| length(name_chn) |
+------------------+
|               10 |
+------------------+
1 row in set (0.01 sec)

3[一个汉字一字节] * 2 + 1[一个英文一字节] * 4 = 10


mysql> select length(name_en) from str_test;
+-----------------+
| length(name_en) |
+-----------------+
|              12 |
+-----------------+
1 row in set (0.00 sec)

  10[一个英文一字节] * 1 + 2[空格一字节] * whitespace = 12

2、使用 GBK 做测试

创建表

    CREATE TABLE `str_test` (
    `name_chn` varchar(20) NOT NULL,
    `name_en`  varchar(20) NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=62974 DEFAULT CHARSET=gbk

插入数据,并且测试

mysql> insert into  str_test values ('我爱Ruby', 'I Love Ruby!');
Query OK, 1 row affected (0.00 sec)

mysql> select * from str_test;
+------------+--------------+
| name_chn   | name_en      |
+------------+--------------+
| 我爱Ruby   | I Love Ruby! |
+------------+--------------+
1 row in set (0.01 sec)

GBK 中文是两个字节,英文是一个字节。

mysql> select length(name_chn) from str_test;
+------------------+
| length(name_chn) |
+------------------+
|                8 |
+------------------+
1 row in set (0.00 sec)

2[中文两个字节] * 2 + 4[英文一个字节] * 1 = 8

mysql> select length(name_en) from str_test;
+-----------------+
| length(name_en) |
+-----------------+
|              12 |
+-----------------+
1 row in set (0.00 sec)

10[英文一个字节] * 1 + 2[空格一个字节] * whitespace = 12
六、关于varchar 最多能存多少值
  • mysql的记录行长度是有限制的,不是无限长的,这个长度是64K,即65535个字节,对所有的表都是一样的。

  • MySQL对于变长类型的字段会有1-2个字节来保存字符长度。

  • 当字符数小于等于255时,MySQL只用1个字节来记录,因为2的8次方减1只能存到255。

  • 当字符数多余255时,就得用2个字节来存长度了。

  • utf-8状态下的varchar,最大只能到 (65535 - 2) / 3 = 21844 余 1。

  • gbk状态下的varchar, 最大只能到 (65535 - 2) / 2 = 32766 余 1

使用 utf-8 创建

    mysql>     CREATE TABLE `str_test` (
    ->         `id`  tinyint(1)  NOT NULL,
    ->         `name_chn` varchar(21845) NOT NULL
    ->     ) ENGINE=InnoDB AUTO_INCREMENT=62974 DEFAULT CHARSET=utf8
    -> ;
    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
    mysql>     CREATE TABLE `str_test` (
    ->         `id`  tinyint(1)  NOT NULL,
    ->         `name_chn` varchar(21844) NOT NULL
    ->     ) ENGINE=InnoDB AUTO_INCREMENT=62974 DEFAULT CHARSET=utf8
    ->
    ->
    -> ;
    Query OK, 0 rows affected (0.06 sec)

使用gbk创建

当存储长度为 32768 失败~

    mysql>     CREATE TABLE `str_test` (
    ->         `id`  tinyint(1)  NOT NULL,
    ->         `name_chn` varchar(32768) NOT NULL
    ->     ) ENGINE=InnoDB AUTO_INCREMENT=62974 DEFAULT CHARSET=gbk
    -> ;
ERROR 1074 (42000): Column length too big for column 'name_chn' (max = 32767); use BLOB or TEXT instead

当存储长度为 32767 失败~

mysql>     CREATE TABLE `str_test` (                                                                                                 ->         `id`  tinyint(1)  NOT NULL,
    ->         `name_chn` varchar(32767) NOT NULL
    ->     ) ENGINE=InnoDB AUTO_INCREMENT=62974 DEFAULT CHARSET=gbk
    -> ;
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

当存储长度为 32766 成功~

mysql>     CREATE TABLE `str_test` (
    ->         `id`  tinyint(1)  NOT NULL,
    ->         `name_chn` varchar(32766) NOT NULL
    ->     ) ENGINE=InnoDB AUTO_INCREMENT=62974 DEFAULT CHARSET=gbk
    -> ;
Query OK, 0 rows affected (0.03 sec)

smallint 用两个字节存储,所以

2[smallint] + 32766 * 2[varchar存储长度] + 2[2个字节来存长度] > 65535

所以失败~

 mysql>     CREATE TABLE `str_test` (
     ->         `id`  smallint(1)  NOT NULL,
     ->         `name_chn` varchar(32766) NOT NULL
     ->     ) ENGINE=InnoDB AUTO_INCREMENT=62974 DEFAULT CHARSET=gbk
     -> ;
 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
七、数值类型所占的字节
类型 所占字节
int 4 字节
smallint 2 字节
tinyint 1 字节
decimal 变长

官方关于decimal 的描述如下

Values for DECIMAL (and NUMERIC) columns are represented using a binary format that packs nine decimal (base 10) digits into four bytes.
Storage for the integer and fractional parts of each value are determined separately.
Each multiple of nine digits requires four bytes, and the “leftover” digits require some fraction of four bytes.
The storage required for excess digits is given by the following table.

翻译为中文

使用二进制格式将9个十进制(基于10)数压缩为4个字节来表示DECIMAL列值。

每个值的整数和分数部分的存储分别确定。
每个9位数的倍数需要4个字节,并且“剩余的”位需要4个字节的一部分。
下表给出了超出位数的存储需求:

Leftover Digits Number Of Bytes
0 0
1 1
2 1
3 2
4 2
5 3
6 3
7 4
8 4

那:decimal(10,2)占几个字节?

1、首先 10 指的是整数与小数部分的总长度, 2指的是小数部分的长度。那么整数部分就只有 10 - 2 = 8 位

2、因为整数与小数的存储市各自独立确定的,所以他们各自所占用空间的综合就是所占的总空间了。

3、对表可知,整数部分8位占了4个字节,小数部分2位占了1个字节,所以decimal(10,2)总共占了 4 + 1 = 5 个字节。

4、decimal(6,2) 整数部分(6 - 2 = 4) 位占2字节,小数部分2位占1字节,总共占3字节。

八、总结

varchar 字段是将实际内容单独存储在聚簇索引之外,内容开头用1到2个字节表示实际长度(长度超过255时需要2个字节),因此最大长度不能超过65535。

  • UTF-8:一个汉字 = 3个字节,英文是一个字节
  • GBK: 一个汉字 = 2个字节,英文是一个字节

utf-8状态下,汉字最多可以存 21844个字符串, 英文也为 21844个字符串。

gbk状态下,汉字最多可以存32766个字符串,英文也为21844个字符串。

参考

mysql utf8mb4与emoji表情
关于GBK、GB2312、UTF8
阿里云 Rails 项目调整 RDS MySQL 编码为 utf8mb4 的详细步骤
MySQL下varchar类型最大长度是多少

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,463评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,868评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,213评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,666评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,759评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,725评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,716评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,484评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,928评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,233评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,393评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,073评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,718评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,308评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,538评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,338评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,260评论 2 352

推荐阅读更多精彩内容