14. 索引

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: 14. 索引

什么是索引?

类似字典或者电话簿里的目录,可以加速查询。 多数情况下,索引很小,可以放进内存里。

但是索引也有代价,比如增加数据库的大小,因为他们必须永久存储在表旁边;每次添加、修改和删除时,MySQL必须更新对应的索引,这会影响正常操作的性能

所以我们应该为性能关键的表创建索引,因为索引的最终目的是加速查询,如果基于表的设计为所有表添加索引,是浪费资源的,不要盲目创建索引。

索引在内存中通常表示为二叉树。

创建索引

sql

复制代码

explain select customer_id from customers where state = 'CA';

执行上面语句后,会返回如下信息:

id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE customers ALL 1012 10.00 Using where
  • type 表示当前的查询是 ALL,即会在查询的表中遍历所有的记录,全表扫描
  • rows 表示遍历查询总共有 1012 行 显然如果用户数量增加,遍历的查询会越来越慢。

sql

复制代码

create index idx_state on customers (state);

通过上面的命令可以创建一个索引,此时再去解释查询语句,就会变成下面:

|id|select_type|table|partitions|type|possible_keys|key|key_len|ref|rows|filtered|Extra| |---|---|---|---|---|---|---|---|---|---|---|---|---| |1|SIMPLE|customers||ref|idx_state|idx_state|8|const|112|100.00|Using where; Using index|

  • type 是 ref,没有再做全表扫描
  • rows 中扫描的记录条数明显减少
  • possible_keys 表示 MySQL执行查询时可能会考虑的索引,可能会有多个索引
  • key MySQL实际使用的索引

练习

sql

复制代码

explain select customer_id from customers where points > 1000;
create index idx_points on customers (points);

id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE customers range idx_points idx_points 4 529 100.00 Using where; Using index

查看索引

sql

复制代码

show indexes in customers;
Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment Index_comment Visible Expression
customers 0 PRIMARY 1 customer_id A 12 BTREE YES
customers 1 idx_state 1 state A 48 BTREE YES
customers 1 idx_points 1 points A 789 BTREE YES
  • Key_name 表示索引名称 第一个 PRIMARY 表示聚集索引,每个表自动有的主键默认是聚集索引,每个表也只有一个聚集索引第二个和第三个都是二级索引还有一种是外键,也是二级索引,可以快速链接到别的表上
  • Column_name 表示索引在的列上
  • Collation 表示数据在索引中的排序规则,A表示升序,D表示降序
  • Cardinality 表示索引中唯一值的估计数量,不是实际数量,要想获得实际数量,可以先执行analyze table customers
  • Index_type 表示索引类型 BTREE 表示二分树

前缀索引

当索引的列是字符串时(包括 CHAR、VARCHAR、TEXT、BLOG),尤其是当字符串较长时,我们通常不会使用整个字符串而是只是用字符串的前面几个字符来建立索引,这被称作 Prefix Indexes 前缀索引,这样可以减少索引的大小使其更容易在内存中操作

sql

复制代码

create index idx_lastname on customers (last_name(20))

上面是创建前缀索引的方法,但是如何选取前缀的值呢?

  • 如果值太少,达不到彻底加速查询的目的,相同前缀的结果还是会遍历查找
  • 如果值太多,会造成空间和时间的浪费 所以必须找到一个恰当的值设置为前缀索引长度

sql

复制代码

select 
  count(distinct left(last_name,1)), -- 25
  count(distinct left(last_name,5)), -- 967
    count(distinct left(last_name,10)) -- 997
from customers;

执行的目的是用找到一个最小的数,通过最少的前缀字符,能够匹配出尽可能多的原文中的记录 显然在这里 5 最优,因为 1 得到的太少,而 10 增长的数据量有限。

全文索引

sql

复制代码

select *
from posts
where title like '%react redux%' or
  body like '%react redux%';

像这种搜索关键词的查询使用like并不方便,一是没有索引会导致全表扫描,效率低下;二是返回结果只会包含关键词,不符合搜索引擎的效果。

通过建立 全文索引 来实现搜索引擎的搜索 建立全文索引:

sql

复制代码

CREATE FULLTEXT INDEX idx_title_body ON posts (title, body);

利用全文索引,结合 MATCH 和 AGAINST 进行 google 式的模糊搜索:

sql

复制代码

select *,match(title,body) against('react redux')
from posts
where match(title,body) against('react redux');

注意:MATCH后的括号里必须包含全文索引 idx_title_body 建立时相关的所有列,不然会报错

全文检索有两个模式:

  • 自然语言模式,自然语言模式是默认模式,也是上面用到的模式。
  • Boolean模式,可以更明确地选择包含或排除一些词汇(google也有类似功能)

sql

复制代码

select *,match(title,body) against('react redux')
from posts
where match(title,body) against('react -redux +form' in boolean mode);
  • in boolean mode 表示使用Boolean模式
  • - 表示排除后面的关键词,+表示必须出现后面的关键词,使用双引号 "" 表示必须准确包含这个短语

总结

需要全文搜索的字段,比如描述,文章内容等 不需要全文搜索的字段,比如姓名,地址,使用前缀索引就够了

