没有建立聚醋索引,mysql会自动建立一个
1、若没有主键,则会使用唯一索引 Unique key做主键
2、若没有 Unique key,则系统会生成一个内存的rowid做主键
聚簇索引的特点
- 聚簇索引又称为主键索引
- 非叶子节点只存储指针不存行
- 叶子节中只存储行的数据。
- 叶子节点之间链表相连,增加范围查询效率
下面展示了聚族索引中的记录是如何存放的。注意到,叶子页包含了行的全部数据,但是节点页只包含了索引列。
辅助索引
- 非叶子节点只存key
- 叶子节点存 key和 primary key
- 索引中不包含的字段被查到的话就会回到聚簇索引中查询(回表)
- primary key被查到不会回表因为已经是能直接找到
聚簇索引带来的页分裂问题
innodb,索引节点下存储了行数据。分裂的时候,还需要移动行数据。分裂的速度会比较慢。对于聚簇索引,节点分裂问题比较严重。而对于myisam,节点存储的对物理行地址,内容较小,又缓存在内存里,分裂速度快很多。
用过show status;得到Innodb_pages_written参数的值,这个就是页分裂次数。在随机插入时页分裂的次数要比顺序插入时页分裂次数要多,插入数据时效率上有明显的差异。
所以,对于innodb的聚簇索引需要设计 高性能主键策略进行优化:
1、尽量使用整型,而且是递增的整型做主键。若是无规律的数据,将会产生频繁的页分裂,节点分裂问题。影响速度
2、索引长度直接影响索引文件的大小,影响增删改的速度,并间接影响查询速度(占用内存多)、而且因为所有辅助索引都引用主索引,过长的主索引会令辅助索引变得过大。针对字符类型主键列(聚合列),可以使用前缀索引
。从左往右截取字符串部分来建立索引。
①、截的越短,区分度就越小,索引效果并不好
②、截的越长,重复读越低,区分度越高。索引效果越好,但是需要更多的空间存储索引文件。增删改变慢。
所以我们需要在 区分度+长度 两者行取得一个平衡。我们可以截取不同的长度,并测试其区分度。关于前缀索引可以看看这篇 https://www.jianshu.com/p/6d1ac6529697
为什么需要在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配置。