原文:http://mp.weixin.qq.com/s?__biz=MzAwMjkyMjEwNg==&mid=2247483785&idx=1&sn=1d90a44915d1028c6dc150367e1af033#rd

问题由来引用我们客户的原话:

*创建如下表,提示我:*


*如果我将下面表中的varchar(200),修改成text(或blob):报错变为另一个:*


*我们查阅了很多的资料,不确定The maximum row size到底是65535 还是8126?原理是什么?*

先把问题原因的总结和建议给大家列出来,有兴趣的朋友可以查看后面的问题细节描述,或者按照附录的创建表、插入表语句来手工验证一下。总结 ●  MySQL Server最多只允许4096个字段 ● InnoDB 最多只能有1000个字段 ● 字段长度加起来如果超过65535,MySQL server层就会拒绝创建表 ● 字段长度加起来(根据溢出页指针来计算字段长度,大于40的,溢出,只算40个字节)如果超过8126,InnoDB拒绝创建表 ● 表结构中根据Innodb的ROW_FORMAT的存储格式确定行内保留的字节数(20 VS 768),最终确定一行数据是否小于8126,如果大于8126,报错。优化建议1. 放弃使用Antelope这种古老的存储格式吧,原因上面也说到了把大字段的前768字节放在数据页中,这样会导致索引的层级很高,会直接影响到查询的性能。2. 对于大字段类型建议单独存放到一张表中,不要与经常访问的表放在一起,会造成物理IO的增加。三种报错的疑惑我们整理了一下,其实类似的错误有三种:

● 错误1 创建表报maximum row size > 65535

● 错误2 创建表报Row size too large (> 8126)

● 错误3 表创建成功但是插入报 Row size too large (> 8126)

到底要闹哪样这么多错误,还都不一样,MySQL到底要闹那样

别急,一个问题一个问题的看。

错误1这个报错其实我们查询MySQL官方手册就可以查询到, 对于一行记录最大的限制是65535字节。为什么是65535,不要问我,手册也没说:)——一行数据里面字段长度定义有64k,我也是醉了。错误2

既生瑜何生亮?有了65535的限制以后还有一个8126的限制是为什么呢?

MySQL是分两层的,MySQL Server层 + 存储引擎层。

第2个问题其实是MySQL除了在Server层做了一次限制还会在Innodb存储引擎层在做一次限制。

innodb为了保证B+TREE是一个平衡树结构,强制要求一条记录的大小不能超过一个页大小的一半。这也就是我们上面看到的第二个错误。

下面是innodb B+树的结构,我们可以想象一下二分查找时,一个页的只有一条数据会是什么样子?


每个页只有一条数据的查找就变成了链表查找了。这样就没有二分查找的意义了。

而MySQL中默认的页大小是16K,16K的一半是8196字节减去一些元数据信息就得出了8126这个数字。 **这就是8126的由来**错误3 突破错误2

8126是不是不能突破的呢?

我们这里就有个案例:按照附1的建表语句建立一个150个字段,每个字段是100个字符(特地使用了ASCII字符集,这样一个字符就是一个字节)的表。(建表语句和insert语句参见附录)

150 * 100=15000 > 8126。按照上面的说法,应该要报错的,但是各位可以在自己的数据库上试一下,表能够建立成功,这是为什么呢?其实MySQL在计算字段长度的时候并不是按照字段的全部长度来记的。列字段小于40个字节的都会按实际字节计算,如果大于20 * 2=40 字节就只会按40字节。

对应到MySQL代码中storage/innobase/dict/dict0dict.cc的dict_index_too_big_for_tree()中:


也就是说,如果字段长度超过BTR_EXTERN_FIELD_REF_SIZE * 2,字段就只算20 * 2=40(BTR_EXTERN_FIELD_REF_SIZE=20)

举例如下: ●  创建一个300个字段长度类型为varchar(30)的表,在创建时不会创建成功。因为varchar(30)没有超过20*2,那么总长度就是300*30=9000 > 8126就会创建失败。 ●  创建一个150个字段长度类型为varchar(100)的表可以创建成功。因为varchar(100) 大于了20*2那么就只会按40计算 总长度就是150*20*2=6000 < 8126 就会创建成功。这个20字节是不是看着有点眼熟,可以联系到InnoDB的一个参数:innodb_file_format。该参数用于设置Innodb表内部存储的文件格式,该参数可设置为Antelope,Barracuda两种格式。 ● Antelope是MySQL原始的记录格式,是较古老的记录格式。

在这种格式记录下Innodb 对于大字段的处理如下:


对于大字段,innodb只会存放前DICT_ANTELOPE_MAX_INDEX_COL_LEN(768)字节在数据页中,超过768字节都会放到溢出页中。这种方式也是B+TREE结构,但是也并不是完美的,因为我们将大字段存放到了数据页中会造成叶子节点的个数会很多,同样会造成非叶子节点的的个数增加。最终导致索引层级增高,访问IO次数增加。

● Barracuda格式是InnoDB新的存储格式

他的溢出存储方式如下:


在Barracuda格式下,会用20字节的指针指向溢出页,这样做的好处就是不会造成索引层级的增高。

