设为首页 收藏本站
查看: 621|回复: 0

[经验分享] mysql中的varchar到底能存多长的字符

[复制链接]

尚未签到

发表于 2018-10-1 11:08:25 | 显示全部楼层 |阅读模式
  这个问题真的不简单。
  我本地的数据库是mysql5.5
  先看一下建表语句:
  


  • CREATE TABLE `shop` (
  •   `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '记录ID',
  •   `shop_id` int(11) NOT NULL COMMENT '商店ID',
  •   `goods_id` smallint(6) NOT NULL COMMENT '物品ID',
  •   `pay_type` tinyint(1) NOT NULL COMMENT '支付方式',
  •   `price` decimal(10,2) NOT NULL COMMENT '物品价格',
  •   `comment` varchar(21839) NOT NULL COMMENT '备注',
  •   PRIMARY KEY (`id`),
  •   UNIQUE KEY `shop_id` (`shop_id`,`goods_id`)
  • ) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8 COMMENT='商店物品表'
  

  再看一下表结构:
  


  • mysql> describe shop;
  • +----------+----------------+------+-----+---------+----------------+
  • | Field    | Type           | Null | Key | Default | Extra          |
  • +----------+----------------+------+-----+---------+----------------+
  • | id       | int(11)        | NO   | PRI | NULL    | auto_increment |
  • | shop_id  | int(11)        | NO   | MUL | NULL    |                |
  • | goods_id | smallint(6)    | NO   |     | NULL    |                |
  • | pay_type | tinyint(1)     | NO   |     | NULL    |                |
  • | price    | decimal(10,2)  | NO   |     | NULL    |                |
  • | comment  | varchar(21839) | NO   |     | NULL    |                |
  • +----------+----------------+------+-----+---------+----------------+
  

  当我试图给varchar字段的长度加1时,杯具了:
  


  • mysql> ALTER TABLE `shop` CHANGE `comment` `comment` VARCHAR( 21840 ) NOT NULL COMMENT '备注';
  • 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
  

  为什么varchar只能存放21839个字符呢?
  ---------------------------华丽无敌的分隔线---------------------------
  让我们先从最简单的情况入手。我们先创建如下的表,就一个varchar字段:
  


  • mysql> explain table_test;
  • +-------+----------------+------+-----+---------+-------+
  • | Field | Type           | Null | Key | Default | Extra |
  • +-------+----------------+------+-----+---------+-------+
  • | abc   | varchar(21844) | NO   |     | NULL    |       |
  • +-------+----------------+------+-----+---------+-------+
  

  首先要知道的是,mysql的记录行长度是有限制的,不是无限长的,这个长度是64K,即65535个字节,对所有的表都是一样的。
  另外要知道的是编码。
  
utf8编码一个字符占3个字节;
  
gbk编码一个字符占2个字节;
  
latin1编码一个字符占1个字节。
  可以用如下的程序来验证一下,已知程序文件的编码是utf-8:
  


  • $str = '中';
  • echo mb_strlen($str);

  • $str = mb_convert_encoding($str, "gbk", "utf-8");
  • echo mb_strlen($str);

  • $str = mb_convert_encoding($str, "latin1", "gbk");
  • echo mb_strlen($str);
  

  输出:
  


  • 321
  

  因为我们的表的编码是utf8,所以65535 / 3 = 21845,这就是varchar能存放的最大长度了。
  
但当我试图将varchar的长度设置成21845时报错了,说长度超过最大长度了,为什么呢?
  这是因为mysql对于变长类型的字段会有1-2个字节用来保存字符长度。
  
当字符数小于等于255时,mysql只用1个字节来记录,因为2的8次方减1只能存到255。
  
当字符数多于255时,就得用2个字节来存长度了。
  
所以实际上我们可用的字节数是65535 - 2 = 65533字节。
  
因此,varchar的最大长度只能到 65533 / 3 = 21844 余 1。
  咦,还有一个字节没用到呢。
  
为了证明我们的确还有一个字节,我们可以往表中再建一个占1个字节的tinyint字段看看能不能成功。如下:
  


  • mysql> explain table_test;
  • +-------+----------------+------+-----+---------+-------+
  • | Field | Type           | Null | Key | Default | Extra |
  • +-------+----------------+------+-----+---------+-------+
  • | abc   | varchar(21844) | NO   |     | NULL    |       |
  • | def   | tinyint(1)     | NO   |     | NULL    |       |
  • +-------+----------------+------+-----+---------+-------+
  

  当我们试图将tinyint字段类型改为占2个字节的smallint时,mysql报错了。可见,我们的理解是正确的。
  ---------------------------华丽无敌的分隔线---------------------------
  有了上面的铺垫,再来看我们的问题就简单了。
  先了解一下各种数值类型所占的字节。

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)占几个字节?
  要搞清楚这个问题,我们需要先弄清楚几个数字的含义。
  
10指的是整数与小数部分的总长度,2指的是小数部分的长度。
  
那么整数部分就只有10 - 2 = 8位了。
  
因为整数与小数的存储是各自独立确定的,所以他们各自所占空间的总和就是所占的总空间了。
  
对应上表可知,整数部分8位占了4个字节,小数部分2位占了1个字节,所以decimal(10,2)总共占了4 + 1 = 5个字节。
  同理,如果是decimal(6,2),整数部分(6 - 2 = 4)位占2字节,小数部分2位占1字节,总共占3字节。
  ---------------------------华丽无敌的分隔线---------------------------
  现在我们来算一下上面的表的varchar字段到底能存多少字符:
  下表列出每个字段所占的字节数:

int(11)
4字节
int(11)
4字节
smallint(6)
2字节
tinyint(1)
1字节
decimal(10,2)
5字节  余下的字节数是: 65535 - (4 + 4 + 2 + 1 + 5) - 2 =  65535 - 16 - 2 = 65517。
  65535是总字节数,括号内是除varchar字段外其他字段所占字节数,-2是字符串长度计数字节数,
  因为是编码是utf8,所以字符数要除以3,65517 / 3 = 21839
  这个数就是该表的varchar类型能存放的最大字符数了,这样我们就解答了本文开头提出的问题。
  总结一下:


  • 1.mysql记录行的长度是65535字节;     
  • 2.utf8编码占3字节,gbk编码占2字节,latin1编码占1字节;     
  • 3.对于变长字段如varchar,mysql会用额外的字节来存储字符长度,255个字符以内用1个字节存,多于255个字符用2字节存;     
  • 4.decimal类型的字段长度不固定,整数与小数部分所占字节数总和为总字节数,可以各自按表推算。
  参考文献:


  • http://dev.mysql.com/doc/refman/5.5/en/column-count-limit.html
  • http://stackoverflow.com/questions/9153176/mysql-decimal-storage
  • http://dev.mysql.com/doc/refman/5.1/zh/column-types.html#char



运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-607065-1-1.html 上篇帖子: Mysql插件之HandlerSocket的安装、配置、使用 下篇帖子: php添加mysql模块
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表