Mysql索引整理总结

做一个积极的人

编码、改bug、提升自己

我有一个乐园,面向编程,春暖花开

分享一下大神老师的人工智能教程。零基础!通俗易懂!风趣幽默(偶尔开开车,讲讲黄段子)!
大家可以看看是否对自己有帮助,如果你对人工智能感兴趣,希望你也加入到我们人工智能的队伍中来,点击这里查看【人工智能教程】。接下来进入正文。

一、索引概述

1. 简介

索引是对数据库表中一列或多列的值进行排序的一种结构,使用索引可快速访问数据库表中的特定信息。

举例说明索引:如果把数据库中的某一张看成一本书,那么索引就像是书的目录,可以通过目录快速查找书中指定内容的位置,对于数据库表来说,可以通过索引快速查找表中的数据。

2. 索引的原理

索引一般以文件形式存在磁盘中(也可以存于内存中),存储的索引的原理大致概括为以空间换时间,数据库在未添加索引的时候进行查询默认的是进行全量搜索,也就是进行全局扫描,有多少条数据就要进行多少次查询,然后找到相匹配的数据就把他放到结果集中,直到全表扫描完。而建立索引之后,会将建立索引的KEY值放在一个n叉树上(BTree)。因为B树的特点就是适合在磁盘等直接存储设备上组织动态查找表,每次以索引进行条件查询时,会去树上根据key值直接进行搜索。

3. 索引的优点

建立索引的目的是加快对表中记录的查找或排序!

① 建立索引的列可以保证行的唯一性,生成唯一的rowId

② 建立索引可以有效缩短数据的检索时间

③ 建立索引可以加快表与表之间的连接

④ 为用来排序或者是分组的字段添加索引可以加快分组和排序顺序

4. 索引的缺点

① 创建索引和维护索引需要时间成本,这个成本随着数据量的增加而加大

② 创建索引和维护索引需要空间成本,每一条索引都要占据数据库的物理存储空间,数据量越大,占用空间也越大(数据表占据的是数据库的数据空间)

③ 会降低表的增删改的效率,因为每次增删改索引需要进行动态维护,导致时间变长

二、索引的使用场景

数据库中表的数据量较大的情况下,对于查询响应时间不能满足业务需求,可以合理的使用索引提升查询效率。

三、索引的分类和创建和修改删除等命令

1. 基本索引类型

① 普通索引(单列索引)

② 复合索引(组合索引)

③ 唯一索引

④ 主键索引

⑤ 全文索引

2. 创建的语句

CREATE TABLE table_name[col_name data type]
[unique|fulltext][index|key][index_name](col_name[length])[asc|desc]

  • unique|fulltext为可选参数,分别表示唯一索引、全文索引

  • index和key为同义词,两者作用相同,用来指定创建索引

  • col_name为需要创建索引的字段列,该列必须从数据表中该定义的多个列中选择

  • index_name指定索引的名称,为可选参数,如果不指定,默认col_name为索引值

  • length为可选参数,表示索引的长度,只有字符串类型的字段才能指定索引长度

  • asc或desc指定升序或降序的索引值存储

3. 索引的创建、查询和删除

索引的创建
① 普通索引(单列索引)

普通索引(单列索引):单列索引是最基本的索引,它没有任何限制。

(1)直接创建索引

CREATE INDEX index_name ON table_name(col_name);

(2)修改表结构的方式添加索引

ALTER TABLE table_name ADD INDEX index_name(col_name);

(3)创建表的时候同时创建索引

CREATE TABLE `news` (
    `id` int(11) NOT NULL AUTO_INCREMENT ,
    `title` varchar(255)  NOT NULL ,
    `content` varchar(255)  NULL ,
    `time` varchar(20) NULL DEFAULT NULL ,
    PRIMARY KEY (`id`),
    INDEX index_name (title(255))
)

(4)删除索引

DROP INDEX index_name ON table_name;
或者
alter table `表名` drop index 索引名;

② 复合索引(组合索引)

复合索引:复合索引是在多个字段上创建的索引。复合索引遵守“最左前缀”原则即在查询条件中使用了复合索引的第一个字段,索引才会被使用。因此,在复合索引中索引列的顺序至关重要。

(1)创建一个复合索引

create index index_name on table_name(col_name1,col_name2,...);

(2)修改表结构的方式添加索引

alter table table_name add index index_name(col_name,col_name2,...);

③ 唯一索引

