MySQL索引是用于提高数据库查询性能的关键数据库对象。索引是一种数据结构,它存储了表中一个或多个列的值,以帮助数据库管理系统快速查找和访问表中的数据。索引的作用类似于书中的目录,它使数据库可以更快地定位和检索数据,而不必全表扫描。
一、mysql 索引分类
在数据库表中,对字段建立索引可以大大提高查询速度。通过善用这些索引,可以令 MySQL的查询和运行更加高效。索引是快速搜索的关键。MySQL索引的建立对于MySQL的高效运行是很重要的。 下面介绍几种常见的MySQL索引类型。
1、普通型索引
这是最基本的索引类型,而且它没有唯一性之类的限制。普通索引可以通过以下几种方式创建: (1)创建索引,例如CREATE INDEX 索引的名字 ON tablename (列名1,列名2,…); (2)修改表,例如ALTER TABLE tablename ADD INDEX 索引的名字 (列名1,列名2,…); (3)创建表的时候指定索引,例如CREATE TABLE tablename ( […], INDEX 索引的名字 (列名1,列名2,…));
2、唯一索引
这种索引和前面的“普通索引”基本相同,但有一个区别:索引列的所有值都只能出现一次,即必须唯一。唯一性索引可以用以下几种方式创建:
- 创建索引,例如CREATE UNIQUE INDEX 索引的名字 ON tablename (列的列表);
- 修改表,例如ALTER TABLE tablename ADD UNIQUE 索引的名字 (列的列表);
- 创建表的时候指定索引,例如CREATE TABLE tablename ( […], UNIQUE 索引的名字 (列的列表) );
3、主键
主键是一种唯一性索引,但它必须指定为“PRIMARY KEY”。如果你曾经用过AUTO_INCREMENT类型的列,你可能已经熟悉主键之类的概念了。
主键一般在创建表的时候指定,例如“CREATE TABLE tablename ( […], PRIMARY KEY (列的列表) ); ”。但是,我们也可以通过修改表的方式加入主键,例如“ALTER TABLE tablename ADD PRIMARY KEY (列的列表); ”。每个表只能有一个主键。(主键相当于聚合索引,是查找最快的索引)
4、单列索引和多列索引索引可以是单列索引,也可以是多列索引。
- 单列索引就是常用的一个列字段的索引,常见的索引。
- 多列索引就是含有多个列字段的索引alter table student add index sy(name,age,score); 索引sy就为多列索引. 多列索引在以下几种情况下才能有效: select * from student where name=‘jia’ and age>=‘12’ //where条件中含有索引的首列字段和第二个字段 select * from student where name=‘jia’ //where条件中只含有首列字段 select * from student where name=‘jia’ and score<60//where条件中含有首列字段和第三个字段总结:多列索引只有在where条件中含有索引中的首列字段时才有效5、选择索引列应该怎样选择索引列,首先要看查询条件,一般将查询条件中的列作为索引]\
二、聚集索引和非聚集索引区别?
聚集索引(Clustered Index): 唯一性:每张表只能有一个聚集索引,因此它是唯一的。 物理存储顺序:聚集索引决定了表中数据行的物理存储顺序。 表的数据行按照聚集索引的键值进行排序,这也是为什么它通常称为"聚集"索引。 叶子节点包含数据行:在聚集索引中,叶子节点包含了实际的数据行,而不仅仅是索引键值。这意味着聚集索引可以直接用于检索数据,从而加快查询速度。 主键:如果表有主键,通常主键会被用作聚集索引的键值。 非聚集索引(Non-Clustered Index):可以有多个:一张表可以有多个非聚集索引。物理存储顺序:非聚集索引不会改变表中数据行的物理存储顺序,数据行仍然按照插入的顺序存储。叶子节点不包含数据行:在非聚集索引中,叶子节点只包含索引键值和指向实际数据行的指针。这意味着在使用非聚集索引进行查询时,需要先查找索引,然后再通过指针找到实际数据行。
三、建立索引的常用技巧
1.最左前缀匹配原则,非常重要的原则,mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。=和in可以乱序,比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意顺序,mysql的查询优化器会帮你优化成索引可以识别的形式
2.尽量选择区分度高的列作为索引,区分度的公式是count(distinct col)/count(*),表示字段不重复的比例,比例越大我们扫描的记录数越少,唯一键的区分度是1,而一些状态、性别字段可能在大数据面前区分度就是0,那可能有人会问,这个比例有什么经验值吗?使用场景不同,这个值也很难确定,一般需要join的字段我们都要求是0.1以上,即平均1条扫描10条记录
3.索引列不能参与计算,保持列“干净”,比如from_unixtime(create_time) = ’2014-05-29’就不能使用到索引,原因很简单,b+树中存的都是数据表中的字段值,但进行检索时,需要把所有元素都应用函数才能比较,显然成本太大。所以语句应该写成create_time = unix_timestamp(’2014-05-29’);
4.尽量地扩展索引,不要新建索引。比如表中已经有a的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可,当然要考虑原有数据和线上使用情况
四、B+树和B树的区别
1.B树
维基百科对B树的定义为“在计算机科学中,B树(B-tree)是一种树状数据结构,它能够存储数据、对其进行排序并允许以O(log n)的时间复杂度运行进行查找、顺序读取、插入和删除的数据结构。B树,概括来说是一个节点可以拥有多于2个子节点的二叉查找树。与自平衡二叉查找树不同,B-树为系统最优化大块数据的读和写操作。B-tree算法减少定位记录时所经历的中间过程,从而加快存取速度。普遍运用在数据库和文件系统。”
B 树可以看作是对2-3查找树的一种扩展,即它允许每个节点有M-1个子节点。
- 根节点至少有两个子节点
- 每个节点有M-1个key,并且以升序排列
- 位于M-1和M key的子节点的值位于M-1 和M key对应的Value之间
- 其它节点至少有M/2个子节点
2.B+树
B+树是对B树的一种变形树,它与B树的差异在于:
- 有k个子结点的结点必然有k个关键码。
- 非叶结点仅具有索引作用,跟记录有关的信息均存放在叶结点中。
- 树的所有叶结点构成一个有序链表,可以按照关键码排序的次序遍历全部记录。
B+树的非叶子结点只包含导航信息,不包含实际的值,所有的叶子结点和相连的节点使用链表相连,便于区间查找和遍历。B+ 树的优点在于:
IO次数更少:由于B+树在内部节点上不包含数据信息,因此在内存页中能够存放更多的key。数据存放地更加紧密,具有更好的空间局部性。因此访问叶子节点上关联的数据也具有更好的缓存命中率。
遍历更加方便:B+树的叶子结点都是相链的,因此对整棵树的遍历只需要一次线性遍历叶子结点即可。而且由于数据顺序排列并且相连,所以便于区间查找和搜索。而B树则需要进行每一层的递归遍历。相邻的元素可能在内存中不相邻,所以缓存命中性没有B+树好。
五、为什么MySQL选择B+树做索引
-
B+树的磁盘读写代价更低:B+树的内部节点并没有指向关键字具体信息的指针,因此其内部节点相对B树更小,如果把所有同一内部节点的关键字存放在同一盘块中,那么盘块所能容纳的关键字数量也越多,一次性读入内存的需要查找的关键字也就越多,相对IO读写次数就降低了。 2.B+树的查询效率更加稳定:由于非终结点并不是最终指向文件内容的结点,而只是叶子结点中关键字的索引。所以任何关键字的查找必须走一条从根结点到叶子结点的路。所有关键字查询的路径长度相同,导致每一个数据的查询效率相当。
-
B+树更便于遍历:由于B+树的数据都存储在叶子结点中,分支结点均为索引,方便扫库,只需要扫一遍叶子结点即可,但是B树因为其分支结点同样存储着数据,我们要找到具体的数据,需要进行一次中序遍历按序来扫,所以B+树更加适合在区间查询的情况,所以通常B+树用于数据库索引。
-
B+树更适合基于范围的查询:B树在提高了IO性能的同时并没有解决元素遍历的我效率低下的问题,正是为了解决这个问题,B+树应用而生。B+树只需要去遍历叶子节点就可以实现整棵树的遍历。而且在数据库中基于范围的查询是非常频繁的,而B树不支持这样的操作或者说效率太低。
六、B+树的查找过程
如图所示,如果要查找数据项29,那么首先会把磁盘块1由磁盘加载到内存,此时发生一次IO,在内存中用二分查找确定29在17和35之间,锁定磁盘块1的P2指针,内存时间因为非常短(相比磁盘的IO)可以忽略不计,通过磁盘块1的P2指针的磁盘地址把磁盘块3由磁盘加载到内存,发生第二次IO,29在26和30之间,锁定磁盘块3的P2指针,通过指针加载磁盘块8到内存,发生第三次IO,同时内存中做二分查找找到29,结束查询,总计三次IO。真实的情况是,3层的b+树可以表示上百万的数据,如果上百万的数据查找只需要三次IO,性能提高将是巨大的,如果没有索引,每个数据项都要发生一次IO,那么总共需要百万次的IO,显然成本非常非常高。
七、索引什么情况失效
- 查询条件包含or,可能导致索引失效
- 如何字段类型是字符串,where时一定用引号括起来,否则索引失效
- like通配符可能导致索引失效。
- 联合索引,查询时的条件列不是联合索引中的第一个列,索引失效。
- 在索引列上使用mysql的内置函数,索引失效。
- 对索引列运算(如,+、-、*、/),索引失效。
- 索引字段上使用(!= 或者 < >,not in)时,可能会导致索引失效。
- 索引字段上使用is null, is not null,可能导致索引失效。
- 左连接查询或者右连接查询查询关联的字段编码格式不一样,可能导致索引失效。
- mysql估计使用全表扫描要比使用索引快,则不使用索引