在Mysql数据库中创建了联合索引(或称复合索引,即包含多个列的索引),要遵循最左前缀法则。最左前缀法则指的是查询从索引最左列开始,不跳过中间列。
示例,以之前的tb_user1表为例,数据如下表所示:
id | name | phone | profession | age | gender | status | createtime | |
---|---|---|---|---|---|---|---|---|
1 | 吕布 | 17799990000 | [email protected] | 软件工程 | 23 | 1 | 6 | 2001-02-02 00:00:00 |
2 | 曹操 | 17799990001 | [email protected] | 通讯工程 | 33 | 1 | 0 | 2001-03-05 00:00:00 |
3 | 赵云 | 17799990002 | [email protected] | 英语 | 34 | 1 | 2 | 2002-03-02 00:00:00 |
4 | 孙悟空 | 17799990003 | [email protected] | 工程造价 | 54 | 1 | 0 | 2001-07-02 00:00:00 |
5 | 花木兰 | 17799990004 | [email protected] | 软件工程 | 23 | 2 | 1 | 2001-04-22 00:00:00 |
6 | 大乔 | 17799990005 | [email protected] | 舞蹈 | 22 | 2 | 0 | 2001-02-07 00:00:00 |
7 | 露娜 | 17799990006 | [email protected] | 应用数学 | 24 | 2 | 0 | 2001-02-08 00:00:00 |
8 | 程咬金 | 17799990007 | [email protected] | 化工 | 38 | 1 | 5 | 2001-05-23 00:00:00 |
9 | 项羽 | 17799990008 | [email protected] | 金属材料 | 43 | 1 | 0 | 2001-09-18 00:00:00 |
10 | 白起 | 17799990009 | [email protected] | 机械工程及其自动化 | 27 | 1 | 2 | 2001-08-16 00:00:00 |
11 | 韩信 | 17799990010 | [email protected] | 无机非金属材料工程 | 27 | 1 | 0 | 2001-06-12 00:00:00 |
12 | 荆轲 | 17799990011 | [email protected] | 会计 | 29 | 1 | 0 | 2001-05-11 00:00:00 |
13 | 兰陵王 | 17799990012 | [email protected] | 工程造价 | 44 | 1 | 1 | 2001-04-09 00:00:00 |
14 | 狂铁 | 17799990013 | [email protected] | 应用数学 | 43 | 1 | 2 | 2001-04-10 00:00:00 |
15 | 貂蝉 | 17799990014 | [email protected] | 软件工程 | 40 | 2 | 3 | 2001-02-12 00:00:00 |
16 | 妲己 | 17799990015 | [email protected] | 软件工程 | 31 | 2 | 0 | 2001-01-30 00:00:00 |
17 | 芈月 | 17799990016 | [email protected] | 工业经济 | 35 | 2 | 0 | 2000-05-03 00:00:00 |
18 | 嬴政 | 17799990017 | [email protected] | 化工 | 38 | 1 | 1 | 2001-08-08 00:00:00 |
19 | 狄仁杰 | 17799990018 | [email protected] | 国际贸易 | 30 | 1 | 0 | 2007-03-12 00:00:00 |
20 | 安琪拉 | 17799990019 | [email protected] | 城市规划 | 51 | 2 | 0 | 2001-08-15 00:00:00 |
21 | 典韦 | 17799990020 | [email protected] | 城市规划 | 52 | 1 | 2 | 2000-04-12 00:00:00 |
22 | 廉颇 | 17799990021 | [email protected] | 土木工程 | 19 | 1 | 3 | 2002-07-18 00:00:00 |
23 | 后羿 | 17799990022 | [email protected] | 城市园林 | 20 | 1 | 0 | 2002-03-10 00:00:00 |
24 | 姜子牙 | 17799990023 | [email protected] | 工程造价 | 29 | 1 | 4 | 2003-05-26 00:00:00 |
tb_user1表中索引如下所示:
Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment Index_comment Visible Expression
tb_user1 0 PRIMARY 1 id A 24 BTREE YES
tb_user1 0 idx_tb_user1_phone 1 phone A 24 BTREE YES
tb_user1 1 idx_tb_user1_name 1 name A 24 BTREE YES
tb_user1 1 idx_tb_user1_pro_age_sta 1 profession A 16 YES BTREE YES
tb_user1 1 idx_tb_user1_pro_age_sta 2 age A 22 YES BTREE YES
tb_user1 1 idx_tb_user1_pro_age_sta 3 phone A 24 BTREE YES
其中有我们之前创建的联合索引idx_tb_user1_pro_age_sta,字段及顺序profession,age,status
测试开始
执行如下sql语句
explain select * from tb_user1 where profession='软件工程' and age=31 and `status`='0';
-- 结果
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE tb_user1 ref idx_user1_profession_age_sta idx_user1_profession_age_sta 54 const,const,const 1 100.00
执行如下sql语句
explain select * from tb_user1 where profession='软件工程' and age=31;
-- 查询结果
1 SIMPLE tb_user1 ref idx_user1_profession_age_sta idx_user1_profession_age_sta 49 const,const 1 100.00
执行如下sql语句
explain select * from tb_user1 where profession='软件工程';
-- 查询结果
1 SIMPLE tb_user1 ref idx_user1_profession_age_sta idx_user1_profession_age_sta 47 const 4 100.00
执行如下sql语句
explain select * from tb_user1 where age=31 and `status`='0';
-- 查询结果
1 SIMPLE tb_user1 ALL 24 4.17 Using where
执行如下sql语句
explain select * from tb_user1 where profession='软件工程' and `status`='0';
-- 查询结果
1 SIMPLE tb_user1 ref idx_user1_profession_age_sta idx_user1_profession_age_sta 47 const 4 10.00 Using index condition
执行如下sql语句
explain select * from tb_user1 where `status`='0' and profession='软件工程' and age=31;
-- 查询结果
1 SIMPLE tb_user1 ref idx_user1_profession_age_sta idx_user1_profession_age_sta 54 const,const,const 1 100.00
联合索引中,出现范围查询(>,<),范围查询右侧的列索引失效。
以上述表为例
执行sql语句
explain select * from tb_user1 where profession='软件工程' and age>30 and `status`='0';
-- 查询结果
1 SIMPLE tb_user1 range idx_user1_profession_age_sta idx_user1_profession_age_sta 49 2 10.00 Using index condition
执行sql语句
explain select * from tb_user1 where profession='软件工程' and age>=30 and `status`='0';
-- 查询结果
1 SIMPLE tb_user1 range idx_user1_profession_age_sta idx_user1_profession_age_sta 54 2 10.00 Using index condition
索引列运算:不要在索引列上进行运算操作,否则索引将失效
我们想查询手机号后两位为15的人员信息
explain select * from tb_user1 where SUBSTR(phone,10,2)='15';
-- 查询结果
1 SIMPLE tb_user1 ALL 24 100.00 Using where
字符串不加单引号:字符串类型字段使用时,不加引号,单列索引将失效,联合索引当前列及之后索引列失效。
explain select * from tb_user1 where phone = 17799990002;
-- 查询结果
1 SIMPLE tb_user1 ALL idx_tb_user1_phone 24 10.00 Using where
explain select * from tb_user1 where phone = '17799990002';
-- 查询结果
1 SIMPLE tb_user1 const idx_tb_user1_phone idx_tb_user1_phone 46 const 1 100.00
explain select * from tb_user1 where profession='软件工程' and age=31 and `status`=0;
-- 查询结果
1 SIMPLE tb_user1 ref idx_user1_profession_age_sta idx_user1_profession_age_sta 49 const,const 1 10.00 Using index condition
-- 虽然使用了索引但是status 字段没加单引号该列索引失效
模糊匹配:如果仅仅是尾部模糊匹配,索引不会失效。如果是头部模糊匹配,索引失效。
explain select * from tb_user1 where profession like '软件%';
-- 查询结果
1 SIMPLE tb_user1 range idx_user1_profession_age_sta idx_user1_profession_age_sta 47 4 100.00 Using index condition
explain select * from tb_user1 where profession like '%工程';
-- 查询结果
1 SIMPLE tb_user1 ALL 24 11.11 Using where
or链接的条件:用or链接的条件,如果任一条件没有索引,那么所有索引都不会生效;当所有条件列都有索引,索引才会生效;
explain select * from tb_user1 where age=20 or id=1;
-- 查询结果
1 SIMPLE tb_user1 ALL PRIMARY 24 13.75 Using where
explain select * from tb_user1 where id=20 or phone='1111';
-- 查询结果
1 SIMPLE tb_user1 index_merge PRIMARY,idx_tb_user1_phone PRIMARY,idx_tb_user1_phone 4,46 2 100.00 Using union(PRIMARY,idx_tb_user1_phone); Using where
如果MySQL评估使用索引比全表慢,则使用索引。
explain select * from tb_user1 where phone >= '17799990012';
-- 查询结果
1 SIMPLE tb_user1 range idx_tb_user1_phone idx_tb_user1_phone 46 12 100.00 Using index condition
explain select * from tb_user1 where phone >= '17799990011';
-- 查询结果
1 SIMPLE tb_user1 ALL idx_tb_user1_phone 24 54.17 Using where
数据分布对null和not null影响
-- 表中profession 字段值全部非null
explain select * from tb_user1 where profession is null;
-- 查询结果
1 SIMPLE tb_user1 ref idx_user1_profession_age_sta idx_user1_profession_age_sta 47 const 1 100.00 Using index condition
-- 把professon 字段全部置为null,在此直线上述查询,结果
1 SIMPLE tb_user1 ALL idx_user1_profession_age_sta 24 100.00 Using where
现在我们在tb_user1表的profession
字段上有单列索引和联合索引,默认使用哪个索引呢?
explain select * from tb_user1 where profession='软件工程';
-- 查询结果
1 SIMPLE tb_user1 ref idx_user1_profession_age_sta,idx_user1_pro idx_user1_profession_age_sta 47 const 4 100.00
默认使用的联合索引
use index:建议使用索引
explain select * from tb_user1 use index(idx_user1_pro) where profession='软件工程';
-- 查询结果
1 SIMPLE tb_user1 ref idx_user1_pro idx_user1_pro 47 const 4 100.00
ignore index:忽略索引
explain select * from tb_user1 ignore index(idx_user1_pro) where profession='软件工程';
-- 查询结果
1 SIMPLE tb_user1 ref idx_user1_profession_age_sta idx_user1_profession_age_sta 47 const 4 100.00
force index:强制使用索引
explain select * from tb_user1 FORCE index(idx_user1_pro) where profession='软件工程';
-- 查询结果
1 SIMPLE tb_user1 ref idx_user1_pro idx_user1_pro 47 const 4 100.00
覆盖索引是一种优化技术,可以提高查询性能,特别是在查询涉及到大量数据列时。当一个查询可以直接使用索引来满足查询需求,而无需访问实际的数据行时,就称之为覆盖索引。
覆盖索引的优势在于,它可以减少磁盘I/O和CPU开销,因为数据库不需要访问实际的数据行,而只需读取索引所包含的列数据即可完成查询。这对于大型数据表和复杂查询特别有用,可以显著提高查询性能。
要创建一个覆盖索引,需要确保索引包含了查询所需的所有列。通常,只需要在SELECT子句中列出需要的列,而不需要SELECT *(选择所有列)。然后,创建一个多列索引,包括查询所需的列。当查询时,MySQL将使用该索引来直接满足查询需求。
需要注意的是,覆盖索引在某些情况下可能不适用或效果有限。例如,如果查询涉及大量的列或复杂的计算,覆盖索引可能无法满足查询需求。此外,过多的索引可能会增加写操作的开销,因为每次修改数据时,都需要更新索引。因此,在创建索引时需要权衡索引的数量和覆盖的列,以及对读写性能的影响。
MySQL的回表查询是在使用非覆盖索引的情况下,当需要获取查询结果中的列不在索引中时,MySQL需要通过回表操作来获取缺失的列数据。
当执行一个查询时,MySQL会首先使用索引定位到满足查询条件的行,但索引中只包含了部分列的数据。如果查询结果需要包含其他列,MySQL就需要通过回表操作去主键索引或聚集索引中查找并获取这些列的数据。
回表查询的过程可以分为两个步骤:
- 使用索引定位:MySQL首先使用非覆盖索引定位到满足查询条件的行,这一步的效率相对较高,因为索引的数据量通常比实际数据行少。
- 回表获取数据:MySQL通过找到的索引中的主键或聚集索引的值,再去主键索引或聚集索引中查找并获取查询结果所需的其他列数据。这一步需要额外的I/O操作,因为需要读取实际的数据行。
示例如下:
explain select id, profession, age, status from tb_user1 where profession='软件工程' and age=31 and `status`='0';
-- 查询结果
1 SIMPLE tb_user1 ref idx_user1_profession_age_sta idx_user1_profession_age_sta 54 const,const,const 1 100.00 Using index
explain select profession, email from tb_user1 where profession = '软件工程';
-- 查询结果
1 SIMPLE tb_user1 ref idx_user1_profession_age_sta idx_user1_profession_age_sta 47 const 4 100.00
explain select profession from tb_user1 where status = '0';
-- 查询结果
1 SIMPLE tb_user1 index idx_user1_profession_age_sta idx_user1_profession_age_sta 54 24 10.00 Using where; Using index
MySQL的前缀索引是一种索引技术,它允许你为索引的列指定一个前缀长度,而不是使用完整的列值作为索引。通过指定较短的前缀长度,可以减小索引的大小,提高查询性能和减少存储空间的需求。
前缀索引的主要优势在于减小了索引的大小,因为只存储了列值的前缀而不是完整的列值。这样可以减少磁盘I/O和内存使用,特别是在具有大量数据和较长列的表中,对查询性能的提升较为显著。
前缀长度
语法
create index xxx on table_name(column(长度));
选择性查看:
select count(distinct SUBSTRING(email,1,10))/count(*) from tb_user1;
-- 查询结果
1.0000
select count(distinct SUBSTRING(email,1,5))/count(*) from tb_user1;
-- 查询结果
0.9583
select count(distinct SUBSTRING(email,1,4))/count(*) from tb_user1;
-- 查询结果
0.9167
create index idx_email_5 on tb_user1(email(5));
-- 索引结果
tb_user1 1 idx_email_5 1 email A 23 5 YES BTREE YES
-- 查询示例
explain select * from tb_user1 where email like '17799%';
1 SIMPLE tb_user1 range idx_email_5 idx_email_5 23 2 100.00 Using where
单列索引和联合索引是在MySQL中用于优化查询性能的两种索引类型。
单列索引(Single-Column Index): 单列索引是指只针对单个列创建的索引。它可以加速根据该列进行等值比较(例如"=“)或范围比较(例如”<“、”>")的查询。当查询涉及到单个列时,单列索引是最常见和简单的索引类型。
联合索引(Composite Index): 联合索引是指基于多个列创建的索引,也称为复合索引或多列索引。它可以加速涉及到联合索引中的多个列的查询条件的查询。当查询条件涉及到多个列时,联合索引可以提供更好的查询性能。
示例
-- 两个单列索引情况
explain select id, phone, name from tb_user1 where name='项羽' and phone='17799990008';
-- 查询结果
1 SIMPLE tb_user1 const idx_tb_user1_phone,idx_tb_user1_name idx_tb_user1_phone 46 const 1 100.00
-- 同一字段上既有单列索引又联合索引时,可以指定索引 见## 3 SQL提示
联合索引的创建顺序非常重要。MySQL使用联合索引按照索引的列顺序进行排序和存储数据。因此,如果查询条件只涉及到联合索引的前缀列,那么MySQL可以有效地使用该索引。这就是最左前缀法则,可以帮助优化查询性能。
需要注意的是,使用过多的索引可能会增加写操作的开销,因为每次修改数据时,都需要更新索引。因此,在创建索引时需要谨慎权衡索引的数量和覆盖的列,以及对读写性能的影响。
综上所述,单列索引和联合索引都是优化查询性能的重要工具。选择何种索引类型,取决于查询的特点和需求。在设计和创建索引时,需要考虑查询模式、数据分布和性能需求等因素,以获得最佳的查询性能。
在设计索引时,可以考虑以下原则来提高查询性能和减少存储空间的需求:
综上所述,索引设计是一个权衡和优化的过程。需要根据具体的应用需求、数据分布和查询模式来选择合适的索引策略。理解数据和查询的特点,并遵循上述原则,可以提高查询性能和优化数据库的使用。
如果小伙伴什么问题或者指教,欢迎交流。
❓QQ:806797785
参考链接:
[1]MySQL数据库视频[CP/OL].2020-04-16.p79-88.