mysql的varchar大字段性能研究

咱们知道,innodb的一个页是16K(16*1024=16384字节),若是一条记录占的字节数大于16K,意味着一个页没法装下一条记录,这种状况下mysql是如何处理的呢?mysql

  在回答这个问题以前咱们先来作一个实验:sql

  咱们建立了两张表,分别是T1和T2,T1和T2惟一的区别是在于b字段一个是varchar(8094),另外一个是varchar(8095),而后咱们有一个T1_test.sql文件,该文件包含有10W行“insert into T1 values(1,repeat('a',8094));”条sql语句,咱们导入到表里花费了4分24秒。工具

  接着咱们把T2_test.sql导入T2表中(T2.test.sql一样也包含了10W行相似的sql语句),花费了9分58秒!google

  咱们再来看这两个表文件的空间占用状况(本实例开启了innodb_file_per_talbe):code

  能够看到T1表占用804M,T2表占用空间1.9G,空间及时间差别均超过2倍。blog

  为何相差一个字符时间和空间相差会如此巨大呢?下面咱们一块儿剖析下。索引

  在innodb存储引擎里,将一条记录中的某些数据存储在真正的数据页面以外称之为行溢出数据,通常认为blob、text这类的大对像列的存储会把数据存放在数据页面以外。但从上述咱们的实验看出,除了blob、text这类大对像列之外,varchar类型彷佛也会采用行溢出的方法来存储数据。get

  Innodb存储引擎表是索引组织的,即B+树的结构,所以每一个页中至少应该有两个行记录(不然失去了B+树的意义,变成链表了),若是页中只能存放下一条记录,那么InnoDB存储引擎会自动将行数据存放到溢出页中,以使每一个页最少能存放两个行记录或以上。innodb

  下面借助姜承尧先生写的py_innodb_page_info工具来证明T2发生了行溢出(点击这里可取得该工具源代码table

  为了更清晰的分析缘由,下面咱们分别建立T1_1和T2_1表,其b字段也相差1字符,而后分别往这两个表插入一条记录,以下图所示:

  

  此时咱们经过py_innodb_page_info工具分析T1_1表的状况

上图说明表T1_1只包含了一个B-tree,咱们再看看T2_1的状况:

   咱们看到T2_1表比T1_1表多了一页“Uncompressed BLOB Page”的页,这也充分说明了插到T2_1个的记录分了两页存储,其中一页是BLOB页。

  在上述的实验中,咱们故意采起了两个字段,一个字段是4字节的int,另外一个字段是varchar(8094),二者加起来即8098,也就说,当一个表的字段总大小数大于8098时,插入一条记录即会分裂成两页存储,故不管时间仍是空间方面都大打折扣,有兴趣的读者可尝试下用一个或多个字段大小总和<=8098及>8098来作对比。

  你们在实验过程当中要注意一点的是,当varchar小于255时,会额外用1个字节来记录该记录的实际长度,当varchar大于255时会用2个字节,由于时使用多个变长字段实验时,要注意其总大小要加上该空间。例如:“create table A1(a int, b varchar(2000), c varchar(3000), d varchar(3090));”那么此时的临界值就是4+2000+3000+3090=8090,由于相对于咱们上述的实验,A1这个表多了两个大于255的varchar字段,所以须要额外的2+2=4字节来记录变长字段。