提高查询性能的秘密:深入剖析聚集、辅助、覆盖和联合索引

📕我是廖志伟,一名Java开发工程师、Java领域优质创作者、CSDN博客专家、51CTO专家博主、阿里云专家博主、清华大学出版社签约作者、产品软文创造者、技术文章评审老师、问卷调查设计师、个人社区创始人、开源项目贡献者。🌎跑过十五公里、徒步爬过衡山、🔥有过三个月减肥20斤的经历、是个喜欢躺平的狠人。

📘拥有多年一线研发和团队管理经验,研究过主流框架的底层源码(Spring、SpringBoot、Spring MVC、SpringCould、Mybatis、Dubbo、Zookeeper),消息中间件底层架构原理(RabbitMQ、RockerMQ、Kafka)、Redis缓存、MySQL关系型数据库、 ElasticSearch全文搜索、MongoDB非关系型数据库、Apache ShardingSphere分库分表读写分离、设计模式、领域驱动DDD、Kubernetes容器编排等。🎥有从0到1的高并发项目经验,利用弹性伸缩、负载均衡、报警任务、自启动脚本,最高压测过200台机器,有着丰富的项目调优经验。

📙经过多年在CSDN创作上千篇文章的经验积累,我已经拥有了不错的写作技巧。同时,我还与清华大学出版社签下了四本书籍的合约,并将陆续在明年出版。这些书籍包括了基础篇、进阶篇、架构篇的📌《Java项目实战—深入理解大型互联网企业通用技术》📌,以及📚《解密程序员的思维密码–沟通、演讲、思考的实践》📚。具体出版计划会根据实际情况进行调整,希望各位读者朋友能够多多支持!

以梦为马,不负韶华

希望各位读者大大多多支持用心写文章的博主,现在时代变了,信息爆炸,酒香也怕巷子深,博主真的需要大家的帮助才能在这片海洋中继续发光发热,所以,赶紧动动你的小手,点波关注❤️,点波赞👍,点波收藏⭐,甚至点波评论✍️,都是对博主最好的支持和鼓励!

💡在这个美好的时刻,本人不再啰嗦废话,现在毫不拖延地进入文章所要讨论的主题。接下来,我将为大家呈现正文内容。

CSDN
提高查询性能的秘密:深入剖析聚集、辅助、覆盖和联合索引
在数据库中,索引是提高查询效率的重要手段之一。而聚集索引、辅助索引、覆盖索引和联合索引是常用的一些索引类型。

首先,聚集索引是一种按照主键顺序排列的索引,它将数据页中的行记录按主键值进行排序,使得相近的记录在物理存储上也是相邻的。因此,聚集索引可以实现较快的检索和范围查询。每个数据页通过一个双向链表进行链接,同时存放完整的行记录。举个例子,如果有一张订单表,主键为订单号,那么聚集索引就可以将不同订单按照订单号排序,在进行查询或者范围查询时可以快速定位到相应订单。

接着,辅助索引不是按照主键值进行排序的,而是将索引列的值作为键值存储,并指向聚集索引中相应行记录的位置。因此,辅助索引可以在聚集索引的基础上提供更多的检索方式和查询条件,例如在一张员工表中,辅助索引可以按照员工姓名进行排序,提供姓名的检索和查询。此外,辅助索引叶子节点中不包含完整的行记录,而是包含一个书签,用来指向相应的行记录,以加快查询速度。

其次,覆盖索引是指辅助索引中已经包含了需要查询的所有字段,在查询时就不需要再去聚集索引中查找相应的行记录了。这样的话可以减少IO操作,提高查询效率。举个例子,如果有一张员工表,辅助索引包含员工姓名和薪资两个字段,而查询仅需要查询员工姓名和薪资,那么辅助索引就可以提供全部需要的字段信息,无需再去聚集索引中查找。

再次,联合索引是对表上的多个列进行索引,联合索引中的键值都是排序的。联合索引的好处在于可以避免多建索引带来的开销。例如,如果建立一个(a,b,c)的复合索引,实际上就建立了(a),(a,b),(a,b,c)三个索引。这样做不仅可以避免建立过多的索引,同时减少了写操作和磁盘空间的开销。此外,联合索引还可以避免filesort排序,这是一种将结果集按照指定顺序排序的过程,用于优化查询性能,但是会增加查询的负担。联合索引可以通过叶子节点以顺序的方式读取数据,避免了filesort排序的开销,提高了查询效率。