回到错误3回归正题,第二个错误我们可以越过去,但是我们是不是能够真的插入150个100字符的字段列。用附2的插入语句试一下就知道,错误3也会报错出来。也就是说表可以创建成功但是插入却失败,原因如下: ● Antelope格式下的COMPACT大字段按照DICT_ANTELOPE_MAX_INDEX_COL_LEN(768)字节溢出页。varchar(100)没有存储为溢出页。 ● Barracuda的DYNAMIC和COMPRESSED格式下只有长字段才会用20字节溢出页的方式,varchar(100)也没有存储为溢出页。

引用reference的原文如下:

参考网址 ●   MySQL reference: Limits on Table Column Count and Row Size ●   MySQL reference: innodb row format dynamic ●   mysqlserverteam:Externally Stored Fields in InnoDB ●   MySQL · 引擎特性 · InnoDB 文件系统之文件物理结构附1.建表语句

附上测试的建表语句和insert语句,有兴趣的朋友可以自己按照上面的几种方式在Antelope和Barracuda的几种不同ROW_FORMAT格式上试试。

附2.insert语句

MySQL的一个表最多可以有多少个字段相关推荐

  1. mysql的表最多可设置多少字段?

    一.前言 最近在开发的时候,遇到一个报错:Can't create tablexxx(errno: 185 "Too many columns")根据英文,意思是表的字段太多了,那 ...

  2. mysql用一个表更新另一个表的方法

    Solution 1:  修改1列(navicate可行) update student s, city c set s.city_name = c.name where s.city_code = ...

  3. MySQL查询一个表的前25%的数据

    MySQL查询一个表的前25%的数据,SQL如下: # qianfeng SELECT g.* FROM (SELECT @rownum:=0) r join tb_shopping_car g wh ...

  4. mysql 查询两个字段相同的数据_sql语句如何查询一个表中某两个字段的相同数据?...

    查询一个表中某两个字段的相同数据代码是:Select Name,ID From A group by Name,ID having count (*)>1. 结构化查询语言(Structured ...

  5. mysql一张表最多多少索引_MySQL一个索引最多有多少个列?真实的测试例子

    MySQL一个索引最多有多少个列?真实的测试例子 更新时间:2009年07月01日 22:22:21   作者: MySQL一个索引最多有多少个列?下面是具体的实现代码. 最多16列. create ...

  6. mysql查一个表3到5行的数据类型_MySQL入门(三) 数据库表的查询操作【重要】

    序言 本节比较重要,对数据表数据进行查询操作,其中可能大家不熟悉的就对于INNER JOIN(内连接).LEFT JOIN(左连接).RIGHT JOIN(右连接)等一些复杂查询. 通过本节的学习,可 ...

  7. 查看MySQL数据库一个表所占的存储空间大小

    在mysql中有一个information_schema数据库,这个数据库中装的是mysql的元数据,包括数据库信息.数据库中表的信息等.所以要想查询数据库占用磁盘的空间大小可以通过对informat ...

  8. mysql中一个表最多能有几个auto_mysql--一个表上可以指定几个auto_increment

    auto_increment测试: 一个表是否可以有多个列使用auto_increment? 只有一个列使用auto_increment: mysql> create table tab_aut ...

  9. mysql中一个表怎么查询多以上的信息,MySQL怎么样实现多个表的或查询?

    我想要像上图那样在两个表里面查询username,只有其中一个表有这个数据就返回true,但是MySQL判断必须两个表都有才返回true. 请问有什么办法可以实现这样的查询呢?(两个表中任何一个表有数 ...

最新文章

  1. linux怎么运行cli,linux脚本 直接用cli模式运行脚本
  2. pycharm shadows name 'xxxx' from outer scope 警告
  3. where does ZCRM_OPPORTUNITY_0001_BE come from
  4. jQuery form插件使用详解
  5. asp.net学习之ado.net(无连接模式中的DataAdapter)
  6. matlab求FB色散模型,有效折射率法求矩形波导色散曲线(附Matlab程序)
  7. 使用idea打包war包部署
  8. 戴尔微型计算机7060,戴尔 Dell OptiPlex 7060 微型机 :1 升的迷你小钢炮
  9. 如何把pdf分割成多个?怎么把pdf文件分成多个?
  10. 戏人看戏,苏旭博客网-学无止尽
  11. MySql:学生表、教师表、课程表、分数表 练习
  12. 网络对战五子棋(来一起PK鸭)
  13. 云集上市,短短四年时间缔造了一个新的电商神话
  14. 自然辩证法问题思考范围(开卷可用)
  15. Apache安装配置(Linux)-有手就行
  16. Linux platform
  17. 机器学习中的特征空间
  18. 实现屏幕监控的大体思路
  19. python 外星人游戏下载大全_【Python】Python制作外星人入侵小游戏
  20. 开源免费的图片压缩软件,从50M到50K,极力安利

热门文章

  1. PPT2013 动画应该这样玩-秦川-专题视频课程
  2. java 信使服务_Android 绑定类型服务---使用信使(Messenger)
  3. layer.confirm 用法
  4. 人员梯度培养_(完整版)人才梯队培养计划执行方案
  5. 幻想神域服务器维护,《幻想神域》1月18日部分服务器数据互通公告
  6. 洛谷 肮脏的牧师 题解
  7. 高精度尘埃粒子计数器工厂空气质量监测必备
  8. Lerix的git学习笔记(3)_初始化配置(精华)
  9. c# DirectoryInfo类
  10. wpf wrapPanel居中并从左到右排列