到底什么是索引:
全表扫描就是从第一行数据扫描到最后一行,话是这么简单理解起来则不然;为什么会从第一行扫描到最后一行?因为无论查询哪一个字段,都有可能会有你想要的查询的数据在表的末尾,所以只有从第一行扫描到最后一行才是最保险的,数据库中默认是这样的查询机制;
因为索引是有序的:
前面说了索引是一种特殊的包含了对数据表中所有记录引用指针的特殊文件,索引是有序的,而数据是无序的,索引中的指针指向了数据值相同的真实数据,因此在查询时不需要进行全表查询也可以避免数据纰漏;
举个例子,假如要查id = 5的studentname,在没有索引的时候为了避免漏掉学生名字,会从第一行读到最后一行;而如果给id和studentname都加上了索引那么引擎直接通过产生的索引表进行查找,索引表中指向id=5的索引值在一块,其中每一个索引值都对应一个id=5的地址,每一个id索引值都对应一个studentname索引值,每个studentname索引值又都指向相应studentname的地址,然后通过地址指针直接将对应的数据拿出来。通过这样的方式,即使数据本身是杂乱无章的还是可以进行有序的读取,从而避免了全表扫描,这种查询方式叫做 索引覆盖;
这里要提醒的是,在通过索引覆盖来读取数据时,所有的查询字段都需要创建索引,因为只要其中有一个字段没有创建索引那么其余索引都是无效的,因为即使四个字段当中有三个字段已经创建了索引,那也只能保证那三个字段的数据没有纰漏,为了没有创建索引的那个字段,引擎同样会进行全表扫描;
- 索引的应用场景:
通过索引的有序性,不光是查询,包括where、order by、join都能通过索引来进行性能优化;
- order by:
在没有创建索引的默认情况下,使用order by对某个字段进行排序的操作是将数据从硬盘分批读取到内存进行内部排序,知道全表读取完毕后再进行合并排序。而使用索引则不用进行排序,因为索引本身就是有序的,只需通过地址指针就能直接拿到有序的原始数据;
索引的几种类型:
主键索引:数据列不许重复,不许为null,全表唯一;
唯一索引:数据列不许重复,可以为null,同表可创多个;
ALTER TABLE table_name ADD UNIQUE (column);
普通索引:无限制;
ALTER TABLE table_name ADD INDEX index_name (column);
全文索引:目前搜索引擎的一种关键技术;
索引设计原则:
1、适合出现在where、join、order by所修饰的列中;
2、基数较小的表没必要建立索引,不划算;
3、适当创建索引,索引需要额外的磁盘空间,减少操作空间(因为会破坏树的结构);
索引创建原则:
1、最左前缀匹配原则:MySQL 将一直向右匹配直到遇到范围查询(即>、<、between、like)就停止匹配,后面字段的缩印将不会在被读取;
2、经常用作查询条件的创建索引,经常作修改的不创建;
3、字段值区分度不高不创建;
4、修改大于创建,如果说在已有的索引上通过修改能够满足功能,则不提倡创建新的索引;
5、外键字段一定要创建索引;
6、字段值类型为text、image、bit的列不创建索引;
B树与B+树的优劣:
在InnoDB中对索引的实现采取B+树的数据结构;
不了解B树或者B+树的可以去看看这篇博客:漫画叙述B+树和B-树,很值得看! 这是比较通俗易懂的一篇博客适合新手学习;
B+树的树枝节点全部用来充当索引,没有真实数据,因此能够容纳更多节点元素,首先这意味着更少次的IO;其次B+树的叶子节点通过链表的方式进行有序(从小到大)连接,当在某个叶子节点中没有找到数据时,直接通过链表在叶子节点之间进行遍历,无论是在单值查询还是范围查询中性能都很好很稳定;
B树的树枝节点中除了索引外还有真实数据,首先这意味着更多次的IO,其次,当单值查询的时候如果没有一次到位,由于节点之间也没有像B+树那样的连接,那么就需要进行回退,然后进入另外的树枝节点(中序遍历),这种情况在范围查询当中更容易发生,因此B数的性能具有不稳定的性质。但在重复访问同样数据的情况下,由于树枝节点中就有真实数据,所以会比B+树更高效;
- 为什么数据库用B+树而不是B树 ?
相比于B树来说,B+树空间利用率更高,减少IO次数,性能稳定且高效,并且解决了元素遍历效率低下的问题;
聚簇索引与非聚簇索引:
聚簇索引:将真实数据与索引一起放在叶子节点中。在InnoDB中默认只有主键为聚簇索引,没有主键索引则挑选一个唯一索引建立聚簇索引,没有唯一索引则隐式生成一个键来建立聚簇索引;
非聚簇索引:叶子节点中存放了索引,索引中的指针指向了数据的地址,通过key_buffer把索引加载到缓存区中,当需要使用索引时在内存中搜索索引;
这也是为什么通过主键查询会比通过其他索引查询要快的原因;
首先明确InnoDB的默认读取方式:
数据是存储在磁盘当中的,当读取数据的时候通过IO将磁盘中的数据加载至缓存中,而在磁盘中读取数据的时候是以磁盘块为单位进行读取的,也就是说不管该磁盘块中的数据是否有用都将被读取;而InnoDB引擎的读取是以数据为单位的,往往需要连续多个磁盘块的大小才能填满一个数据页,因此读取时会将连续的磁盘块中的内容加载至缓存区,通过多次IO操作直到全表扫描;
这样的方式首先全表扫描无法避免,查出很多无效数据,其次,当数据表的数据量大时往往需要很多次IO才能全表扫描完毕,这样又大大延长了读取时间;
而创建索引后将不在这样子来读取数据,而是使用B+树来进行数据读取:
这时候InnoDB将不会在直接对磁盘中的数据信息进行读取了,而是先读取创建索引所产生的根索引文件,该文件同样存储在磁盘当中,需要进行一次IO,但根索引文件中的数据仅仅只有指向其n个支节点的指针(指针所指向的最终内容是从小到大排序的),InnoDB读取跟索引文件后,通过这n个支节点的指针又找到n个子索引文件,这里又是一次IO,这n个子索引文件中又分别存放有指向另外n个支节点的指针,以此类推,当到达叶子节点的时候,叶子节点中存放了指向数据库中对应结果的指针以及指向相邻叶子节点中的指针,最终InnoDB根据指向数据库的指针取到需要的数据,而不需要的则不读取;如果需要进行范围读取时,又可以通过叶子节点间通过指向相邻叶子节点的指针所形成的链表来实现直接在叶子节点间进行顺序遍历,而不必像B树一样进行中序遍历;
虽然同样需要进行多次IO,但当数据量大时,相对于默认的查找方式来说,所需要经历的IO次数将呈几何式的减少,并且还能避免全表扫描和中序遍历,这就是B+树的优异之处;