🍊 聚集索引

聚集索引,听起来好像很高大上的样子,其实它就是一种能够让查询更快的技术。我们先来看看什么是InnoDB存储引擎表,它其实就是一种索引组织表,也就是说,在这种表中,数据是按照主键的顺序进行存放的。这样做有什么好处呢?可以让查询的速度更快!

而聚集索引,顾名思义,就是针对每一张表的主键构建一颗B+树,这样就可以在叶子节点上存储整张表的行记录数据。聚集索引的叶子节点被称为数据页,每个数据页之间是通过双向链表进行链接的。因为每张表只能拥有一个聚集索引,所以在多数情况下查询优化器会倾向于采用聚集索引。

聚集索引有什么好处呢?首先它能够在B+树索引的叶子节点上直接找到数据,这样就可以让查询的速度更快。其次,由于定义了数据的逻辑顺序,它对于主键的排序查找和范围查找速度非常快。而且,聚集索引还可以让我们在进行范围查询时,通过叶子节点的上层中间节点得知页的范围,直接读取数据页即可,这样也能够让查询速度更快。

接下来,我们来看几个例子,帮助大家更好地理解聚集索引的应用。

假设我们有一个注册用户的表,需要查找最后注册的10位用户。我们可以使用如下语句进行查询:SELECT * FROM Profile ORDER BY id LIMIT 10; 由于B+树索引是双向链表的,所以我们可以快速找到最后一个数据页,并取出10条记录。需要注意的是,在这个查询过程中,虽然使用了ORDER BY对主键id记录进行排序,但是实际上并没有进行所谓的filesort操作,而这就是因为聚集索引的特点。

再举个例子,如果我们需要查找主键某一范围内的数据,我们可以使用如下语句进行查询:SELECT * FROM Profile where id>1 and id < 100; 这种查询就是范围查询,而聚集索引正是能够让这种查询更加高效。

🍊 辅助索引

辅助索引是另一种不同的索引类型,也被称为非聚集索引。与聚集索引不同,辅助索引的叶子节点并不包含行记录的全部数据。而是包含键值以及一个书签,这个书签指向另一个存储位置,告诉数据库在哪里可以找到与该索引相对应的行数据。

举个例子来说明这一点。假设我们有一个存储顾客信息的数据库表,其中的主键是客户ID,而我们还想要按客户姓名来进行搜索。那么我们可以使用辅助索引来加速此类查询。辅助索引会通过客户姓名来建立索引,它的叶子节点会存储客户姓名以及指向主键索引的书签。当我们查询一个客户的信息时,数据库会通过辅助索引找到对应的主键,然后再通过主键索引来找到该客户的全部信息。

辅助索引的存在并不会影响数据在聚集索引中的组织,因此每张表上可以有多个辅助索引。当我们使用辅助索引来查找数据时,数据库会遍历辅助索引并通过叶级别的指针获得指向主键索引的主键,然后再通过主键索引来找到一个完整的行记录。

让我们再来看一下之前的例子。如果我们在一张存储顾客信息的表中有一个辅助索引来支持按客户姓名的查询。假设我们有一个高度为3的辅助索引树,我们需要遍历这棵树3次才能找到对应的主键。如果聚集索引树的高度也为3,那么我们还需要对聚集索引树进行3次查找,最终才能找到一条完整的记录。因此,我们总共需要6次逻辑IO访问才能得到我们需要的数据。

再来看一个示例。假设我们有一个存储产品信息的数据库表,其中的主键是产品ID。我们想要按照产品类别和价格来进行查询。我们可以创建一个辅助索引来支持这类查询。该辅助索引将产品类别和价格作为关键字,而它的叶子节点中包含了指向主键索引的书签。当我们查询某个产品类别和价格的时候,数据库会遍历辅助索引树找到相应的主键,然后再通过主键索引来找到完整的行记录。

🍊 覆盖索引

覆盖索引:减少IO操作的好帮手

覆盖索引是一种针对辅助索引的优化方式,在InnoDB存储引擎中被广泛应用,可以大大减少查询所需的IO操作,提高查询的效率。下面我们详细介绍什么是覆盖索引,以及什么情况下优化器会选择使用覆盖索引。

🎉 覆盖索引是什么?

一般情况下,当我们在进行SELECT语句查询操作时,MySQL会使用B+树索引来加速查询。B+树索引是一种常见的数据结构,它将数据按照索引字段的值建立一棵树,查询时只需遍历这棵树就可以快速找到目标数据。

