关于InnoDB的索引大小

背景

         关于InnoDB内的索引大小。对于表

CREATE TABLE `testtb` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `a` int(11) DEFAULT NULL,
  `b` int(11) DEFAULT NULL,
  `c` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `i1` (`a`),
  KEY `i2` (`a`,`id`),
  KEY `i3` (`id`,`a`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

 

 

由于InnoDB在存储索引的时候会自动取出重复的主键,源码分析见这里

先说几个结论

1)       Index i2 由于索引定义中已经包含pk  id,因此不会存两份,实际就是(a, id)

2)       Index i1 本身要包含主键id,因此也是(a, id),与 i2 相同

3)       同理1,Index i3 里存的也只是(id, a)

 

异常

         按照上面的结论,以下操作能发现“异常”

 

 

    

CREATE TABLE `t1` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `a` int(11) DEFAULT NULL,
  `b` int(11) DEFAULT NULL,
  `c` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `i2` (`a`,`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `testtb` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `a` int(11) DEFAULT NULL,
  `b` int(11) DEFAULT NULL,
  `c` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `i3` (`id`,`a`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

 

          接下来往这个两个表中各插入5w行记录,插入语句类似

 

 Foreach I in 1 to 5w
     {
       Set @v = rand() * 100;
       Insert into testtb(a,b,c) values(@v, @v, @v);
     }

 

         插入数据完成后,这个两个表索引大小是否相同?

 

         从show table status like ‘%t1%’; 和 show table status like ‘%t2%’;的index_length可以看出来,是不同的,t1大一些。

         存储的行数相同,数据也相同,为什么大小不一样?

 

原因说明

         主要在于插入顺序导致的分裂。在上面的例子中,由于a,b,c的值都是随机值,导致索引 (a, id)是随机的。 Id是递增的,所以(id, a)是递增的。

         随机插入索引和顺序递增插入索引之所以有差别,就在于随机插入会导致更多的Btree分裂。

 

这也就是为什么在某些场景下,我们建议在表比较大的应用中,用自增id替代unique key (并非唯一的原因,也不是固定的规范,需要具体分析)。

 

验证

有了上面的分析,要验证就比较简单了,把两个表清空(truncate),

插入数据改为如下的语句

 

Foreach I in 1 to 5w
     {
       Set @v = i;
       Insert into testtb(a,b,c) values(@v, @v, @v);
     }

 

 

这样插入的每行都是(n,n,n,n), n为1到5w的递增。这样索引(a, id)也是顺序递增方式,与(id,a)一样紧凑,再看show table status能发现一样了。

 

有个工具

         分两个表验证比较麻烦。这个是之前写过的一个分析文件利用率的工具 ibd_used,可以看一个表上各个索引的大小和索引上page的利用率。

         用法

./ibd_used testtb.ibd 0 N > k

 

说明:第一个参数是要分析的ibd文件

              第二、三个参数是起始、结束page_no。  如果你要分析整个文件,N可以输入一个很大的数就行。

               结果中会输出每个page的利用率,因此比较多,记得重定向输出

 

         最后几行是整个索引的统计结果。

 

 

用文章开头的例子和随机值插入的case,得到的表中,执行

 

ibd_used data/test/testtb.ibd 0 99999999 > k
最后几行如下
index_id:1517 rate 1751652/1949696=0.898423
index_id:1518 rate 701314/1212416=0.578443
index_id:1519 rate 701314/1212416=0.578443
index_id:1520 rate 700828/770048=0.910109

 

 

 

 按顺序对应索引,可以看出,i1和i2的利用率一模一样,但跟i3 比起来就差多了。

 

grep -Po " index_id:\d+" k | sort | uniq -c
    120 index_id:1517
     75 index_id:1518
     75 index_id:1519
     48 index_id:1520

 

 

    这个命令看每个索引占用多少page,与我们的结论一致:i3因为紧凑,所以占用更少的page。

时间: 2024-08-28 20:05:29

关于InnoDB的索引大小的相关文章

显示超出索引大小?

问题描述 for(inti=0;i<listView1.Columns.Count;i++){listView1.Columns[i].Width=listView1.Width/listView1.Columns.Count;}cn.Open();SqlCommandcmd=newSqlCommand("SELECT海贼名fromdwhere海贼名='"+textBox1.Text.Trim()+"'",cn);SqlDataReaderread1=cmd.

前缀索引,一种优化索引大小的解决方案

今天在读一篇关于数据库索引介绍的文章时,该文章提到了前缀索引,对于我这个搞数据库应用开发那么多年的人来说,这个词还真是一个新词,没用过.于是打算研究一番. 前缀索引似乎是MySQL中的一个概念,在SQL Server和Oracle中没提出这个概念.于是就安装了一个MySQL来做实验,搞清楚前缀索引. 前缀索引说白了就是对文本的前几个字符(具体是几个字符在建立索引时指定)建立索引,这样建立起来的索引更小,所以查询更快.有点相当于Oracle中对字段使用Left函数,建立函数索引,只不过MySQL的

[MySQL5.7] Innodb的索引锁优化

背景: . 这是一个臭名昭彰的问题,Innodb的btree发生合并/分裂等可能修改B-tree的操作时,都需要对其加排他的索引锁,这时候是无法对该索引进行读写操作的,极大的影响了性能:关于index lock,可以看看大神Domas的这篇博文:"Innodb locking makes me sad"  以及Vadim的这篇博客  . 总而言之,MySQL5.7.2的这个功能点的改进是万众期待的!  . 以下是阅读Rev6232的笔记,大概理了下关于索引锁优化的几个点.有些只是自己的

【oracle 】如何估算即将创建的索引大小

SQL> startup ORACLE 例程已经启动. Total System Global Area  426852352 bytes                                       Fixed Size                  1333648 bytes                                       Variable Size             260048496 bytes                     

MYSQL INNODB 组合索引分支节点数据解析

1.本文证明组合索引的所有键值在分支节点(非叶子结点也进行了存储). 2.本文给出B+ 索引如何进行验证其B+树结构 关于B树结构(不是B+树)可以参考: http://blog.itpub.net/7728585/viewspace-2126929/ 脚本: mysql> create table testzh(id int  primary key auto_increment ,id2 int,id3 int,name varchar(20), key(id2,id3)); Query O

InnoDB引擎的索引和存储结构

在Oracle 和SQL Server等数据库中只有一种存储引擎,所有数据存储管理机制都是一样的. 而MySql数据库提供了多种存储引擎.用户可以根据不同的需求为数据表选择不同的存储引擎,用户也可以根据自己的需要编写自己的存储引擎. 1.MySQL主要存储引擎的区别 MySQL默认的存储引擎是MyISAM,其他常用的就是InnoDB,另外还有MERGE.MEMORY(HEAP)等. (1)主要的几个存储引擎 MyISAM管理非事务表,提供高速存储和检索,以及全文搜索能力. MyISAM是Mysq

建立索引的原则 - 以innodb为例

一.写在前面         随着开发.测试任务进入尾声,大家都在整理一些项目发布前的一些准备工作,其中一个重要的工作就是为之前写的一些sql语句建立索引,这高并发.高访问量的环境下是非常有必要的,建立一个好的索引能够极大地提高sql语句的查询效率,那么问题来了,到底什么是索引,怎样才能建立一个好的索引呢?本文以mysql Innodb存储引擎为例,结合实际的项目来看一下,如何建立一个好的而索引. 二.索引定义         MySQL官方对索引的定义为:索引(Index)是帮助MySQL高效

[MySQL 源码] innodb如何创建二级索引

以下为分析问题时的随笔.写的很凌乱,仅做记录,以备后用...... ////////////////////////////////////////////////////////////// ha_innobase::add_index是innodb创建索引的接口函数. 以下所有的讨论都是基于创建一个非聚集的二级索引.因此一些过程是被省略掉了. 1.获取数据词典信息           indexed_table = dict_table_get(prebuilt->table->name,

探究MySQL中索引和提交频率对InnoDB表写入速度的影响_Mysql

本次,我们来看看索引.提交频率对InnoDB表写入速度的影响,了解有哪些需要注意的. 先直接说几个结论吧: 1.关于索引对写入速度的影响: a.如果有自增列做主键,相对完全没索引的情况,写入速度约提升 3.11%: b.如果有自增列做主键,并且二级索引,相对完全没索引的情况,写入速度约降低 27.37%: 因此,InnoDB表最好总是有一个自增列做主键. 2.关于提交频率对写入速度的影响(以表中只有自增列做主键的场景,一次写入数据30万行数据为例): a.等待全部数据写入完成后,最后再执行com