索引的定义与作用
索引
是数据库管理系统中用于提高查询速度
的一种数据结构
。在MySQL中,索引可以看作是一种特殊的表,其中包含了对数据表中特定列的值及其在数据表中的位置信息。
它就像是一本书的目录或字典,允许我们在很短的时间内找到所需的信息。 在数据库环境中,索引可以大大加快数据检索速度,减少处理时间和磁盘I/O次数。此外,它还可以确保对数据库记录的操作(如INSERT、UPDATE和DELETE)的完整性。因此,在设计数据库应用程序时,应充分考虑并合理利用索引技术。
索引的创建
建表时创建
create table users(
id int unsigned auto_increment primary key,
username varchar(50) not null,
password varchar(11) not null
)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
在创建表时,在表中定义一个primary key
约束时,MySQL会自动为我们创建一个相应的主键索引。这也是所谓的隐式创建
,可以通过如下语句来进行查看。
show index from users;
建表后添加
当然,除了在创建表的时候定义,你也可以在已经定义好的表中进行添加。
create index username_index on users(username);
以上语句就为users
表又添加了一个普通索引(单列索引)
或者通过alter语句进行索引的创建。
alter table users add index indexName(password);
# ALTER TABLE `tableName` ADD PRIMARY KEY ( `column` )
# 添加主键索引
# ALTER TABLE `tableName` ADD UNIQUE (`column`)
# 添加唯一索引
# ALTER TABLE `tableName` ADD INDEX index_name (`column1`,`column2`)
# 添加组合索引
以上就又创建了一个名为indexName
的普通索引,同时列举了主键索引跟唯一索引的创建方式。
在创建索引方面,ALTER TABLE 和 CREATE INDEX 都可以达到相同的效果,但它们之间存在一些微妙的区别。例如,ALTER TABLE 可以同时执行多个更改,而 CREATE INDEX 则只能用于创建单个索引。
删除索引
在优化表时,如果遇到需要删除索引时,MySql也是支持的,可通过如下语句实现:
# 删除索引
# DROP INDEX index_name ON table_name;
DROP INDEX username_index ON users;
这里我们就将刚刚创建的单列索引给删除了,只剩下最初建表时创建的主键索引。
被动创建
那如果我们在创建表的时候不设置索引,在后续也不给表添加索引,那该表是否就没有索引呢,它的查询效率是否就会很低?
从MySql 8.0.30
开始,MySQL支持为没有显式主键的InnoDB表
生成不可见的主键。当sql_generate_invisible_primary_key
服务器系统变量设置为ON时,MySQL服务器会自动将生成的不可见主键(GIPK)添加到任何这样的表中。
- 查询对应设置
SELECT @@sql_generate_invisible_primary_key;
该属性的默认值为:0。不开启,也就是说默认的情况下,MySql不会为没有创建的表创建主键(索引)。
create table demos(
username varchar(50) not null,
password varchar(11) not null
)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
# show index from demos;
未将sql_generate_invisible_primary_key
的值设为on
的情况下,是不会为其添加主键的。
接下来我们将其设置为on,再重新创建表看看。
SET sql_generate_invisible_primary_key=ON;
| @@sql_generate_invisible_primary_key | ------------------------------------ 0 |
create table demos(
username varchar(50) not null,
password varchar(11) not null
)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
# show index from demos;
此时,就可以看到该表在创建成功后,自动有了一个名为PRIMARY
的主键
相关链接:
MySql-官网文档
通过索引定位数据
MySQL常用的InnoDB
引擎中,还是使用 B+树索引
比较多。以下是MySql使用B+树进行数据存储的一个简略结构,
真实场景中最底层中每个节点(叶子节点
)都有若干条数据,并且用其中的最小值作为一个标识符,而中间的节点则是存储的最底层中每个节点的最大值(当然其中的数据也有若干条),并用其中最大值作为标识符。而最上层的节点就是根节点
,它存储的就是第二层中的子节点的标识符跟其他一些信息。
查找过程
比如我们去找43这个数,
- 首先在跟节点进行比较,发现应该去最大的那个子节点图中为0033去查找
- 接着在0033节点中跟内部元素进行比较,发现需要去最大的叶子节点中查找
- 最后在叶子节点0033-0042中查找,经过一番比较过,没有找到,于是返回结果:没有该元素
以上就是如何通过主键索引
查询数据的过程,每个数据节点存储的数据具体有些哪些这里就不展开了。
提示:使用InnoDB引擎创建
主键索引
,叶子节点存储
了我们完整的用户记录
。
聚簇索引跟非聚簇索引
使用InnoDB 引擎创建的主键索引也叫聚簇索引
(Clustered Index)。聚簇索引是指:将整个表的数据都存储在一个 B+树结构中的索引。该索引组织了表中的数据,使它们按照主键值排序并存储在一个 B+树中。聚簇索引的特点:是可以快速访问指定范围内的记录,因为不需要多次查找就可以检索到所需要的数据。此外还有聚簇索引的叶子节点存储的是表中的数据
。
此外还有非聚簇索引
,在MySql中,非聚簇索引是指在 InnoDB 引擎中除了主键索引以外的所有其他索引类型,如:唯一索引,组合索引...非聚簇索引不同于聚簇索引,它不包含表的实际数据,而是指向数据的指针。当需要从非聚簇索引中检索数据时,MySQL 需要执行一次回表
操作,以便访问聚簇索引并将数据返回给客户端。非聚簇索引也叫二级索引,或者叫辅助索引。它叶子结点存储
的则是主键值
跟索引列
。
使用非聚簇索引查找数据的过程一般情况如下:
从非聚簇索引转到聚簇索引的这个过程就叫做回表
。
注意:通过非聚簇索引查询不是一定会发生回表,如果查询完全符合非聚簇索引的条件,则可以直接从非聚簇索引中检索出所需的记录。在这种情况下,无需进一步回表即可完成查询。
索引的一些类型
- 主键索引:设定为主键后,数据库自动建立索引,InnoDB为聚簇索引,主键索引列值不能为空(Null)
- 唯一索引:索引列的值必须唯一,但允许有空值(Null),但只允许有一个空值(Null)
- 复合索引:一个索引可以包含多个列,多个列共同构成一个复合索引
- 全文索引:全文索引是一种特殊类型的索引,用于在文本数据上进行高效的全文搜索。
- 空间索引: MySQL 提供的一种用于索引地理位置数据的技术,它使用 R 树结构来提高查询性能。
索引失效的情况
查看一个语句是否走索引,可以通过如下sql进行查看
#explain select * from tableName where 条件
explain select * from users where password="222"
若对应的key有值,则为查询时走了索引。
不满足最左匹配原则
使用联合索引时,比如我们建立了name, age, phone 三个字段的联合索引。
alter table users add index uni_index(name,age,phone);
# 不走索引
explain select * from users where age=12 and phone= 1298672
因为最左优先原则:会先比较name属性,再接着age,最后phone,如没有,则无法匹配
所以如果where age = 12,则不会生效,而where age = 12 and name="test"则会生效。或者说只有(a),(a,b),(a,b,c)三种查询可以生效。
使用不等于(!= 、<>)
explain select * from user where age != 20;
通常情况来说,使用不等于是不走索引的,但也有另外,MySQL中有一个叫做优化器
的东西,他会对每一条查询sql做成本分析,然后根据分析结果选择是否使用索引或者全表扫描。
这里举2种情况,如果不等于20的记录跟等于20的记录,数量都比较多,则成本很高,不会走索引。但如果本身记录很少,比如一共只有几条,则会走索引,或者不等于2的仅有10条,也会走索引。
总结:使用不等于的情况下,不一定不走索引,而是看成本,如果走索引成本不大,也会走索引,否则不会。
“OR”前后存在非索引列,导致索引失效
当where语句中使用or
操作符并且两边的条件至少涉及两个字段,且字段中存在非索引列
时,MySQL无法使用索引,会转向全表扫描。
index_merge: OR前后的两个条件中的列都是索引时,查询中才使用索引,索引类型是“index_merge”,将两个索引字段分别扫描,然后合并。
explain select * from diy.users where age =88 or hobby ='看电影';
# 使用了索引
explain select * from diy.users where age =88 or hobby ='看电影' or password='123'
# 以上语句 则没有使用索引
在上述两个语句中,因为age,hobby都使用了索引,所以第一个sql走了索引,而password没有索引,所以第二个sql不会走索引。
like中用通配符开头('%xxx')
# 在一个表中创建一个索引
alter table diy.users add index h_index(hobby);
# 以下语句 使用了索引
explain select * from diy.users where hobby like '看%'
# 未使用索引 花费时间更久
explain select * from diy.users where hobby like '%看'
%通配符:表示任意字符,可以匹配任意的字符数量。 _通配符:表示任意单个字符。
使用Order By
explain select * from diy.users order by id,age;
以上sql语句,order后跟了2个字段,其中id是主键,但age没建立索引,则在查询时不会走索引。这是因为:ORDER BY子句中的列与索引列不完全匹配(即索引列包含部分数据而非完整列),所以MySQL可能无法有效地利用索引进行排序。
通常来说,order by后跟的列只要是索引列就会走索引,但有时即使order by后跟的列是索引列,也会发生索引失效的情况,这是因为
优化器
的作用,可能是由于查询优化器的估算成本较高,或者使用文件排序的开销较小等原因。
在索引列上进行计算、函数
现有一个需求,找出name为ty开头的字段信息:
create index idx_name on demos(name);
# 对索引列执行函数计算
explain select * from demos where left(name,2)='ty'
字段使用函数会让MySql中的优化器无法生效,因为函数的结构可能和B+树中的值不匹配,所以不会走索引。字段能不用函数就不用函数。
Select *
select * from tableName
使用select * 查询,不加任何条件,也不会走索引,直接返回全部结果,没有用到任何索引,查询效率很低
还有其它的一些索引失效的情况,这里只列举了一些常用的。
优缺点与建议
优点:它类似于书的目录,降低了数据库IO成本,大大提高了数据查询效率
缺点:索引也是数据,因此会占据一些磁盘空间,同时由于索引跟数据存在一定关联关系,在改动数据时,也需要同时对它进行更新,一定程度上减低了更新表的效率(每次对表进行增删改查,MySQL不仅要保存数据,还有保存或者更新对应的索引文件)。
总结:索引确实能提高查询效率,但并非每次查询都会奏效,因为它独特的数据结构,有时也会存在失效的情况。索引其实就是用
空间换取时间
的典型例子。数据库表中不是索引越多越好,而是根据情况为那些常用的搜索字段建立索引,效果才是最佳!
一些扩展资料
菜鸟教程-MySql索引
数据结构可视化网站