文章目录
概述
MySQL索引是一种数据结构,用于提高查询效率和加快数据的检索速度。通过在指定的列上创建索引,MySQL可以更快地找到满足查询条件的数据,而不需要全表扫描。它可以加快数据库查询的速度,从而提高应用程序的性能。在MySQL中,索引用于快速查找、排序和过滤数据。索引类似于书本的目录,是存储引擎用于提高数据库表的访问速度的一种数据结构。在mysql内部,将热点数据,以B+树的形式将所有page页,进行组织成一定的数据结构,再用其配套的查找算法进行查找,叫做索引查询。
MySQL索引的建立对于MySQL的高效运行是很重要的,索引可以大大提高MySQL的检索速度。
打个比方,如果合理的设计且使用索引的MySQL是一辆兰博基尼的话,那么没有设计和使用索引的MySQL就是一个人力三轮车。
索引分单列索引和组合索引。单列索引,即一个索引只包含单个列,一个表可以有多个单列索引,但这不是组合索引。组合索引,即一个索引包含多个列。
创建索引时,你需要确保该索引是应用在 SQL 查询语句的条件(一般作为 WHERE 子句的条件)
实际上,索引也是一张表,该表保存了主键与索引字段,并指向实体表的记录。
上面都在说使用索引的好处,但过多的使用索引将会造成滥用。因此索引也会有它的缺点:虽然索引大大提高了查询速度,同时却会降低更新表的速度,如对表进行INSERT、UPDATE和DELETE。因为更新表时,MySQL不仅要保存数据,还要保存一下索引文件。建立索引会占用磁盘空间的索引文件。
一个表最多可有16个索引。最大索引长度是256个字节,尽管这可以在编译MySQL时被改变。
索引可以提高数据检索的效率,降低数据库的IO成本。
MySQL在300万条记录左右性能开始逐渐下降,虽然官方文档说500~800w记录,所以大数据量建立索引是非常有必要的。
MySQL提供了Explain,用于显示SQL执行的详细信息,可以进行索引的优化。
索引是一种可以让SELECT语句提高效率的数据结构,可以起到快速定位的作用。
索引对查询速度的影响
mysql中提高性能的最有效的方式就是对数据表设计合理的索引。索引提供了高效访问数据的方法,并且加快了查询的速度。因此,索引对查询的速度有着至关重要的影响。使用所以可以快速的定位到表中的某条记录,从而提高数据库查询的速度,提高数据库的性能。如果查询的时候没有使用索引,查询语句将扫描表中的所有记录。在数据量大的情况下,这样查询的速度会很慢。如果使用索引进行查询,查询语句可以根据索引快速定位到待查询的记录,从而减少查询的记录数,达到提高查询速度的目的。
使用索引查询,几种特殊情况
索引可以提高查询的速度,但并不是使用带有索引的字段查询时,索引都会起到作用。下面是几种比较特殊的情况。在这些情况下,有可能使用带有索引的字段查询时,索引并没有起到作用。
1.使用like关键字的查询语句。在使用like关键字进行查询的查询语句中,如果匹配字符串的第一个字符为"%“,索引不会起作用。只有”%"不在第一个位置,索引才会起到作用。
使用模糊查询,”%“在前面的结果,显示索引没有起了作用了的。
使用模糊查询,”%“在后面的结果,显示索引起了作用了的。
左模糊查询是指在查询条件的开头使用通配符 % 进行模糊匹配,例如 column_name LIKE ‘%keyword’。在这种情况下,索引可能仍然会起作用,但它取决于具体的数据库管理系统(DBMS)以及索引的类型。
对于某些 DBMS,例如 MySQL,在某些情况下,对于左模糊查询,如果列上存在索引,数据库可能会优化查询以利用这个索引。这是因为数据库可以倒序遍历索引树,从索引的末尾开始匹配,直到找到第一个匹配项,然后停止搜索。
但是,并不是所有的数据库管理系统都支持这种优化。在某些情况下,即使对于左模糊查询,索引仍然可能失效,这取决于具体的DBMS和索引类型。
因此,尽管在左模糊查询中索引可能会起作用,但也不能保证在所有情况下都会如此。在设计数据库时,应该根据具体的查询需求和性能要求来考虑是否创建索引以及索引的类型。
右模糊查询是指在查询条件的结尾处使用通配符 %
进行模糊匹配,例如 column_name LIKE 'keyword%'
。在右模糊查询中,索引通常不会起作用,这是因为通配符 %
在查询条件的结尾处,这种情况下索引的快速定位功能无法发挥作用。
数据库通常会按照索引的顺序来存储数据,但是对于右模糊查询,由于通配符 %
出现在查询条件的结尾,数据库无法确定索引树的哪个部分是匹配的范围。因此,数据库通常会选择放弃使用索引,而是进行全表扫描来执行这样的模糊查询。
虽然在某些数据库管理系统中可能会对右模糊查询进行一些优化,但大多数情况下,右模糊查询都会导致索引失效,从而影响查询性能。因此,在设计数据库时,应该根据查询需求和性能要求来考虑是否创建索引以及索引的类型,以避免右模糊查询带来的性能问题。
2、使用多列索引的查询语句。mysql可以为多个字段创建索引。一个索引可以包括16个字段。对于多列索引,只有查询条件中使用了这些字段中第1个字段的时候,索引才会被使用。
3、使用or关键字的查询语句。使用语句的查询条件中只有or关键字,且or前后的两个条件中的列都有索引时,查询中才使用索引。否则,查询将不适用索引。
索引的优缺点
优点:某些情况下使用select语句大幅度提高效率,合适的索引可以优化MySQL服务器的查询性能,从而起到优化MySQL的作用。
缺点:表行数据的变化(index、update、delect),简历在表列上的索引也会自动维护,一定程度上会使DML操作变慢。索引还会占用磁盘额外的存储空间。
优点
加速查询速度
辅助使用排序和分组操作
提高连接操作的效率
缺点
索引需要占用额外的磁盘空间。
索引会减慢插入、更新和删除操作的速度,因为MySQL需要维护索引结构。
对于大型表,全表扫描可能仍然比使用不合适的索引更快
索引类型
Mysql目前主要有以下几种索引类型:FULLTEXT,HASH,BTREE,RTREE。
B-Tree索引:这是最常见的索引类型,用于大多数数据类型,如CHAR、VARCHAR、INT等。
HASH索引:主要用于MEMORY存储引擎。
FULLTEXT索引:用于全文搜索。
空间索引 (R-Tree):用于空间数据类型,如GEOMETRY。
- FULLTEXT全文索引
全文索引适用于对文本内容进行搜索和匹配。MySQL提供了全文索引功能,可以进行全文搜索和模糊匹配。
即为全文索引,目前只有MyISAM引擎支持。其可以在CREATE TABLE ,ALTER TABLE ,CREATE INDEX 使用,不过目前只有 CHAR、VARCHAR ,TEXT 列上可以创建全文索引。
全文索引并不是和MyISAM一起诞生的,它的出现是为了解决WHERE name LIKE “%word%"这类针对文本的模糊查询效率较低的问题。 - HASH
哈希索引适用于等值查询,但不支持范围查询。它将索引列的值通过哈希算法转换为哈希码,然后在内存中进行快速的哈希查找。
由于HASH的唯一(几乎100%的唯一)及类似键值对的形式,很适合作为索引。
HASH索引可以一次定位,不需要像树形索引那样逐层查找,因此具有极高的效率。但是,这种高效是有条件的,即只在“=”和“in”条件下高效,对于范围查询、排序及组合索引仍然效率不高。 - BTREE
B-Tree索引是最常见和默认的索引类型,在大多数情况下都能满足需求。它适用于精确匹配和范围查询。
BTREE索引就是一种将索引值按一定的算法,存入一个树形的数据结构中(二叉树),每次查询都是从树的入口root开始,依次遍历node,获取leaf。这是MySQL里默认和最常用的索引类型。 - RTREE
RTREE在MySQL很少使用,仅支持geometry数据类型,支持该类型的存储引擎只有MyISAM、BDb、InnoDb、NDb、Archive几种。
相对于BTREE,RTREE的优势在于范围查找。
普通索引:仅加速查询
唯一索引:加速查询 + 列值唯一(可以有null)
主键索引:加速查询 + 列值唯一(不可以有null)+ 表中只有一个
组合索引:多列值组成一个索引,专门用于组合搜索,其效率大于索引合并
全文索引:对文本的内容进行分词,进行搜索
普通索引:MySQL中基本索引类型,没有什么限制,允许在定义索引的列中插入重复值和空值,纯粹为了 查询数据更快一点。
唯一索引:索引列中的值必须是唯一的,但是允许为空值,
主键索引:是一种特殊的唯一索引,不允许有空值。
组合索引:在表中的多个字段组合上创建的索引,只有在查询条件中使用了这些字段的左边字段时,索引才会被使用,使用组合索引时遵循最左前缀集合。这个如果还不明白,等后面举例讲解时在细说
全文索引:只有在MyISAM引擎上才能使用,只能在CHAR,VARCHAR,TEXT类型字段上使用全文索引,介绍了要求,
空间索引:空间索引是对空间数据类型的字段建立的索引,MySQL中的空间数据类型有四种,GEOMETRY、POINT、LINESTRING、POLYGON。
在创建空间索引时,使用SPATIAL关键字。要求,引擎为MyISAM,创建空间索引的列,必须将其声明为NOT NULL
一级索引和二级索引的区别
一级索引索引和数据存储在一起,都存储在同一个B+tree中的叶子节点。一般主键索引都是一级索引。
二级索引树的叶子节点存储的是主键而不是数据。也就是说,在找到索引后,得到对应的主键,再回到一级索引中找主键对应的数据记录
聚集索引和非聚集索引(一级索引和二级索引)
简单来说,聚集索引就是基于主键创建的索引,一级索引
除了主键索引以外的其他索引,称为非聚集索引,也叫做二级索引。
由于在 InnoDB 引擎里面,一张表的数据对应的物理文件本身就是按照 B+树来组织的一种索引结构,而聚集索引就是按照每张表的主键来构建一颗 B+树,然后叶子节点里面存储了这个表的每一行数据记录。
所以基于 InnoDB 这样的特性,聚集索引并不仅仅是一种索引类型,还代表着一种数据的存储方式。
同时也意味着每个表里面必须要有一个主键,如果没有主键,InnoDB 会默认选择或者添加一个隐藏列作为主键索引来存储这个表的数据行。一般情况是建议使用自增 id 作为主键,这样的话 id 本身具有连续性使得对应的数据也会按照顺序存储在磁盘上,写入性能和检索性能都很高。否则,如果使用 uuid 这种随机 id,那么在频繁插入数据的时候,就会导致随机磁盘 IO,从而导致性能较低。
需要注意的是,InnoDB 里面只能存在一个聚集索引,原因很简单,如果存在多个聚集索引,那么意味着这个表里面的数据存在多个副本,造成磁盘空间的浪费,以及数据维护的困难。由于在 InnoDB 里面,主键索引表示的是一种数据存储结构,所以如果是基于非聚集索引来查询一条完整的记录,最终还是需要访问主键索引来检索。
二级索引又称为辅助索引,是因为二级索引的叶子节点存储的数据是主键。也就是说,通过二级索引,可以定位主键的位置。
常用的二级索引包括:
1唯一索引(Unique Key) :唯一索引也是一种约束。唯一索引的属性列不能出现重复的数据,但是允许数据为 NULL,一张表允许创建多个唯一索引。 建立唯一索引的目的大部分时候都是为了该属性列的数据的唯一性,而不是为了查询效率。
2普通索引(Index) :普通索引的唯一作用就是为了快速查询数据,一张表允许创建多个普通索引,并允许数据重复和 NULL。
3前缀索引(Prefix) :前缀索引只适用于字符串类型的数据。前缀索引是对文本的前几个字符创建索引,相比普通索引建立的数据更小, 因为只取前几个字符。
MySQL 回表
使用 drinker_id 这个索引来查询 drinker_id = 3 的记录时就会涉及到回表。
SELECT * FROM t_back_to_table WHERE drinker_id = 3;
因为通过 drinker_id 这个普通索引查询方式,则需要先搜索 drinker_id 索引树(该索引树上记录着主键ID的值),然后得到主键 ID 的值为 3,再到 ID 索引树搜索一次。这个过程虽然用了索引,但实际上底层进行了两次索引查询,这个过程就称为回表。
回表小结
● 对比发现,基于非主键索引的查询需要多扫描一棵索引树,先定位主键值,再定位行记录,它的性能较扫一遍索引树更低。
● 在应用中应该尽量使用主键查询,这里表中就四条数据,如果数据量大的话,就可以明显的看出使用主键查询效率更高。
● 使用聚集索引(主键或第一个唯一索引)就不会回表,普通索引就会回表。
联合索引(最左前缀原则
建立联合索引a b、b a 这两种方式有区别吗。
利用索引中的附加列,您可以缩小搜索的范围,但使用一个具有两列的索引 不同于使用两个单独的索引。复合索引的结构与电话簿类似,人名由姓和名构成,电话簿首先按姓氏对进行排序,然后按名字对有相同姓氏的人进行排序。如果您知道姓,电话簿将非常有用;如果您知道姓和名,电话簿则更为有用,但如果您只知道名不姓,电话簿将没有用处。
所以说创建复合索引时,应该仔细考虑列的顺序。对索引中的所有列执行搜索或仅对前几列执行搜索时,复合索引非常有用;仅对后面的任意列执行搜索时,复合索引则没有用处。
重点:
多个单列索引在多条件查询时只会生效第一个索引!所以多条件联合查询时最好建联合索引!
ALTER TABLE user_innodb add INDEX comidx_name_phone(name,phone); – 创建联合索引
最左前缀原则:带头大哥不能死,中间兄弟不能断
顾名思义是最左优先,以最左边的为起点任何连续的索引都能匹配上,
注:如果第一个字段是范围查询需要单独建一个索引
注:在创建联合索引时,要根据业务需求,where子句中使用最频繁的一列放在最左边。这样的话扩展性较好,比如 userid 经常需要作为查询条件,而 mobile 不常常用,则需要把 userid 放在联合索引的第一位置,即最左边
同时存在联合索引和单列索引(字段有重复的),这个时候查询mysql会怎么用索引呢?
这个涉及到mysql本身的查询优化器策略了,当一个表有多条索引可走时, Mysql 根据查询语句的成本来选择走哪条索引;
有人说where查询是按照从左到右的顺序,所以筛选力度大的条件尽量放前面。网上百度过,很多都是这种说法,但是据我研究,mysql执行优化器会对其进行优化,当不考虑索引时,where条件顺序对效率没有影响,真正有影响的是是否用到了索引!如果使用联合索引,那么where条件也要尽量根据联合索引的顺序来,如果不按照顺序来,索引也同样会用到,但是在执行前,SQL优化器也会将条件调整为联合索引的顺序,既然可以直接避免这种情况,就没必要再让SQL优化器去处理,毕竟处理也是有开销的。
联合索引本质:
当创建(a,b,c)联合索引时,相当于创建了(a)单列索引,(a,b)联合索引以及(a,b,c)联合索引
想要索引生效的话,只能使用 a和a,b和a,b,c三种组合;当然,我们上面测试过,a,c组合也可以,但实际上只用到了a的索引,c并没有用到!
注:这个可以结合上边的 通俗理解 来思考!
其他知识点:
1、需要加索引的字段,要在where条件中
2、数据量少的字段不需要加索引;因为建索引有一定开销,如果数据量小则没必要建索引(速度反而慢)
3、如果where条件中是OR关系,联合索引不起作用
4、联合索引比对每个列分别建索引更有优势,因为索引建立得越多就越占磁盘空间,在更新数据的时候速度会更慢。另外建立多列索引时,顺序也是需要注意的,应该将严格的索引放在前面,这样筛选的力度会更大,效率更高
在平时开发中,最常见的是单列索引(比如主键primary id),但是在需要多条件查询的时候,也会创建联合索引。单列索引也可以看成特殊的联合索引。比如在user表中,给name和phone建立一个联合索引。
主键索引和唯一索引的区别
主键一定是唯一性索引,唯一性索引并不一定就是主键。
所谓主键就是能够唯一标识表中某一行的属性或属性组,一个表只能有一个主键,但可以有多个候选索引。因为主键可以唯一标识某一行记录,所以可以确保执行数据更新、删除的时候不会出现张冠李戴的错误。主键除了上述作用外,常常与外键构成参照完整性约束,防止出现数据不一致。数据库在设计时,主键起到了很重要的作用。 主键可以保证记录的唯一和主键域非空,数据库管理系统对于主键自动生成唯一索引,所以主键也是一个特殊的索引。
2. 一个表中可以有多个唯一性索引,但只能有一个主键。
3. 主键列不允许空值,而唯一性索引列允许空值。
4. 索引可以提高查询的速度。
其实主键和索引都是键,不过主键是逻辑键,索引是物理键,意思就是主键不实际存在,而索引实际存在在数据库中,主键一般都要建,主要是用来避免一张表中有相同的记录,索引一般可以不建,但如果需要对该表进行查询操作,则最好建,这样可以加快检索的速度。
首先primary key跟unique都是Constraints,属于logical object,而index则是physical object,会占用index page并被保存在磁盘上。
Primary key Constraints和unique Constraints都需要保证列是唯一的,不能有重复值,但是一张表只能有一个Primary key Constraints,但是可以有多个unique Constraints
B+Tree索引和Hash索引的区别
Hash索引和B+树索引的特点:
● Hash索引结构的特殊性,其检索效率非常高,索引的检索可以一次定位;
● B+树索引需要从根节点到枝节点,最后才能访问到页节点这样多次的IO访问;
为什么不都用Hash索引而使用B+树索引
Hash索引仅仅能满足"=",“IN"和”"查询,不能使用范围查询
Hash索引遇到大量Hash值相等的情况后性能并不一定就会比B+树索引高
B+树可以进行范围查询,Hash 索引不能。
B+树支持联合索引的最左侧原则,Hash 索引不支持。
B+树支持 order by 排序,Hash 索引不支持。
Hash 索引在等值查询上比 B+树效率更高。
B+树使用 like 进行模糊查询的时候,like 后面(比如%开头)的话可以起到优化的作用,Hash 索引根本无法进行模糊查询。
为什么说B+比B树更适合实际应用中操作系统的文件索引和数据库索引
(1) B+的磁盘读写代价更低
B+的内部结点并没有指向关键字具体信息的指针。因此其内部结点相对B树更小。如果把所有同一内部结点的关键字存放在同一盘块中,那么盘块所能容纳的关键字数量也越多。一次性读入内存中的需要查找的关键字也就越多。相对来说IO读写次数也就降低了。
(2) B+tree的查询效率更加稳定
由于非终结点并不是最终指向文件内容的结点,而只是叶子结点中关键字的索引。所以任何关键字的查找必须走一条从根结点到叶子结点的路。所有关键字查询的路径长度相同,导致每一个数据的查询效率相当。
主键索引为什么比普通索引快
例如对于下面这个表(其实就是上面的表中增加了一个k字段),且ID是主键。
主键索引和非主键索引的示意图如下:
其中R代表一整行的值。
从图中不难看出,主键索引和非主键索引的区别是:非主键索引的叶子节点存放的是主键的值,而主键索引的叶子节点存放的是整行数据,其中非主键索引也被称为二级索引,而主键索引也被称为聚簇索引。
根据这两种结构我们来进行下查询,看看他们在查询上有什么区别。
1、如果查询语句是 select * from table where ID = 100,即主键查询的方式,则只需要搜索 ID 这棵 B+树。
2、如果查询语句是 select * from table where k = 1,即非主键的查询方式,则先搜索k索引树,得到ID=100,再到ID索引树搜索一次,这个过程也被称为回表。
覆盖索引
SELECT id, name, phone FROM user_innodb WHERE name = “蝉沐风”;
可以直接从叶子节点获取所有数据,根本不需要回表操作。
我们把索引中已经包含了所有需要读取的列数据的查询方式称为覆盖索引(或索引覆盖)
索引下推
现在引入索引下推。准确来说,应该叫做索引条件下推(Index Condition Pushdown,ICP),就是过滤的动作由下层的存储引擎层通过使用索引来完成,而不需要上推到Server层进行处理。ICP是在MySQL5.6之后完善的功能。
加索引能够提升查询效率原因
加索引能够提升查询效率的根本原因是:
InnoDB 采用了 B+树这种多路平衡查找树来存储索引,使得在千万级数量的情况
下,树的高度可以控制在 3 层以内。
而层高代表磁盘 IO 的次数,因此基于索引查询减少了磁盘 IO 次数。
不加索引,会比较整个数据库,因为他不知道数据是不是规律的添加了索引,相当于加了一个目录,给索引字段排序,比较的时候只用几次就可以查找到你需要的数据。数据越多,索引约有用。拿空间换时间。
很明显的是:没有用索引我们是需要遍历双向链表来定位对应的页,现在通过**“目录”**就可以很快地定位到对应的页上了,其实底层结构就是B+树,B+树作为树的一种实现,能够让我们很快地查找出对应的记录
先从 MySQL 的基本存储结构说起,MySQL的基本存储结构是页(记录都存在页里边)
各个数据页可以组成一个双向链表
每个数据页中的记录又可以组成一个单向链表
每个数据页都会为存储在它里边儿的记录生成一个页目录,在通过主键查找某条记录的时候可以在页目录中使用二分法快速定位到对应的槽,然后再遍历该槽对应分组中的记录即可快速找到指定的记录
以其他列(非主键)作为搜索条件:只能从最小记录开始依次遍历单链表中的每条记录。
所以说,如果我们写select * from user where name = ‘张三’这样没有进行任何优化的sql语句,默认会这样做:
1)定位到记录所在的页:需要遍历双向链表,找到所在的页
2)从所在的页内中查找相应的记录:由于不是根据主键查询,只能遍历所在页的单链表了
很明显,在数据量很大的情况下这样查找会很慢!这样的时间复杂度为O(n)。
使用索引之后 索引做了些什么可以让我们查询加快速度呢?其实就是将无序的数据变成有序(相对):
要找到id为8的记录简要步骤:
很明显的是:没有用索引我们是需要遍历双向链表来定位对应的页,现在通过 “目录” 就可以很快地定位到对应的页上了,(二分查找,时间复杂度近似为O(logn))
其实底层结构就是B+树,B+树作为树的一种实现,能够让我们很快地查找出对应的记录
很简单,如果一本中华字典,没有前面的字典目录,你需要花多久才能找到某个汉字,同样的道理,如果没有索引,当我们查询数据的时候,需要从磁盘里面随机查找,机械磁盘随机读取数据需要频繁寻找磁道以及从磁盘读取数据,这个过程非常耗时。
第二个问题,索引是如何提升效率的
有了索引以后,相当于把索引列以及所属的磁盘块地址缓存到内存里面,在数据查询的时候,直接找到目标数据列所属的磁盘地址,去读取对应磁盘块的数据就行了,相当于减少了磁盘 IO 的次数。第三个问题,为什么要采用 B+树原因有很多,如果单纯在在性能角度来考虑,磁盘 IO 次数越少越好。
那用什么样的数据结构来存储索引列能够达到这个目的
很显然,多路平衡查找树就是一个很好的选择,也就是 B 树或者 B+树。
准确来说,只有命中了索引列的查询,才能提升效率。
并且,即便是命中了索引,查询效率也不一定高,比如在性别字段上加索引。
因为数据的散列度不高,导致可能会遍历整颗 B+树。
MySQL 索引结构采用 B+树原因
B 树是一种多路平衡树,用这种存储结构来存储大量数据,它的整个高度会相比二叉树来说,会矮很多。
而对于数据库而言,所有的数据都将会保存到磁盘上,而磁盘 I/O 的效率又比较低,特别是在随 机磁盘 I/O 的情况下效率更低。 所以高度决定了磁盘 I/O 的次数,磁盘 I/O 次数越少,对于性能的提升就越大,这也是为什么 采用 B 树作为索引存储结构的原因,Hash 哈希,只适合等值查询,不适合范围查询。
一般二叉树,可能会特殊化为一个链表,相当于全表扫描。
红黑树,一种特化平衡二叉树,MySQL 数据量很大时候,索引体积也会很大,内存放不下而从磁盘读取,树层次高话,读取磁盘次数就多了。
B-Tree,叶子节点和非叶子节点都保存数据,相同数据量,B+树更矮壮,也就说,相同数据量,B+树数据结构,查询磁盘次数会更少。
B+树只有叶子节点存放数据,而其他节点只存放索引,而 B 树每个节点都有 Data 域。所以相同大小的节点 B+树包含的索引比 B 树的索引更多(因为 B 树每个节点还有 Data 域) 还有就是 B+树的叶子节点是通过链表连接的,所以找到下限后能很快进行区间查询,比 B 树中序遍历快
为什么使用B+树而不是B树
在 B 树中,键和值即存放在内部节点又存放在叶子节点;在 B+树中,内部节点只存键,叶子节点则同时存放键和值B+树的叶子节点有一条链相连,而 B 树的叶子节点各自独立的。而 MySQL 的 InnoDB 存储引擎,它用了一种增强的 B 树结构,也就是 B+树来作为索引和数据 的存储结构。
B+树索引的所有数据均存储在叶子节点,而且数据是按照顺序排列的,链表连着的。那么 B+树使得范围查找,排序查找,分组查找以及去重查找变得异常简单(双向链表)
B+树的叶子节点是数据阶段用了一个链表串联起来,便于范围查找
B树非叶子节点上是不存储数据的,仅存储键值,而 B 树节点中不仅存储键值,也会存储数据。innodb 中页的默认大小是 16KB,如果不存储数据,那么就会存储更多的键值,相应的树的阶数(节点的子节点树)就会更大,树就会更矮更胖,如此一来我们查找数据进行磁盘的 IO 次数有会再次减少,数据查询的效率也会更快.
相比较于 B 树结构来说,B+树做了两个方面的优化,
1、B+树的所有数据都存储在叶子节点,非叶子节点只存储索引。
2、叶子节点中的数据使用双向链表的方式进行关联
1、从磁盘 I/O 效率方面来看:B+树的非叶子节点不存储数据,所以树的每一层就能够存储更多的索引数量,也就是说,B+树在层高相同的情况下,比 B 树的存储数据量更多,间接会减少磁盘 I/O 的次数。
2、从范围查询效率方面来看:在 MySQL 中,范围查询是一个比较常用的操作,而 B+树的所有存储在叶子节点的数据使用了双向链表来关联,所以 B+树在查询的时候只需查两个节点进行遍历就行,而 B 树需要获取所有节点,因此,B+树在范围查询上效率更高。
3、从全表扫描方面来看:因为,B+树的叶子节点存储所有数据,所以 B+树的全局扫描能力更 强一些,因为它只需要扫描叶子节点。而 B 树需要遍历整个树。
4、从自增 ID 方面来看:基于 B+树的这样一种数据结构,如果采用自增的整型数据作为主键,还
能更好的避免增加数据的时候,带来叶子节点分裂导致的大量运算的问题。
总体来说,我认为技术方案的选型,更多的要根据具体的业务场景来决定,并不一定是说 B+树就
是最好的选择,就像 MongoDB 里面采用 B 树结构,本质上来说,其实是关系型数据库和非关系型数据
库的差异。 B+树有一个特点就是,非叶子节点存储的是目录项,而叶子节点存储的是数据项,因而可以通过二分查找快速定位到指定的数据项。在数据量比较大的前提下,适当的建立索引可以调高查询效率
需要先理解B+tree、红⿊树的实现原理。B+tree带有顺序访问指针,是红⿊树不具备的。
大致概括为以空间换时间,数据库在未添加索引的时候进行查询默认的是进行全量搜索,也就是进行全局扫描,有多少条数据就要进行多少次查询,然后找到相匹配的数据就把他放到结果集中,直到全表扫描完。而建立索引之后,会将建立索引的KEY值放在一个n叉树上(BTree)。因为B树的特点就是适合在磁盘等直接存储设备上组织动态查找表,每次以索引进行条件查询时,会去树上根据key值直接进行搜索,次数约为log总条数,底数为页面存储数,例如一个100万数据的表,页面存储数为100,那么有索引的查询次数为3次log1000000100,但是全量搜索为100万次搜索,这种方式类似于二分法,但是这个是n分法
B 树和 B+树的区别
B+树,其实是在 B 树的基础上做的增强,最大的区别有两个:
B 树的数据存储在每个节点上,而 B+树中的数据是存储在叶子节点,并且通过链表的方式把叶子节点中的数据进行连接。B+树的子路数量等于关键字数这个是 B 树的存储结构,从 B 树上可以看到每个节点会存储数据。
B+树的所有数据是存储在叶子节点,并且叶子节点的数据是用双向链表关联的。
B 树和 B+树,一般都是应用在文件系统和数据库系统中,用来减少磁盘 IO 带来的性能损耗。
以 Mysql 中的 InnoDB 为例,当我们通过 select 语句去查询一条数据时,InnoDB需要从磁盘上去读取数据,这个过程会涉及到磁盘 IO 以及磁盘的随机 IO我们知道磁盘 IO 的性能是特别低的,特别是随机磁盘 IO。因为,磁盘 IO 的工作原理是,首先系统会把数据逻辑地址传给磁盘,磁盘控制电路按照寻址逻辑把逻辑地址翻译成物理地址,也就是确定要读取的数据在哪个磁道,哪个扇区。为了读取这个扇区的数据,需要把磁头放在这个扇区的上面,为了实现这一个点,磁盘会不断旋转,把目标扇区旋转到磁头下面,使得磁头找到对应的磁道,这里涉及到寻道事件以及旋转时间。很明显,磁盘 IO 这个过程的性能开销是非常大的,特别是查询的数据量比较多的情况下。所以在 InnoDB 中,干脆对存储在磁盘块上的数据建立一个索引,然后把索引数据以及索引列对应的磁盘地址,以 B+树的方式来存储。如图所示,当我们需要查询目标数据的时候,根据索引从 B+树中查找目标数据即可,由于 B+树分路较多,所以只需要较少次数的磁盘 IO 就能查找到。
为什么用 B 树或者 B+树来做索引结构?原因是 AVL 树的高度要比 B 树的高度要高,而高度就意味着磁盘 IO 的数量。所以为了减少磁盘 IO 的次数,文件系统或者数据库才会采用 B 树或者 B+树。
B 树:
(1)关键字集合分布在整颗树中;
(2)任何一个关键字出现且只出现在一个结点中;
(3)搜索有可能在非叶子结点结束;
(4)其搜索性能等价于在关键字全集内做一次二分查找;
B+树:
(1)有 n 棵子树的非叶子结点中含有 n 个关键字(b 树是 n-1 个),这些关键字不保存数据,只用来索引,所有数据都保存在叶子节点(b 树是每个关键字都保存数据);
(2)所有的叶子结点中包含了全部关键字的信息,及指向含这些关键字记录的指针,且叶子结点本身依关键字的大小自小而大顺序链接;
(3)所有的非叶子结点可以看成是索引部分,结点中仅含其子树中的最大(或最小)关键字;
(4)通常在 b+树上有两个头指针,一个指向根结点,一个指向关键字最小的叶子结点;
(5)同一个数字会在不同节点中重复出现,根节点的最大元素就是 b+树的最大元素。
B+树相比于 B 树的查询优势: 磁盘读写代价更低 查询效率更加稳定 遍历所有的数据更方便
(1)B+树的中间节点不保存数据,所以磁盘页能容纳更多节点元素,更“矮胖”;
(2)B+树查询必须查找到叶子节点,B 树只要匹配到即可不用管元素位置,因此 B+树查找更稳定(并不慢);
(3)对于范围查找来说,B+树只需遍历叶子节点链表即可,B 树却需要重复地中序遍历
B 树是一种多路平衡查找树,为了更形象的理解。二叉树,每个节点支持两个分支的树结构,相比于单向链表,多了一个分支。二叉查找树,在二叉树的基础上增加了一个规则,左子树的所有节点的值都小于它的根节点,右子树的所有子节点都大于它的根节点。
二叉查找树会出现斜树问题,导致时间复杂度增加,因此又引入了一种平衡二叉树,它具有二叉查找树的所有特点,同时增加了一个规则:”它的左右两个子树的高度差的绝对值不超过 1“。平衡二叉树会采用左旋、右旋的方式来实现平衡。
而 B 树是一种多路平衡查找树,它满足平衡二叉树的规则,但是它可以有多个子树,子树的数量取决于关键字的数量,比如这个图中根节点有两个关键字 3和 5,那么它能够拥有的子路数量=关键字数+1。
因此从这个特征来看,在存储同样数据量的情况下,平衡二叉树的高度要大于 B树。
了解二叉树、AVL 树、B 树的概念
红黑树 和 b+树的用途有什么区别
红黑树多用在内部排序,即全放在内存中的,STL 的 map 和 set 的内部实现就是红黑树。
B+树多用于外存上时,B+也被成为一个磁盘友好的数据结构。
平衡二叉树
若左子树不空,则左子树上所有节点的值均小于它的根节点的值;
若右子树不空,则右子树上所有节点的值均大于或等于它的根节点的值;
每个非叶子节点的左右子树的高度之差的绝对值(平衡因子)最多为1。
B+树:所有的节点值都在最后叶节点上用双向链表连接在了一起
索引失效的场景
如果条件中有or,即使其中有条件带索引也不会使用。
or 语句前后没有同事使用索引,当且仅当 or 语句查询条件的前后列均为索引时,索引生效。
对于多列索引,不是使用的第一部分,则不会使用索引。
like 以%开头索引无效,当 like 以&结尾,索引有效
如果列类型是字符串,那一定要在条件中将数据使用引号引用起来。否则不使用索引。(添加时,字符串必须’’)
where 子句中使用 != 或 <> 操作符,引擎将放弃使用索引而进行全表扫描。
在索引字段上使用,NOT、 <>、!= 、时候是不会使用索引的,对于这样的处理只会进 行全表扫描。
在 where 子句中对字段进行表达式操作,导致引擎放弃使用索引而进行全表扫描。
对索引字段进行计算操作,函数操作时不会使用索引。
在 where 子句中对字段进行函数操作,导致引擎放弃使用索引而进行全表扫描。在 where 子句中的 “=” 左边进行函数、算术运算或其他表达式运算,导致系统将可能无法正确使用索引。
不适合键值较少的列(重复数据较多的列)
组合索引,使用的不是第一列索引时候,索引失效,即最左匹配规则。
数据类型出现隐式转换,如 varchar 不加单引号的时候可能会自动转换为 int 类型,这个 时候索引失效。
在索引列上使用 IS NULL 或者 IS NOT NULL 时候,索引失效,因为索引是不索引空值 得。
当全表扫描速度比索引速度快的时候不会使用索引
MySQL中使用IN会不会走索引
结论:IN肯定会走索引,但是当IN的取值范围较大时会导致索引失效,走全表扫描
我们只需要注意一个最重要的type 的信息很明显的提现是否用到索引:
type结果值从好到坏依次是:
system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL
mysql区分度不高的字段建索引一定没用吗?
什么重复值高的字段不能建索引(比如性别字段等)
(以innodb为例)
a、非聚簇索引存储了对主键的引用,如果select字段不在非聚簇索引内,就需要跳到主键索引(上图中从右边的索引树跳到左边的索引树),再获取select字段值
b、如果非聚簇索引值重复率高,那么查询时就会大量出现上图中从右边跳到左边的情况,导致整个流程很慢
c、如果where值重复率高的字段,select用了limit,只查较少数据,也就是跳的次数很少的情况下,还是可以建索引的(后来想想也没必要,limit限制了数量,全表扫描也很快,除非字段值是排序的,必须扫描完前面的所有值)
d、如果没有3这个前提,则不建议在值重复率高的字段上建索引,因为查询效率低,还需要维护索引
建立索引的目的是提高数据库查询的性能,通过索引可以快速定位到符合查询条件的数据。然而,对于区分度不高的字段来说,建立索引可能会带来一些问题。
当字段的区分度很低时,即字段中的唯一值或不同值的数量相对较少,建立索引可能并不会显著提升查询性能。因为索引需要占用额外的存储空间,并且维护索引也需要一定的时间和资源开销。如果字段的区分度很低,那么索引的选择性就低,即索引中的每个值所代表的数据记录数量较多,这样查询时需要通过索引访问的次数就增多,反而可能导致查询性能下降。
此外,对于区分度不高的字段,如果频繁进行插入、更新或删除操作,那么索引的维护成本也会增加。因为每次数据的修改都需要对索引进行相应的更新,这会带来额外的性能开销。
综上所述,并不是所有字段都适合建立索引,特别是对于区分度不高的字段。在确定是否建立索引时,需要综合考虑字段的区分度、查询的频率以及数据的修改频率等因素,以及结合具体的业务需求和数据库的性能特点来做出决策。
不一定的
假如我的表中有一个性别字段,他的区分度肯定是不高的,只有男和女两种。一般情况下,如果表中数据量很大的话,用这个字段查询会导致没办法过滤掉很多数据,就可能没办法发挥索引的效果。
但是,如果有一种特殊情况,如男女比例是95:5,那么,这时候,如果我用"女作为性别的查询条件的话,还是以走索引,并且有很大的性能提升的,原因就是因为他可以过滤掉大部分数据。走索引可以大大提升效率。这种一般在任务表中比较多,比如任务表中有状态,两种情况:fail和success,大多数情况下,任务都是success的,只有一少部分是ini,这时候就可以给这个字段加索引。这样当我们扫描任务表执行任务的时候,还是可以大大提升查询效率的。
Mysql索引优化军规
阿里巴巴内部的 MySQL 索引优化经验主要包括以下几个方面的“军规”:
- 排除不必要的索引:在过多的情况下,索引并不一定能够提高查询性能。因此,阿里巴巴推荐只创建必要的索引,避免无意义的索引对数据库性能造成负担。
- 选择性很重要:选择性是指索引列中不同值数量与行数之比。较低的选择性会导致索引命中率降低,从而降低索引查询效率。因此,阿里巴巴建议为选择性较高的列创建索引。
- 联合索引效果好:联合索引是指在多个列上创建一个索引,以提高查询效率。阿里巴巴认为,联合索引在某些情况下效果比单列索引更好,但需要遵循选择性和列顺序的原则。
- 索引长度适中:索引的长度应该尽量短,这样能够提高索引扫描速度和存储效率。阿里巴巴建议将索引长度控制在几十个字节以内。
- 避免隐式转换:阿里巴巴强烈建议避免隐式转换,因为它会使索引失效,从而降低查询效率。
- 利用覆盖索引:覆盖索引是指查询需要的所有字段都在索引中,不需要回表到数据行中查询。阿里巴巴推荐尽可能使用覆盖索引,以减少磁盘 I/O 和网络传输开销。
- 控制索引数量和大小:过多的索引数量和大小会占据大量内存空间,从而影响数据库性能。阿里巴巴建议控制索引数量和大小,遵循最小化原则,同时考虑实际业务需求。
总之,阿里巴巴内部的 MySQL 索引优化经验主要是针对实际业务需求和数据情况而定的。在进行索引优化时,需要综合考虑选择性、长度、覆盖和数量等多种因素,以达到最佳的查询性能和并发处理能力。
mysql数据库建立索引的原则
索引查询是数据库中重要的记录查询方法,要不要进入索引以及在那些字段上建立索引都要和实际数据库系统的查询要求结合来考虑,下面给出实际中的一些通用的原则:
确定针对该表的操作是大量的查询操作还是大量的增删改操作。
尝试建立索引来帮助特定的查询。检查自己的sql语句,为那些频繁在where子句中出现的字段建立索引。
尝试建立复合索引来进一步提高系统性能。修改复合索引将消耗更长时间,同时,复合索引也占磁盘空间。
对于小型的表,建立索引可能会影响性能
应该避免对具有较少值的字段进行索引。
避免选择大型数据类型的列作为索引。
最左前缀匹配原则
频繁作为查询条件的字段才去创建索引
频繁更新的字段不适合创建索引
索引列不能参与计算,不能有函数操作
优先考虑扩展索引,而不是新建索引,避免不必要的索引
在order by 或者group by 子句中,创建索引需要注意顺序
区分度低的数据列不适合做索引列(如性别)
定义有外键的数据列一定要建立索引
对于定义为 text、image 数据类型的列不要建立索引。
删除不再使用或者很少使用的索引
- 在经常用作过滤器的字段上建立索引;
- 在SQL语句中经常进行GROUP BY、ORDER BY的字段上建立索引;
- 在不同值较少的字段上不必要建立索引,如性别字段;
- 对于经常存取的列避免建立索引;
- 用于联接的列(主健/外健)上建立索引;
- 在经常存取的多个列上建立复合索引,但要注意复合索引的建立顺序要按照使用的频度来确定;
- 缺省情况下建立的是非簇集索引,但在以下情况下最好考虑簇集索引,如:含有有限数目(不是很少)唯一的列;进行大范围的查询;充分的利用索引可以减少表扫描I/0的次数,有效的避免对整表的搜索。当然合理的索引要建立在对各种查询的分析和预测
什么样的字段适合创建索引
1、表的主键、外键必须有索引;外键是唯一的,而且经常会用来查询
2、数据量超过300的表应该有索引;
3、经常与其他表进行连接的表,在连接字段上应该建立索引;经常连接查询,需要有索引
4、经常出现在Where子句中的字段,加快判断速度,特别是大表的字段,应该建立索引,建立索引,一般用在select ……where f1 and f2 ,我们在f1或者f2上建立索引是没用的。只有两个使用联合索引才能有用
5、经常用到排序的列上,因为索引已经排序。
6、经常用在范围内搜索的列上创建索引,因为索引已经排序了,其指定的范围是连续的
控制索引的数量
众所周知,索引能够显著的提升查询sql的性能,但索引数量并非越多越好。
因为表中新增数据时,需要同时为它创建索引,而索引是需要额外的存储空间的,而且还会有一定的性能消耗。
阿里巴巴的开发者手册中规定,单表的索引数量应该尽量控制在5个以内,并且单个索引中的字段数不超过5个。
mysql使用的B+树的结构来保存索引的,在insert、update和delete操作时,需要更新B+树索引。如果索引过多,会消耗很多额外的性能。
那么,问题来了,如果表中的索引太多,超过了5个该怎么办?
这个问题要辩证的看,如果你的系统并发量不高,表中的数据量也不多,其实超过5个也可以,只要不要超过太多就行。
但对于一些高并发的系统,请务必遵守单表索引数量不要超过5的限制。
那么,高并发系统如何优化索引数量?
能够建联合索引,就别建单个索引,可以删除无用的单个索引。
将部分查询功能迁移到其他类型的数据库中,比如:Elastic Seach、HBase等,在业务表中只需要建几个关键索引即可。
InnoDB引擎中的索引策略
覆盖索引
最左前缀原则
索引下推
索引下推优化是 MySQL 5.6 引入的, 可以在索引遍历过程中,对索引中包含的字
段先做判断,直接过滤掉不满足条件的记录,减少回表次数。
最左前缀原则:MySQl 建立联合索引时,会遵循最左前缀匹配的原则,
即最左优先。如果你建立一个(a,b,c)的联合索引,相当于建立了
(a)、(a,b)、(a,b,c)三个索引。
覆盖索引:只需要在一棵索引树上就能获取 SQL 所需的所有列数据,无
需回表,速度更快。
不推荐使用索引的场景
1.字段不在 where 语句中出现时不要添加索引,如果 where 后含 IS NULL/IS NOT NULL/LIKE ‘%输入符%’等条件,不要使用索引,Where 子句里对索引使用不等于(<>),不建议使用索引,效果
2.存在函数的字段
3.数据唯一性差的字段不要使用索引 (数据离散度不高的字段 :男女) 所以不建议在有大量重复数据的列上建立索引
4.频繁更新的字段不要使用索引
5.数据量少
6.没有使用的字段(where/group by/order by)
不是where/group by/order by后面的字段没有必要建立索引,因为不会使用到该索引
创建索引
在MySQL中,可以使用以下语法来创建索引:
CREATE INDEX index_name ON table_name (column1, column2, …);
其中,index_name是要创建的索引的名称,table_name是要在其上创建索引的表名,(column1, column2, …)是要在哪些列上创建索引。
例如,如果要在名为users的表上创建一个名为idx_username的索引,用于加快对username列的查询速度,可以执行以下命令:
CREATE INDEX idx_username ON users (username);
如果要创建组合索引,可以将多个列名放在括号中,按照指定的顺序创建索引。例如,要在users表上创建一个组合索引,包括username和email列,可以执行以下命令:
CREATE INDEX idx_username_email ON users (username, email);
需要注意的是,创建索引会占用额外的存储空间,并且会对插入、更新和删除操作的性能产生一定的影响。因此,在创建索引时需要权衡索引的必要性和对性能的影响。
此外,在选择要创建索引的列时,应考虑到频繁被查询的列、经常出现在WHERE和JOIN子句中的列等。同时,如果有多列同时参与查询条件,可以考虑创建组合索引,以提高查询效率。
最后,通过使用SHOW INDEX FROM table_name命令可以查看表上已存在的索引,以及各个索引的相关信息。
创建索引建议
- 不要使用太长的字段来作为索引查找;
- 尽量使用单个列做为索引,而不是多列的联合索引;
- 使用适当的字段类型选择,比如使用 varchar(255) 替换 text 或 blob;
- 尽量减少数据库中不必要的索引,比如重复的索引,冗余的索引等。
总而言之,MySQL索引长度有限制,不可超越,避免使用过于长的索引,否则可能会在生成索引时报错,导致整个索引失效。
数据库页分裂
在数据库系统中,页分裂是指当数据库引擎需要在数据页中插入更多的数据时,而该数据页已经满了,无法容纳更多数据时发生的现象。这种情况下,数据库引擎需要创建新的数据页来存储额外的数据,从而导致数据在存储上的不连续,这就是页分裂。
页分裂可能会对数据库系统的性能产生负面影响。由于数据在物理上的不连续,数据库引擎在执行查询时可能需要访问多个数据页,从而增加了I/O操作的开销。此外,页面分裂还可能导致碎片化,使得数据库文件的大小变得不受控制,进而影响了磁盘的利用率。
为了减少页分裂对数据库性能的影响,可以采取一些优化策略。例如,合理设计数据库表的索引和簇集索引,使得数据插入时能够更加均匀地分布在各个数据页中,从而降低页面分裂的概率。此外,定期进行数据库的重组和整理,可以帮助减少页面分裂和碎片化的问题,提高数据库的性能和稳定性。