在查询时,MySQL通过B+树索引查找到目标数据所在的页,然后从磁盘读取该页数据。但这时可能会出现一种情况:在目标数据所在的页中,还有很多其他的数据,而我们只需要其中的一部分数据,这样就导致了不必要的IO操作。例如,我们有一个表buy_log,包含了用户id和购买日期两个字段,创建如下的两个索引:

ALTER TABLE buy_log ADD KEY (userid);
ALTER TABLE buy_log ADD KEY (userid,buy_date);

那么如果我们执行如下的查询语句:

SELECT userid FROM buy_log WHERE buy_date='2022-05-01';

MySQL首先通过(userid,buy_date)联合索引定位到了目标数据所在的页,但是这个索引包含了两个字段,而我们只需要查询其中的一个字段userid,因此MySQL还是需要从磁盘上读取整个页的数据才能获取到最终结果。如果这样的查询操作比较频繁,将会耗费大量的IO操作,影响系统的性能。

覆盖索引的出现,就是为了解决这个问题。所谓覆盖索引,是指辅助索引中包含了查询要用到的所有字段。例如,在上述的例子中,如果我们将查询语句改为:

SELECT PRIMARY KEY, userid FROM buy_log WHERE buy_date='2022-05-01';

这时,MySQL可以直接从(userid,buy_date)联合索引中获取到所有的查询结果,因为辅助索引中已经包含了查询要用到的所有字段(PRIMARY KEY和userid),不需要再去读取聚簇索引中的数据。这样就可以避免不必要的IO操作。

需要注意的是,覆盖索引并不是所有的查询操作都可以使用的,只有在查询所需的字段都包含在辅助索引中时,才能使用覆盖索引。当然,使用覆盖索引并不能解决所有的性能问题,对于一些复杂的查询操作,还需要使用其他的优化方法。

🎉 什么情况下优化器会选择使用覆盖索引?

我们已经介绍了覆盖索引的概念和优点,接下来我们将详细介绍什么情况下优化器会选择使用覆盖索引。

📝 1. 查询操作只需要用到辅助索引中的字段

当我们查询的条件只包含辅助索引中的字段时,MySQL会尽可能使用覆盖索引来加速查询。所谓辅助索引,即非聚集索引,也称为二级索引,是相对于聚集索引(也称为主键索引)而言的。辅助索引中包含了索引字段以及主键字段的值,可以说是聚集索引的一个附属物。在InnoDB存储引擎中,辅助索引叶子节点中会包含主键字段的值,这样就能够通过辅助索引查询到目标数据的主键值,并且可以直接从辅助索引中获取到所有的查询结果,从而避免不必要的IO操作。

例如,我们有一个表buy_log,包含了用户id和购买日期两个字段,创建如下的两个索引:

ALTER TABLE buy_log ADD KEY (userid);
ALTER TABLE buy_log ADD KEY (userid,buy_date);

那么,下面这四个查询操作都可以使用覆盖索引来加速查询:

SELECT userid FROM buy_log WHERE buy_date='2022-05-01';
SELECT PRIMARY KEY, userid FROM buy_log WHERE buy_date='2022-05-01';
SELECT userid FROM buy_log WHERE userid=1 AND buy_date='2022-05-01';
SELECT PRIMARY KEY, userid FROM buy_log WHERE userid=1 AND buy_date='2022-05-01';

因为这些查询操作只需要用到辅助索引中的字段userid和buy_date,可以直接从辅助索引中获取到查询结果,不需要读取聚簇索引中的数据。

📝 2. 进行统计操作时,可以利用覆盖索引加速查询

当我们进行COUNT、AVG、SUM等统计操作时,如果辅助索引中包含了所有的统计字段,那么MySQL就会选择使用覆盖索引来加速查询,因为这样可以避免不必要的IO操作,提高查询效率。

例如,对于上面的buy_log表,如果我们要查询所有记录的数量,可以使用以下查询语句:

SELECT COUNT(*) FROM buy_log;

此时,InnoDB存储引擎并不会选择通过查询聚集索引来进行统计。由于buy_log表上还有辅助索引,而辅助索引远小于聚集索引,选择辅助索引可以减少IO操作。

📝 3. 覆盖索引的查询效率优于从聚簇索引中查询

当使用覆盖索引查询的效率要高于从聚簇索引中查询时,MySQL会优先选择使用覆盖索引。

例如,我们有一个表orders,其中包含了订单编号、订单金额和下单日期三个字段,创建了如下的索引:

CREATE TABLE orders (
  order_number INT NOT NULL,
  order_amount FLOAT NOT NULL,
  order_date DATE NOT NULL,
  PRIMARY KEY (order_number),
  KEY order_date (order_date)
);

那么,我们执行以下两种查询语句:

SELECT order_number FROM orders WHERE order_date='2022-05-01';
SELECT order_amount FROM orders WHERE order_number=1001;

第一种查询语句中,WHERE条件包含了辅助索引order_date中的字段,而order_number字段在辅助索引中也有相应的值,因此可以使用覆盖索引来加速查询。

第二种查询语句中,WHERE条件包含了聚簇索引primary key中的字段order_number,但我们需要查询的是order_amount字段,如果采用从聚簇索引中查询的方式,需要将整个数据行都读取出来,而如果使用覆盖索引,只需要读取辅助索引中的数据即可,因此查询效率会更高。

综上所述,覆盖索引是一种非常有效的优化方式,可以大大减少不必要的IO操作,提高查询效率。但需要注意的是,并不是所有的查询操作都可以使用覆盖索引,只有在满足特定条件时,MySQL才会选择使用覆盖索引来加速查询。

🍊 联合索引

联合索引:让查询更快,让数据库更高效

在数据库中,索引是非常重要的部分,因为它能够提高数据库的查询效率。而联合索引又是其中一种比较重要的索引,在对表中的多列进行索引时,它能够让查询更快,让数据库更高效。本文将详细介绍什么是联合索引、如何使用联合索引以及它的优缺点等内容。

🎉 什么是联合索引?

联合索引,指的是对表上的多个列进行索引。通常在设计数据库表的时候,我们会根据表的结构和需求来选择哪些列需要进行索引,而当需要同时对多个列进行筛选和排序时,就需要使用联合索引。

比如,在一个购物网站的数据库中,有一个购买记录的表(buy_log),其中包含用户ID(userid)和购买日期(buy_date)两个字段。如果需要根据用户ID来查询购买记录,并按购买日期降序排列,那么就需要使用联合索引对userid和buy_date进行索引,以提高查询效率。

🎉 如何使用联合索引?

假设有以下购买记录的表(buy_log):

CREATE TABLE buy_log(
userid INT UNSIGNED NOT NULL,
goods_id INT UNSIGNED NOT NULL,
buy_date DATE
)ENGINE=InnoDB;

为了提高查询效率,我们需要对用户ID(userid)、商品ID(goods_id)和购买日期(buy_date)这三个字段进行索引。那么我们可以先对userid进行单列索引,再对(userid,goods_id,buy_date)这个联合索引进行索引,代码如下:

ALTER TABLE buy_log ADD KEY (userid);
ALTER TABLE buy_log ADD KEY (userid,goods_id,buy_date);

这样,当我们需要查询商品ID为1的用户购买记录,并以购买日期降序排序时,可以使用以下SQL语句:

SELECT * FROM buy_log WHERE goods_id=1 ORDER BY buy_date DESC;

MySQL在优化器中会优先选择使用(userid,goods_id,buy_date)这个联合索引进行查询,因为索引中已经按照购买日期进行排序,无需再进行一次额外的排序操作。而如果我们只需要按照用户ID进行查询,那么就可以直接使用单列索引(userid)进行查询,以提高查询效率。

🎉 联合索引的优缺点

虽然联合索引可以提高查询效率,但也存在一些缺点。下面我们来详细介绍一下。

优点:

  1. 提高查询效率:联合索引能够提高查询效率,尤其是在同时筛选多个字段的时候。

  2. 减少IO次数:联合索引可以让MySQL更快地定位到需要查找的记录,从而减少IO次数。

  3. 覆盖索引:如果联合索引包含了所有需要查询的字段,那么只需要使用索引就可以得到查询结果,无需读取数据表,这就是覆盖索引(Covering Index)。

缺点:

  1. 索引长度:联合索引的长度会随着所包含的字段数量增加而增加,如果字段数量过多,那么索引长度就会变得很大。

  2. 索引更新:对于包含多个字段的联合索引,如果其中任何一个字段发生了更新,那么整个索引都需要更新,这就会导致索引更新操作比较耗时。

  3. 查询条件顺序:使用联合索引时,查询条件的顺序非常重要。如果条件的顺序不对,那么MySQL就无法使用索引,需要做全表扫描。

🎉 如何优化联合索引?

