网站首页 > 教程文章 正文
复合索引,也被称为联合索引,是指在单个索引中包含了数据表中的多个列的数据,与普通的单索引最大的区别就是复合索引会覆盖多个列值,从而可以提升多字段查询的速度。例如比较经典的employees表中会包含很多的列,如下所示id、name、age、department,那么在进行数据查询的时候,为了能够提升查询的效率,我们可以为 name 和 department 创建一个联合索引,这样我们就可以快速的根据这两个列的值进行数据查询操作了。下面我们就来详细介绍一下复合索引。
为什么使用复合索引?
其实使用索引最大的目的就是为了提高查询效率,尤其是涉及到多个列条件进行查询的时候,如果为每个列设置索引可能无法做到很好的多条件匹配,所以创建复合索引的话可以大大减少MySQL执行多列查询的时间提升查询效率,总结来讲如下所示。
- 提高多列查询性能:在MySQL的查询中如果涉及到了多列查询,那么我们就可以通过复合索引来覆盖所有的查询的列,这样,如果在读取数据的涉及到了包含的列,那么我们就不用回表查询数据而是直接利用索引进行返回,这样可以快速定位到多列的复合条件的查询记录。
- 避免多列单索引:上面我们也提到了,在没有复合索引的情况下,查询条件的索引选择只能是单列索引,然后将其结果进行合并,这样的操作会导致系统开销过大,因为单列索引需要一个一个走索引,而复合索引的话只需要走一次索引即可。
- 避免回表:如果我们查询的字段正好包含在了复合索引中,这个时候我们就不需要回表去数据,而是直接从索引中获取数据即可,这样的操作减少了回表带来的I/O操作从而提升了数据查询的性能。
下面我们给出一个简单的复合索引的小例子,以employees表为例,包含了如下的一些字段。
CREATE TABLE employees (
id INT,
name VARCHAR(50),
age INT,
department VARCHAR(50),
PRIMARY KEY(id)
);
单列索引
我们可以通过下面的操作来为name 和 department 分别创建单列索引。
CREATE INDEX idx_name ON employees (name);
CREATE INDEX idx_department ON employees (department);
在执行查询操作的时候,如果包含了 name 和 department两个字段,那么在查询操作的时候MySQL会分别使用这两个索引来执行查询操作,然后执行一个额外的操作来合并获取最终的结果,而这个操作所带来的影响就是查询性能会变差,毕竟是涉及到了数据的合并,如果再加上排序等操作的话这个时间会非常长。
复合索引
如果我们为这两个列创建了一个 (name, department)的复合索引,如下所示。
CREATE INDEX idx_name_department ON employees (name, department);
通过这个复合索引,我们可以同时加速对这两个列的查询,尤其是在查询条件中涉及到了name和department的查询的时候,MySQL就可以直接使用这个索引进行查询,从而避免了多列扫描和数据合并的操作所带来的性能损耗,如下所示。下面这个查询操作就可以很有效的利用我们创建的复合索引。
SELECT * FROM employees WHERE name = 'John' AND department = 'HR';
复合索引的使用规则和注意事项
其实根据上面的分析,我们也知道了复合索引并不是所有操作的最优解,在某些情况下还是要注意慎用,因此在选择复合索引的时候,需要考虑到如下的一些注意事项。
最左前缀匹配原则
在之前的分享中,我们提到过在使用复合索引的时候,查询条件需要遵守最左前缀匹配的原则,这个最左匹配的原则主要涉及到的就是索引B+数据的顺序问题。如下所示。复合索引 (col1, col2, col3) 可以用于匹配以下查询:
- WHERE col1 = ?(仅匹配 col1)
- WHERE col1 = ? AND col2 = ?(匹配 col1 和 col2)
- WHERE col1 = ? AND col2 = ? AND col3 = ?(匹配 col1、col2 和 col3)
但是对于如下的一些匹配条件,就无法做到高效的匹配了,这是因为这些索引使用中没有涉及到col1列,所以在B+树索引中无法确定中间节点,从而导致索引无法高效利用。
- WHERE col2 = ? 或 WHERE col2 = ? AND col3 = ?,这样的查询无法使用复合索引来优化。
列的顺序很重要
根据上面的原则,我们也可以知道设置索引过程中字段的顺序是非常关键的,所以查询的字段可以按照使用字段的顺序来进行设置,这样的话在查询时候就可以利用复合索引实现高效查询了,例如,在日常开发中如果查询条件通常是 WHERE col1 = ? AND col3 = ?,那么我们就可以考虑将ol1 放在复合索引的最左侧,而不是 col3。这样就可以很好的利用好最左前缀匹配
避免过多的列
在创建复合索引的时候,需要避免将太多的列放入到索引中,从而增加了索引维护的成本尤其是在一些有频繁操作的CUD表中,一般建议在创建复合索引的时候,仅仅包含查询中常用到的一些列就可以了,另外就是可以将查询返回结果中的列设置为复合索引,这样可以避免在查询过程中回表带来的I/O消耗。
索引覆盖
如果查询涉及的所有字段都存在于复合索引中,那么MySQL就可以使用覆盖索引,直接从索引中返回查询结果,避免回表操作,提升查询性能。另外,如果查询条件中如果涉及到了多个列的组合查询,可以考虑使用复合索引。但对于那些仅使用某一列的查询,单列索引可能会更合适。
复合索引的应用实例
假设我们有如下的一些表和复合索引。
CREATE TABLE orders (
order_id INT AUTO_INCREMENT PRIMARY KEY,
customer_id INT,
product_id INT,
order_date DATE,
amount DECIMAL(10, 2)
);
-- 创建复合索引:customer_id, order_date
CREATE INDEX idx_customer_date ON orders (customer_id, order_date);
多列过滤查询
如下所示,在执行如下的这个操作的时候,查询条件正好匹配到了索引列的顺序,那么MySQL的查询就会走多列查询操作。
SELECT * FROM orders WHERE customer_id = 101 AND order_date = '2024-01-01';
单列过滤查询
如下所示,如果在查询操作中复合了最左前缀匹配,那么这个索引也会被MySQL的查询优化器选择使用该复合索引进行查询。
SELECT * FROM orders WHERE customer_id = 101;
不完全匹配的查询
如下所示,这个查询则不能有效的利用这个符合索引,是因为它是从第二列开始查询的,所以没有复合最左前缀匹配原则,所以不能有效的利用这个索引。
SELECT * FROM orders WHERE order_date = '2024-01-01';
总结
复合索引是由多个列组成的索引,可以有效地加速包含多个列的查询的情况,尤其是用于涉及多个查询条件的复杂查询,可以显著提高查询性能。然而,复合索引的列顺序非常关键,必须遵循最左前缀匹配原则,因此设计复合索引时需要根据实际查询模式来考虑列的顺序和选择性。
- 上一篇: 程序员必须掌握的8种数据结构: 1.数组: 数组
- 下一篇: 图文并茂 10分钟深度剖析数据库索引
猜你喜欢
- 2025-03-07 深入解析MySQL索引高速查询的核心机制与原理
- 2025-03-07 每个人都需要了解的索引知识点
- 2025-03-07 C++ B-tree:探索数据结构之美
- 2025-03-07 Java面试核心技能全景解析:架构设计与编码能力的深度碰撞
- 2025-03-07 图文并茂 10分钟深度剖析数据库索引
- 2025-03-07 程序员必须掌握的8种数据结构: 1.数组: 数组
- 2025-03-07 PostgreSQL技术内幕25:时序数据库插件TimescaleDB
- 2025-03-07 为什么大厂不建议使用多表join?
- 2025-03-07 什么是算法?
- 2025-03-07 数据结构——第7章-查找
- 05-11阿里开源MySQL中间件Canal快速入门
- 05-11MyBatis插件开发实战:手写一个分页插件
- 05-11Flask数据库——SQLAlchemy
- 05-11MySQL 到 Hazelcast Cloud 实时数据同步实操分享
- 05-11sqlmap 详解
- 05-11一篇文章让你学会Elasticsearch中的查询
- 05-11Mysql性能优化这5点你知道吗?简单却容易被初学者忽略!
- 05-11Spring Boot 实现 MySQL 读写分离技术
- 最近发表
- 标签列表
-
- location.href (44)
- document.ready (36)
- git checkout -b (34)
- 跃点数 (35)
- 阿里云镜像地址 (33)
- qt qmessagebox (36)
- md5 sha1 (32)
- mybatis plus page (35)
- semaphore 使用详解 (32)
- update from 语句 (32)
- vue @scroll (38)
- 堆栈区别 (33)
- 在线子域名爆破 (32)
- 什么是容器 (33)
- sha1 md5 (33)
- navicat导出数据 (34)
- 阿里云acp考试 (33)
- 阿里云 nacos (34)
- redhat官网下载镜像 (36)
- srs服务器 (33)
- pico开发者 (33)
- https的端口号 (34)
- vscode更改主题 (35)
- 阿里云资源池 (34)
- os.path.join (33)