复合索引

sql

复制代码

show indexes in customers;
explain select customer_id from customers
where state = 'CA' and points > 1000;
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE customers ref idx_state,idx_points idx_state 8 const 112 52.27 Using index condition; Using where

通过上面的语句的执行结果可以发现,MySQL 只使用了一个state上的索引,所以

  • MySQL 不管一条查询条件会有多少个索引,只会选择一个索引使用
  • 对于索引之外的列,依然是使用全部遍历的方法来查找,但后半部分的数据量非常大的时候,索引的效率会降低。

复合索引可以同时对多列创建索引

sql

复制代码

create index idx_state_points on customers (state,points);

再次执行上面解释查询语句,可以明显发现查询的数量减少了(rows 从112 到 58),效率提高了

id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE customers range idx_state,idx_points,idx_state_points idx_state_points 12 58 100.00 Using where; Using index

在 possible_keys 上可以发现有三个索引,当时 MySQL 发现复合索引效率最高,所以自动选择了他。

注意:MySQL 会给每个索引自动加上主键。 所以过多的索引会占用大量储存空间,而且数据每次数据更新都会重建索引,所以过多的索引也会拖慢更新速度实际中更多的是用到组合索引,一个组合索引最多可组合 16 列,根据实际的查询需求和数据量来考虑复合索引的列数

删除索引

sql

复制代码

drop index idx_state on customers;
drop index idx_points on customers;

复合索引中列的顺序

有两条基本规则:

  1. 需要对列进行排序,让更频繁使用的列在前面。这有助于缩小查询范围
  2. 把基数更高的列排在前面,基数表示索引中唯一值的数量,可以把表分割成在尽可能小,最大限度地缩小查询数量 这两条规则不是固定的,必要时还是具体问题具体分析。

sql

复制代码

-- 先给last_name索引,再给state索引
create index idx_lastname_state on customers
(last_name, state);
-- 先给state索引,再给last_name索引
create index idx_state_lastname on customers
(state,last_name);
explain select customer_id from customers
where state = 'CA' and last_name like 'A%';
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE customers range idx_lastname,idx_state_points,idx_lastname_state,idx_state_lastname idx_state_lastname 210 7 100.00 Using where; Using index

MySQL 决定使用第二个索引,它要扫描的数量最少。

强制规定使用那个索引

sql

复制代码

explain select customer_id from customers
use index (idx_lastname_state)
where state = 'CA' and last_name like 'A%';

高效索引的建议

对于查询语句,虽然where last_name like 'A%' and state = 'CA'where state = 'CA' and last_name like 'A%'的执行结果类似。like的范围模糊,=的约束性更高

但是后者的第一步查询使用索引可以将范围限制的更小,约束性更高,所以执行的效率更高

sql

复制代码

explain select customer_id from customers
use index (idx_lastname_state)
where state like 'A%' and last_name like 'A%'; -- 41行
explain select customer_id from customers
use index (idx_state_lastname)
where state like 'A%' and last_name like 'A%'; -- 51行

通过上面的例子可以发现,对于一个查询,应该是约束性越强的放在前面,当 state 从约束性高的=变为约束性较低的like后,执行idx_state_lastname索引的效率不如idx_lastname_state

所以,高效索引结论是:对于一条有多个不同列条件的查询,约束性高的条件所在列,可以在复合索引中的排序靠前,即优先执行

无用索引

sql

复制代码

explain select customer_id from customers
use index (idx_state_lastname)
where last_name like 'A%';

注意,对于复合查询出现的列,如果查询条件没有这一个列,相当于这里有一部分无用索引。 如果有这样的查询条件需求,最好在这一列上单独再创建一个索引。

复合索引提高效率的总结

  1. 让更频繁使用的列在前面
  2. 基数更高的列排在前面
  3. 考虑查询条件,具体分析

当索引失效时

例子1

sql

复制代码

explain select customer_id from customers
where state = 'CA' or points > 1000;
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE customers index idx_state_points,idx_state_lastname idx_state_points 12 1012 34.72 Using where; Using index

上面的结果中可以发现,他比全表扫描快,因为它不需要从磁盘里读取数据,还是只需要在内存中查询。虽然查询的类型是使用索引,但是有接近全表扫描的查询

这种时候,必须重写查询,以尽可能最好的方式利用索引。

sql

复制代码

create index idx_points on customers (points); --
explain 
  select customer_id from customers
  where state = 'CA' 
    union -- 自动去重记录
    select customer_id from customers
  where points > 1000;
  • 原本使用的idx_state_points索引可以快速定位 state ,所以可以不需要再单独添加一列索引
  • 添加一个idx_points 再快速找到 points > 1000 的记录
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 PRIMARY customers ref idx_state_points,idx_lastname_state,idx_state_lastname idx_state_points 8 const 112 100.00 Using where; Using index
2 UNION customers range idx_state_points,idx_points idx_points 4 529 100.00 Using where; Using index
3 UNION RESULT <union1,2> ALL Using temporary