唯一索引:唯一索引和普通索引类似,主要的区别在于,唯一索引限制列的值必须唯一,但允许存在空值(只允许存在一条空值)

如果在已经有数据的表上添加唯一性索引的话:

  • 如果添加索引的列的值存在两个或者两个以上的空值,则不能创建唯一性索引会失败。(一般在创建表的时候,要对自动设置唯一性索引,需要在字段上加上 not null)
  • 如果添加索引的列的值存在两个或者两个以上的null值,还是可以创建唯一性索引,只是后面创建的数据不能再插入null值 ,并且严格意义上此列并不是唯一的,因为存在多个null值。

对于多个字段创建唯一索引规定列值的组合必须唯一。
比如:在order表创建orderId字段和 productId字段 的唯一性索引,那么这两列的组合值必须唯一!


“空值” 和”NULL”的概念: 
1:空值是不占用空间的 .
2: MySQL中的NULL其实是占用空间的.

长度验证:注意空值的之间是没有空格的。

> select length(''),length(null),length(' ');
+------------+--------------+-------------+
| length('') | length(null) | length(' ') |
+------------+--------------+-------------+
|          0 |         NULL |           1 |
+------------+--------------+-------------+

(1)创建唯一索引

# 创建单个索引
CREATE UNIQUE INDEX index_name ON table_name(col_name);

# 创建多个索引
CREATE UNIQUE INDEX index_name on table_name(col_name,...);

(2)修改表结构

# 单个
ALTER TABLE table_name ADD UNIQUE index index_name(col_name);
# 多个
ALTER TABLE table_name ADD UNIQUE index index_name(col_name,...);

(3)创建表的时候直接指定索引

CREATE TABLE `news` (
    `id` int(11) NOT NULL AUTO_INCREMENT ,
    `title` varchar(255)  NOT NULL ,
    `content` varchar(255)  NULL ,
    `time` varchar(20) NULL DEFAULT NULL ,
    PRIMARY KEY (`id`),
    UNIQUE index_name_unique(title)
)

④ 主键索引

主键索引是一种特殊的唯一索引,一个表只能有一个主键,不允许有空值。一般是在建表的时候同时创建主键索引:

(1)主键索引(创建表时添加)


CREATE TABLE `news` (
    `id` int(11) NOT NULL AUTO_INCREMENT ,
    `title` varchar(255)  NOT NULL ,
    `content` varchar(255)  NULL ,
    `time` varchar(20) NULL DEFAULT NULL ,
    PRIMARY KEY (`id`)
)

(2)主键索引(创建表后添加)

alter table tbl_name add primary key(col_name);

CREATE TABLE `order` (
    `orderId` varchar(36) NOT NULL,
    `productId` varchar(36)  NOT NULL ,
    `time` varchar(20) NULL DEFAULT NULL
)

alter table `order` add primary key(`orderId`);

⑤ 全文索引

在一般情况下,模糊查询都是通过 like 的方式进行查询。但是,对于海量数据,这并不是一个好办法,在 like “value%” 可以使用索引,但是对于 like “%value%” 这样的方式,执行全表查询,这在数据量小的表,不存在性能问题,但是对于海量数据,全表扫描是非常可怕的事情,所以 like 进行模糊匹配性能很差。

这种情况下,需要考虑使用全文搜索的方式进行优化。全文搜索在 MySQL 中是一个 FULLTEXT 类型索引。FULLTEXT 索引在 MySQL 5.6 版本之后支持 InnoDB,而之前的版本只支持 MyISAM 表

全文索引主要用来查找文本中的关键字,而不是直接与索引中的值相比较。fulltext索引跟其它索引大不相同,它更像是一个搜索引擎,而不是简单的where语句的参数匹配。fulltext索引配合match against操作使用,而不是一般的where语句加like。目前只有char、varchar,text 列上可以创建全文索引。

小技巧:
在数据量较大时候,先将数据放入一个没有全局索引的表中,然后再用CREATE index创建fulltext索引,要比先为一张表建立fulltext然后再将数据写入的速度快很多。

(1)创建表的适合添加全文索引

CREATE TABLE `news` (
    `id` int(11) NOT NULL AUTO_INCREMENT ,
    `title` varchar(255)  NOT NULL ,
    `content` text  NOT NULL ,
    `time` varchar(20) NULL DEFAULT NULL ,
     PRIMARY KEY (`id`),
    FULLTEXT (content)
)

