1、聚族索引的构造缓存
聚簇索引并非一种单独的索引类型,而是一种数据存储方式。具体的细节依赖于其实现方式,但InnoDB的聚族索引实际上在同一个结构中保存了B-Tree索引和数据行。当表有聚族索引时,它的数据行存放在索引的叶子页中。术语“聚族”表示数据行和相邻的键值紧凑的存储在一块儿。由于没法同时把数据行放在两个不一样的地方,因此一个表只能有一个聚族索引。并发
由于是存储引擎负责实现索引,所以不是全部的存储引擎都支持聚族索引。这里咱们主要关注InnoDB,可是这里讨论的原理对于任何支持聚族索引的存储引擎都是适用的。高并发
下面展现了聚族索引中的记录是如何存放的。注意到,叶子页包含了行的所有数据,可是节点页只包含了索引列。性能
在InnoDB中经过主键汇集数据,这也就是说上图中“被索引的列”就是主键列。若是没有定义主键,InnoDB会选择一个惟一的非空索引代替。若是没有这样的索引,InnoDB会隐式定义一个主键来做为聚族索引。InnoDB只汇集在同一个页面中的记录。包含相邻键的页面可能会相距甚远。测试
聚族主键可能对性能有帮助,但也可能致使严重的性能问题。因此须要仔细的考虑聚族索引,尤为是将表的引擎从InnoDB改为其余引擎的时候。优化
2、聚族索引的优势设计
- 能够把相关数据保存在一块儿。例如实现电子邮件时,能够根据用户ID来汇集数据,这样只须要从磁盘读取少数的数据页就能获取某个用户的所有邮件。若是没有使用聚族索引,则每封邮件均可能致使一次磁盘I/O;
- 数据访问更快。聚族索引将索引和数据保存在同一个B-Tree中,所以从聚族索引中获取数据一般比在非聚族索引中查找更快。
- 使用覆盖索引扫描的查询能够直接使用节点中的主键值。
3、聚族索引的缺点代理
- 聚簇数据最大限度的提升了I/O密集型应用的性能,但若是数据所有都放在内存中,则访问的顺序就没有那么重要了,聚簇索引也就没有那么优点了;
- 插入速度严重依赖于插入顺序。按照主键的顺序插入是加载数据到InnoDB表中速度最快的方式。但若是不是按照主键顺序加载数据,那么在加载完成后最好使用OPTIMIZE TABLE命令从新组织一下表。
- 更新聚簇索引列的代价很高,由于会强制InnoDB将每一个被更新的行移动到新的位置。
- 基于聚簇索引的表在插入新行,或者主键被更新致使须要移动行的时候,可能面临“页分裂”的问题。当行的主键值要求必须将这一行插入到某个已满的页中时,存储引擎会将该页分裂成两个页面来容纳该行,这就是一次分裂操做。页分裂会致使表占用更多的磁盘空间。
- 聚簇索引可能致使全表扫描变慢,尤为是行比较稀疏,或者因为页分裂致使数据存储不连续的时候。
- 二级索引(非聚簇索引)可能比想象的要更大,由于在二级索引的叶子节点包含了引用行的主键列。
- 二级索引访问须要两次索引查找,而不是一次。
备注:有关二级索引须要两次索引查找的问题?答案在于二级索引中保存的“行指针”的实质。要记住,二级索引叶子节点保存的不是指向行的物理位置的指针,而是行的主键值。这意味着经过二级索引查找行,存储引擎须要找到二级索引的叶子节点得到对应的主键值,而后根据这个值去聚簇索引中查找到对应的行。这里作了重复的工做:两次B-Tree查找而不是一次。对于InnoDB,自适应哈希索引可以减小这样的重复工做。指针
4、InnoDB和MyISAM的数据分布对比blog
聚簇索引和非聚簇索引的数据分布有区别,以及对应的主键索引和二级索引的数据分布也有区别。
一、MyISAM的主键索引和二级索引
MyISAM的数据分布很是简单,MyISAM按照数据插入的顺序存储在磁盘上。在行的旁边显示了行号,从0开始递增。由于行是定长的,因此MyISAM能够从表的开头跳过所需的字节找到须要的行。这种分布方式很容易建立索引。而且,MyISAM中主键索引和其余索引在结构上没有什么不一样。主键索引就是一个名为primary的惟一非空索引。以下图:
一、MyISAM数据行分布
二、MyISAM的主键分布
三、MyISAM上的其余索引分布
二、InnoDB的主键索引和二级索引
InnoDB的数据分布,由于InnoDB支持聚簇索引,索引使用很是不一样的方式存储这样的数据,以下图:
仔细查看,会注意到该图显示了整个表,而不是只有索引。由于在InnoDB中,聚簇索引“就是”表,因此不像MyISAM那样须要独立的行存储。聚簇索引的每一个叶子节点都包含了主键值、事务ID、用于事务和MVCC的回滚指针以及全部的剩余列。若是主键是一个列前缀索引,InnoDB也会包含完整的主键列和剩下的其余列。
还有一点和MyISAM的不一样是,InnoDB的二级索引和聚簇索引很不相同。InnoDB二级索引的叶子节点中存储的不是“行指针”,而是主键值,并以此做为指向行的“指针”。这样的策略减小了当出现航移动或者数据页分裂时二级索引的维护工做。使用主键值看成指针会让二级索引占用更多的空间,换来的好处是,InnoDB在移动行时无需更新二级索引中的这个“指针”。下图就是InnoDB的二级索引:
三、MyISAM和InnoDB的对比
5、在InnoDB表中按主键顺序插入行
若是正在使用InnoDB表而且没有什么数据须要汇集,那么能够定义一个代理键做为主键,这种主键的数据应该和应用无关,最简单的方法是使用auto_increment自增列。这样能够保证数据行是按照顺序写入,对于根据主键作关联操做的性能也会更好。
最好避免随机的聚簇索引,特别对于I/O密集型的应用。例如,从性能的角度考虑,使用UUID做为聚簇索引会很糟糕:它使得聚簇索引的插入变得彻底随机,这是最坏的状况,使得数据没有任何汇集特性。经过测试,向UUID主键插入行不只花费的时间更长,并且索引占用的空间也更大。这一方面是因为主键字段更长,另外一方面毫无疑问是因为页分裂和碎片致使的。
这是因为当主键的值是顺序的,则InnoDB把每一条记录都存储在上一条记录的后面。当达到页的最大填充因子时(InnoDB默认的最大填充因子是页大小的15/16,留出的部分空间用于之后修改),下一条记录就会写入新的页中。一旦数据按照这样顺序的方式加载,主键页就会近似于被顺序的记录填满,这也是所指望的结果。
而当采用UUID的聚簇索引的表插入数据,由于新行的主键值不必定比以前的插入值大,因此InnoDB没法简单的老是把新行插入到索引的最后,而是须要为新的行寻找合适的位置----一般是已有数据的中间位置----而且分配空间。这会增长不少额外的工做,并致使数据分布不够优化。下面是总结的一些缺点:
- 写入目标页可能已经刷到磁盘上并从缓存中移除,或者是尚未被加载到缓存中,InnoDB在插入以前不得不先找到并从磁盘读取目标页到内存中,这将致使大量的随机I/O;
- 由于写入是乱序的,InnoDB不得不频繁的作页分裂操做,以便为新的行分配空间。页分裂会致使移动大量数据,一次插入最少须要修改三个页而不是一个页。
- 因为频繁的页分裂,页会变得稀疏并被不规则的填充,因此最终数据会有碎片。
- 把这些随机值载入到聚簇索引之后,须要作一次optimize table来重建表并优化页的填充。
注意:顺序主键也有缺点:对于高并发工做负载,在InnoDB中按主键顺序插入可能会形成明显的争用。主键的上界会成为“热点”。由于全部的插入都发生在这里,因此并发插入可能致使间隙锁竞争。另外一个热点多是auto_increment锁机制;若是遇到这个问题,则可能须要考虑从新设计表或者应用,或者更改innodb_autonc_lock_mode配置。