从结果可以发现,先前扫描了 1000+ 条记录,但是现在只需要扫描 600+ 条记录,有了不小的提升。

例子2

sql

复制代码

explain select customer_id from customers
where points + 10 > 2010;
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE customers index idx_points 4 1012 100.00 Using where; Using index

因为有了一个表达式,MySQL必须对这张表全表扫描,索引完全失效。

可以直接简化这个表达式。

使用索引排序

sql

复制代码

explain select customer_id from customers 
order by state;
explain select customer_id from customers 
order by first_name;
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE customers index idx_state_points 12 1012 100.00 Using index

第一次:通过 state 列排序,MySQL会自动选择有关于这一列的索引,比如 idx_state_points 。Using index 表示使用到了索引进行排序。

1 SIMPLE customers ALL 1012 100.00 Using filesort

第二次:对 first_name 排序,ALL 就意味着进行了全表扫描,Using filesort 意味着使用了“外部排序( MySQL 的一种排序算法,很耗费资源的一种操作)”

通过show status like 'last_query_cost' 查看 MySQL 服务器的环境变量,发现第一次的花费是 1000+ ,第二次只有 100+

要使用索引排序的基本规则

sql

复制代码

explain select customer_id from customers 
order by state,points ;
  1. 不能出现索引中没有出现的列索引肯定没有办法使用的order by state,points,first_name
  2. 索引一般都是升序的,使用降序(desc)也会导致全表扫描,因为排序方向不一致。但是可以对单独的列进行降序排序。order by state,points desc 不行order by state desc,points desc 可以
  3. 排序列的顺序和索引中出现的一致,前缀也不能少。但是索引前缀列条件满足后可以继续使用索引排序order by points,stateorder by points都不行,但是有一个例外,可以先通过索引进入到一个 state 里面,然后再通过索引进行排序,例如where state = 'CA' order by state,points可以

覆盖索引

sql

复制代码

explain select customer_id from customers -- 使用上了索引,100+
order by state;
explain select * from customers -- 全表扫描,索引不够,1000+
order by state;
show status like 'last_query_cost';

使用索引三步

  1. 先查看 where 这种查找语句是否有索引条件
  2. 再查看 order by 排序语句是否有索引条件
  3. 最后查看 select 后选出的列是否有索引条件

如果 SELECT 子句里选择的列在索引中,整个查询就可以在不碰原表的情况下完成,这叫作覆盖索引(covering index),即索引满足了查询的所有需求所以全程不需要使用原表,这是最快的

维护索引

重复索引

例如:(A,B,C)(A,B,C) MySQL不会阻止重复索引创建,所以这回造成资源浪费。

多余索引

例如:(A,B)(A) 因为原来的索引也可以优化包含 A 列的查询

总结

维护索引要去掉重复索引、多余索引和未被使用的索引。 总之,创建索引之前,一定要先查看一下已经有的索引。


相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
27天前
|
存储 关系型数据库 数据库
什么是索引
【10月更文挑战第15天】什么是索引
|
3月前
|
TensorFlow 算法框架/工具 索引
索引
【8月更文挑战第13天】索引。
27 1
|
6月前
|
存储 NoSQL 关系型数据库
索引!索引!!索引!!!到底什么是索引?
**索引是数据库中的数据结构,类似书籍目录,加速数据查找和访问。优点包括提升查询性能、数据检索速度、支持唯一性约束及优化排序和连接操作。缺点在于增加写操作开销、占用存储空间、高维护成本和过多索引可能降低性能。常见的索引类型有单值、复合、唯一、聚集和非聚集索引等,实现方式涉及B树、B+树和哈希表。B树和B+树适合磁盘存储,B+树尤其适用于范围查询,哈希索引则适用于快速等值查询。**
56 0
|
6月前
|
SQL 关系型数据库 MySQL
关于索引的使用
关于索引的使用
|
6月前
|
安全 关系型数据库 MySQL
合理使用索引
【5月更文挑战第9天】这篇文章探讨了数据库索引的高效使用,包括函数和表达式索引、查找和删除未使用的索引、安全删除索引、多列索引策略、部分索引以及针对通配符搜索、排序、散列和降序索引的特殊技巧。还介绍了部分索引在减少索引大小和处理唯一性约束中的应用,以及PostgreSQL对前导通配符搜索的支持。通过遵循简单的多列索引规则和利用特定类型的索引,如哈希和降序索引,可以显著提高查询性能。
104 0
|
存储 关系型数据库 MySQL
了解和认识索引
了解和认识索引 。
62 0
|
6月前
|
存储 算法 关系型数据库
索引总结(2)
索引总结(2)
44 0
|
关系型数据库 MySQL 数据库
了解和认识索引
了解和认识索引。
52 0
|
关系型数据库 MySQL 索引
索引(2)
索引(2)。
41 0
|
数据库 索引
请注意这些情况下,你的索引会不生效!
数据库性能优化是确保系统高效运行的关键要素之一。而索引作为提升数据库查询性能的重要工具,在大部分情况下都能发挥显著的作用。然而,在某些情况下,索引可能会失效或不起作用,导致查询性能下降,甚至引发性能瓶颈。