(2)修改表结构添加全文索引

ALTER TABLE table_name ADD FULLTEXT index_fulltext_content(col_name)

(3)直接创建索引

CREATE FULLTEXT INDEX index_fulltext_content ON table_name(col_name)

注意: 默认 MySQL 不支持中文全文检索!

MySQL 全文搜索只是一个临时方案,对于全文搜索场景,更专业的做法是使用全文搜索引擎,例如 ElasticSearch 或 Solr。

索引的查询和删除
#查看:
show indexes from `表名`;
#或
show keys from `表名`;
 
#删除
alter table `表名` drop index 索引名;


注:MySQl的客户端工具也可以进索引的创建、查询和删除,如 Navicat Premium!

四、简单实例演示

查看索引使用情况

show status like ‘Handler_read%’;

handler_read_key:这个值越高越好,越高表示使用索引查询到的次数
handler_read_rnd_next:这个值越高,说明查询低效

常见索引失效的情况:

创建一个students表:
其中stud_id为主键!

DROP TABLE IF EXISTS `students`;
CREATE TABLE `students` (
  `stud_id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL,
  `email` varchar(50) NOT NULL,
  `phone` varchar(1) NOT NULL,
  `create_date` date DEFAULT NULL,
  PRIMARY KEY (`stud_id`)
 
)

INSERT INTO `learn_mybatis`.`students` (`stud_id`, `name`, `email`, `phone`, `create_date`) VALUES ('1', 'admin', 'student1@gmail.com', '18729902095', '1983-06-25');
INSERT INTO `learn_mybatis`.`students` (`stud_id`, `name`, `email`, `phone`, `create_date`) VALUES ('2', 'root', '74298110186@qq.com', '2', '1983-12-25');
INSERT INTO `learn_mybatis`.`students` (`stud_id`, `name`, `email`, `phone`, `create_date`) VALUES ('3', '110', '7429811086@qq.com', '3dsad', '2017-04-28');

使用 explain 查看 索引是否生效!Mysql中explain用法和结果字段的含义介绍

1. 在where后使用or,导致索引失效(尽量少用or)

简单实例演示:
创建两个普通索引,

CREATE INDEX index_name_email ON students(email);
CREATE INDEX index_name_phone ON students(phone);

使用下面查询sql,

# 使用了索引
EXPLAIN select * from students where stud_id='1'  or phone='18729902095'
# 使用了索引
EXPLAIN select * from students where stud_id='1'  or email='742981086@qq.com'

#--------------------------

# 没有使用索引
EXPLAIN select * from students where phone='18729902095' or email='742981086@qq.com'

# 没有使用索引
EXPLAIN select * from students where stud_id='1'  or phone='222' or email='742981086@qq.com'


2.使用like ,like查询是以%开头

在1的基础上,还是使用 index_name_email 索引。

使用下面查询sql

# 使用了index_name_email索引
EXPLAIN select * from students where email like '742981086@qq.com%'

# 没有使用index_name_email索引,索引失效
EXPLAIN select * from students where email like '%742981086@qq.com'

# 没有使用index_name_email索引,索引失效
EXPLAIN select * from students where email like '%742981086@qq.com%'
3.复合索引遵守“最左前缀”原则,即在查询条件中使用了复合索引的第一个字段,索引才会被使用

删除1的基础创建的 index_name_email 和 index_name_phone 索引。

重新创建一个复合索引:

create index index_email_phone on students(email,phone);

使用下面查询sql

# 使用了 index_email_phone 索引
EXPLAIN select * from students where email='742981086@qq.com' and  phone='18729902095'

# 使用了 index_email_phone 索引
EXPLAIN select * from students where phone='18729902095' and  email='742981086@qq.com'

# 使用了 index_email_phone 索引
EXPLAIN select * from students where email='742981086@qq.com' and name='admin'

# 没有使用index_email_phone索引,复合索引失效
EXPLAIN select * from students where phone='18729902095' and name='admin'

4. 如果列类型是字符串,那一定要在条件中将数据使用引号引用起来,否则不使用索引

给name创建一个索引!

CREATE INDEX index_name ON students(name);

# 使用索引
EXPLAIN select * from students where name='110'

# 没有使用索引
EXPLAIN select * from students where name=110

5. 使用in导致索引失效
# 使用索引
EXPLAIN select * from students where name='admin'

# 没有使用索引
EXPLAIN SELECT * from students where name in ('admin')

6. DATE_FORMAT()格式化时间,格式化后的时间再去比较,可能会导致索引失效。

删除 students 上的创建的索引!重新在create_date创建一个索引!

CREATE INDEX index_create_date ON students(create_date);

# 使用索引
EXPLAIN SELECT * from students where create_date >= '2010-05-05'

# 没有使用索引
EXPLAIN SELECT * from students where DATE_FORMAT(create_date,'%Y-%m-%d') >= '2010-05-05'


7. 对于order by、group by 、 union、 distinc 中的字段出现在where条件中时,才会利用索引!
8. 更多索引的使用注意可以参看这一篇博文:

索引使用注意规则(索引失效–存在索引但不使用索引)

五、总结

MySQL改善查询性能改善的最好方式,就是通过数据库中合理地使用索引!

一般当数据量较大的时候,遇到sql查询性能问题,首先想到的应该是查询的sql时候使用了索引,如果使用了索引性能还是提高不大,就要检查索引是否使用正确,索引是否在sql查询中生效了!

如果索引生效了,并且索引的使用也是合理的,最后sql性能还是不高,那就考虑重新优化sql语句!

六、参考博文

七 拓展阅读

MySQL大表优化方案

备注: 由于本人能力有限,文中若有错误之处,欢迎指正。
公众号有对应的mysql高级的视频教程,关注公众号 回复 【mysql】 获取!


谢谢你的阅读,如果您觉得这篇博文对你有帮助,请点赞或者喜欢,让更多的人看到!祝你每天开心愉快!


Java编程技术乐园:一个分享编程知识的公众号。跟着老司机一起学习干货技术知识,每天进步一点点,让小的积累,带来大的改变!

扫描关注,后台回复【资源】,获取珍藏干货! 99.9%的伙伴都很喜欢

image.png | center| 747x519

© 每天都在变得更好的阿飞云
<think>好的,我现在需要帮助用户总结MySQL索引失效的常见场景及原因。首先,我要回忆用户提供的引用内容,确保信息准确。用户提到了四个引用,涉及索引失效的不同情况,比如OR条件、范围查询后的失效、LIKE操作符的使用等。 首先,我应该整理这些引用中的关键点。引用[1]提到了索引失效的几种常见场景,但没有具体说明;引用[2]指出OR关键字导致失效的情况,特别是当其中一个字段没有索引使用范围查询时;引用[3]提到范围查询索引可能部分使用,但后续条件可能失效;引用[4]强调LIKE以通配符开头导致索引失效。 接下来,我需要将用户的问题结构化,分点说明常见的失效场景。每个场景需要包含原因和示例,这样用户更容易理解。例如,OR条件的情况,需要说明当OR连接的字段中有一个无索引时,整个查询会失效。或者使用LIKE以%开头时,无法利用索引,因为最左前缀原则。 同时,要注意用户提到的系统级指令,比如行内数学表达式用$...$,独立公式用$$...$$,但这个问题可能不需要数学公式,所以可能不需要使用。另外,用中文回答,结构清晰,分点列出。 然后,根据引用中的信息,我需要确保每个失效场景都有对应的引用标注。例如,当提到OR条件失效时,引用[2]和引用[3]。对于范围查询后的索引失效,引用[3]指出优化器可能部分使用索引,但后续条件可能失效。LIKE的情况引用[4]。 可能还需要补充其他常见场景,比如对索引使用函数或表达式,这会导致索引失效,但用户提供的引用中没有提到这点。不过用户的问题可能希望基于提供的引用,所以是否需要包括?根据引用内容,用户提供的引用里没有提到函数导致失效的情况,所以可能不在这里讨论。需要严格按照引用内容来组织答案。 检查是否有遗漏的引用内容。引用[2]还提到OR两边为范围查询时失效。引用[3]提到范围查询索引可能部分使用,但其他条件可能失效。引用[4]主要讲LIKE的通配符开头问题。引用[1]是结语,没有具体场景。 总结起来,用户提供的引用中的场景包括: 1. OR条件导致失效(引用2) 2. 范围查询后的索引失效(引用3) 3. LIKE以%开头导致失效(引用4) 此外,可能还有其他场景如不遵循最左前缀原则、数据类型隐式转换等,但根据用户提供的引用,可能需要集中在提到的几点。或者用户可能希望更全面的总结,但根据提供的引用,需要基于引用内容来回答。 不过,用户的问题是想了解常见的场景及原因,而提供的引用可能没有涵盖所有情况。但根据指令,回答中引用的段落末尾需要添加引用标识,所以必须确保每个点都有对应的引用来源。因此,我需要严格按照提供的四个引用中的信息来组织答案,可能无法涵盖其他常见场景如函数使用、数据类型转换等,因为引用中没有提到这些。 现在,开始组织回答结构: 第一部分:引言,说明索引失效的重要性,引用[1]的结语部分。 然后分点列出场景: 1. 使用OR条件导致索引失效(引用2) 2. 范围查询后的索引失效(引用3) 3. LIKE操作符以通配符开头(引用4) 4. 其他可能的场景,如果引用中有的话? 检查引用内容: 引用2提到OR条件,其中字段无索引或范围查询导致失效;引用3补充了范围查询后可能部分使用索引;引用4是LIKE的情况。引用1只是结语,没有具体场景。可能还有其他场景如联合索引不遵循最左前缀,但用户提供的引用中没有提到,所以可能不在回答范围内。 因此,主要分三个场景: 1. OR条件导致索引失效 2. 范围查询后的索引部分失效 3. LIKE操作符以通配符开头 每个场景需要详细说明原因,并附上示例和对应的引用。例如: 对于OR条件,当其中一个字段没有索引,整个查询无法使用索引,必须全表扫描,如select * from table where a=1 or b=2,如果b无索引,则索引失效(引用2)。此外,如果OR两边是范围查询,也会失效(引用2)。 范围查询后的情况,例如where a>10 and b=5,如果a和b是联合索引,a的范围查询导致b无法使用索引(引用3)。 LIKE的情况,如where name like '%abc',无法使用索引(引用4)。 确保每个点都有对应的引用标识。然后,在最后生成相关问题,如: 相关问题可能包括如何避免索引失效、如何优化查询语句、索引设计的最佳实践等。 现在,把这些整理成结构清晰的回答,使用中文,分点说明,并正确标注引用。</think>### MySQL索引失效常见场景及原因总结 MySQL索引失效会严重影响查询性能,了解常见场景可帮助优化数据库设计和查询语句[^1]。以下是主要场景及分析: --- #### 1. **使用`OR`条件导致索引失效** - **原因**: - 如果`OR`连接的字段中**至少有一个字段未建立索引**,则整个查询会退化为全表扫描。 - 即使所有字段都有索引,若`OR`两侧为范围查询(如`>`、`<`),索引也可能失效[^2]。 - **示例**: ```sql SELECT * FROM users WHERE age = 25 OR address = 'Beijing'; ``` 若`address`字段无索引,即使`age`有索引,整体查询仍无法使用索引。 --- #### 2. **范围查询后的索引部分失效** - **原因**: - 在联合索引中,如果对某一列进行范围查询(如`BETWEEN`、`>`),其后的列无法继续使用索引。 - MySQL优化器可能仅使用索引的部分条件,后续过滤需在内中完成[^3]。 - **示例**: ```sql SELECT * FROM orders WHERE create_time > '2023-01-01' AND status = 'paid'; ``` 若索引为`(create_time, status)`,范围查询`create_time > '2023-01-01'`会导致`status`无法利用索引。 --- #### 3. **`LIKE`操作符以通配符开头** - **原因**: - `LIKE`以`%`开头时(如`%abc`),违反**最左前缀匹配原则**,导致索引失效。 - 若通配符仅出现在末尾(如`abc%`),索引仍可能被使用[^4]。 - **示例**: ```sql SELECT * FROM products WHERE name LIKE '%apple%'; -- 索引失效 SELECT * FROM products WHERE name LIKE 'apple%'; -- 可能使用索引 ``` --- #### 4. **其他常见场景** - **数据类型隐式转换**: 若查询条件与索引字段类型不一致(如字符串字段使用数字值),会导致索引失效。 ```sql SELECT * FROM users WHERE phone = 13800138000; -- phone为VARCHAR类型 ``` - **对索引使用函数或表达式**: 对索引列进行运算或函数调用(如`WHERE YEAR(create_time) = 2023`)会破坏索引结构。 --- ### 优化建议 1. 避免在`WHERE`子句中使用`OR`,改用`UNION`拆分查询。 2. 合理设计联合索引的顺序,将等值查询列放在范围查询列之前。 3. 对高频查询字段建立覆盖索引,减少回表操作。 ---
评论 21
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值