虽然联合索引可以提高查询效率,但在实际使用中,可能会出现无法使用索引或者索引效率不高等问题。这里我们列举几种常见的情况,并介绍如何优化联合索引。

📝 1. 索引长度过长导致索引失效

我们在设计联合索引时需要注意,不宜将太多的字段包含在一个联合索引中,否则会导致索引长度过长,从而导致索引失效。

比如,在之前的购买记录表(buy_log)中,如果需要同时对用户ID(userid)、商品ID(goods_id)、购买日期(buy_date)、商品名称(goods_name)和购买数量(buy_num)进行索引,那么就需要将这五个字段全部包含在一个联合索引中,代码如下:

ALTER TABLE buy_log ADD KEY (userid,goods_id,buy_date,goods_name,buy_num);

但是,由于联合索引的长度过长,MySQL可能会认为它并不是一个好的索引,从而决定不使用它。这个时候,我们可以通过拆分联合索引来解决这个问题。比如,我们可以将商品名称(goods_name)和购买数量(buy_num)这两个不经常用到的字段单独放在一个索引中,代码如下:

ALTER TABLE buy_log ADD KEY (userid,goods_id,buy_date);
ALTER TABLE buy_log ADD KEY (goods_name,buy_num);

这样就可以避免因为索引长度过长而导致索引失效的问题。

📝 2. 查询条件顺序不正确导致索引失效

使用联合索引时,查询条件的顺序非常重要。如果条件的顺序不对,那么MySQL就无法使用索引,需要做全表扫描。比如,在购买记录表(buy_log)中,如果需要查询商品ID为1的用户购买日期在2021年3月1日之后的购买记录,那么查询条件的顺序应该为(goods_id,buy_date,userid),代码如下:

SELECT * FROM buy_log WHERE goods_id=1 AND buy_date>'2021-03-01' AND userid=123;

如果按照(userid,goods_id,buy_date)的顺序来查询,那么MySQL就无法使用联合索引,需要做全表扫描。

📝 3. 索引更新导致性能下降

对于包含多个字段的联合索引,如果其中任何一个字段发生了更新,那么整个索引都需要更新,这就会导致索引更新操作比较耗时。因此,我们在设计联合索引时需要注意,尽量避免将经常更新的字段包含在联合索引中。

比如,在购买记录表(buy_log)中,如果经常需要更新购买日期(buy_date)这个字段,那么我们就应该将其单独放在一个索引中,代码如下:

ALTER TABLE buy_log ADD KEY (userid,goods_id);
ALTER TABLE buy_log ADD KEY (buy_date);

这样就可以避免因为索引更新而导致性能下降的问题。

CSDN

🔔如果您需要转载或者搬运这篇文章的话,非常欢迎您私信我哦~

希望各位读者大大多多支持用心写文章的博主,现在时代变了,信息爆炸,酒香也怕巷子深,博主真的需要大家的帮助才能在这片海洋中继续发光发热,所以,赶紧动动你的小手,点波关注❤️,点波赞👍,点波收藏⭐,甚至点波评论✍️,都是对博主最好的支持和鼓励!

📥博主的人生感悟和目标

探寻内心世界,博主分享人生感悟与未来目标

  • 🍋程序开发这条路不能停,停下来容易被淘汰掉,吃不了自律的苦,就要受平庸的罪,持续的能力才能带来持续的自信。我本身是一个很普通程序员,放在人堆里,除了与生俱来的盛世美颜,就剩180的大高个了,就是我这样的一个人,默默写博文也有好多年了。
  • 📺有句老话说的好,牛逼之前都是傻逼式的坚持,希望自己可以通过大量的作品、时间的积累、个人魅力、运气、时机,可以打造属于自己的技术影响力。
  • 💥内心起伏不定,我时而激动,时而沉思。我希望自己能成为一个综合性人才,具备技术、业务和管理方面的精湛技能。我想成为产品架构路线的总设计师,团队的指挥者,技术团队的中流砥柱,企业战略和资本规划的实战专家。
  • 🎉这个目标的实现需要不懈的努力和持续的成长,但我必须努力追求。因为我知道,只有成为这样的人才,我才能在职业生涯中不断前进并为企业的发展带来真正的价值。在这个不断变化的时代,我必须随时准备好迎接挑战,不断学习和探索新的领域,才能不断地向前推进。我坚信,只要我不断努力,我一定会达到自己的目标。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Java程序员廖志伟

赏我包辣条呗

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值