MySQL:索引

1. 索引概述 

数据库索引(Index)是一种数据结构,其核心概念在于提高数据库的查询效率。具体来说,索引类似于书籍的目录,通过将数据存储在特定的数据结构中,使得数据库能够更快地找到满足查询条件的数据。索引通常是在表的某个列上创建的,这个列被称为索引列。索引列的值会被排序并存储在索引数据结构中,以便快速查找。当执行查询语句时,数据库会首先在索引中查找符合条件的数据行,然后再根据索引中存储的指针(或者地址)找到相应的数据行。

索引的实现通常使用B树和变种的B+树(例如,MySQL常用的索引就是B+树)。索引不仅可以加速数据的检索速度,还可以通过索引列对数据进行排序,降低数据排序的成本,减少CPU的消耗,以及降低数据库IO的成本。

然而,索引并非没有缺点。首先,索引需要占用额外的存储空间。其次,索引的创建和维护(如更新、删除等操作)也会增加数据库的维护开销。因此,在创建索引时需要权衡其带来的性能提升与所需的空间和维护成本。

总的来说,数据库索引是一种优化数据库查询性能的有效手段,通过合理的使用和管理,可以显著提高数据库的查询效率。

2. 索引分类

 主键索引:被primary修饰,特点:该索引默认自动创建唯一不能重复

 唯一索引:被unique修饰,避免表中列中的值重复,特点:该索引可以有多个

 常规索引:快速定位特定的数据,特点:可以有多个

 全文索引:被fulltext修饰,全文索引查找文中的关键字,而不是特定的值,特点:可以有多个

 聚集索引和二级索引

聚集索引:聚集索引下面悬挂着一行的数据,聚集索引唯一不重复,即主键索引

二级索引:下面悬挂着聚集索引,聚集索引以外的索引都为二级索引

  • 故通过二级索引查询得先查询到该索引下悬挂的聚集索引,在通过回表查询查询聚集索引下面悬挂的数据,即二级索引的查询效率没有聚集索引查询效率高

 

3. 索引语法

  • 创建索引 

create index 索引名 on 表明(字段名,...);

一个索引可同时关联多个字段

  • 查看索引

show index from 表名; 

  • 删除索引

drop index 索引名 on 表名;

mysql> #创建索引,该索引名为,该索引的字段来自哪个表
mysql> create index idx_user_name on tb_user(name);
Query OK, 0 rows affected (0.07 sec)
Records: 0  Duplicates: 0  Warnings: 0
mysql> #创建唯一索引
mysql> create unique index idx_user_phone on tb_user(phone);

mysql> #创建联合索引
mysql> create index idx_user_pro_age_sta on tb_user(profession,age,status);
mysql> show index;
mysql> #删除索引
mysql> drop index idx_user_ema on tb_user;

4. SQL性能分析 

查看执行频次 

show global status 

mysql> show global status like 'Com_______';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Com_binlog    | 0     |
| Com_commit    | 0     |
| Com_delete    | 0     |
| Com_import    | 0     |
| Com_insert    | 108   |
| Com_repair    | 0     |
| Com_revoke    | 0     |
| Com_select    | 18    |
| Com_signal    | 0     |
| Com_update    | 0     |
| Com_xa_end    | 0     |
+---------------+-------+

慢查询日志

慢查询日志默认记录查询时长超过10秒的记录,慢查询日志默认是关闭的 

mysql> #慢查询日志
mysql> show variables like 'slow_query_log';
+----------------+-------+
| Variable_name  | Value |
+----------------+-------+
| slow_query_log | OFF   |
+----------------+-------+
1 row in set (0.06 sec)

profile 

通过指令select @@profiling 可查询当前profile状态,该状态默认关闭

可通过set profiling = 1;将该查询状态打开

mysql> select @@profiling;
+-------------+
| @@profiling |
+-------------+
|           0 |
+-------------+
1 row in set, 1 warning (0.01 sec)
mysql> set profiling = 1;
Query OK, 0 rows affected, 1 warning (0.00 sec)

通过以下SQL指令可查看每条SQL业务查询所耗费时间

#查看每一条SQL耗时的基本情况

show profiles;

#查看指定SQL语句各个阶段的耗时情况

show profile for query query_id;

#查看指定SQL语句cpu使用情况

show profile  cpu for query query_id;

explain执行计划 

只需要查询语句前面加上关键字explain/desc即可

如:explain select 字段名 from 表名 where 查询条件;

mysql> explain select * from tb_user where id = 1;
+----+-------------+---------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table   | partitions | type  | possible_keys | key     | key_len | ref   | rows | filtered | Extra |
+----+-------------+---------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | tb_user | NULL       | const | PRIMARY       | PRIMARY | 4       | const |    1 |   100.00 | NULL  |
+----+-------------+---------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

 各个字段含义

id:表示查询顺序,id值越大越先执行,id值相同则由上往下逐一执行

select_type:以什么方式查询该表,如simple(不使用子查询、连接表查询、primary(主查询)、union(union中的第二个或者后面的查询语句),subquery(select或where后包含子查询)

type:表示连接类型,性能由好到差:null、system、conset、eq_ref、ref、range、index、all

possiblekey:可能出现的查询索引

key:实际用到的索引

key_len:索引所用字节数,该长度为可能用到的最大长度,并非实际长度

rows:认为必须要执行的查询行数,innodb引擎中是个预

filtered:返回结果的行数占读取总行数的占比

 5. 索引使用规则

5.1 最左前缀法则 

如果索引为联合索引必须遵循最左前缀法则,即必须从索引最左边字段开始查询,若跳过最左边的索引则该索引直接失效,会进行全表扫描查询,若走了最左边而后面跳过了部门索引则索引可能部分失效 

 遵循最左前缀法则,则走联合索引

mysql> explain select * from tb_user where profession = '软件工程' and age = age and status = 0;
+----+-------------+---------+------------+------+----------------------+----------------------+---------+-------+------+----------+-----------------------+
| id | select_type | table   | partitions | type | possible_keys        | key                  | key_len | ref   | rows | filtered | Extra                 |
+----+-------------+---------+------------+------+----------------------+----------------------+---------+-------+------+----------+-----------------------+
|  1 | SIMPLE      | tb_user | NULL       | ref  | idx_user_pro_age_sta | idx_user_pro_age_sta | 47      | const |    4 |     4.76 | Using index condition |
+----+-------------+---------+------------+------+----------------------+----------------------+---------+-------+------+----------+-----------------------+
1 row in set, 2 warnings (0.01 sec)

 未查询最左端字段,索引失效,全表扫描 

mysql> explain select * from tb_user where age = age and status = 0;
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table   | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | tb_user | NULL       | ALL  | NULL          | NULL | NULL    | NULL |   21 |     4.76 | Using where |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 2 warnings (0.00 sec)

 5.2 索引失效情况

1. 查询条件带有运算 

mysql> explain select * from tb_user where substring(phone, 10, 2) = 15;
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table   | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | tb_user | NULL       | ALL  | NULL          | NULL | NULL    | NULL |   21 |   100.00 | Using where |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.03 sec)

 2. 查询条件的类型和字段类型不符

mysql> explain select * from tb_user where phone = 17799990020;
+----+-------------+---------+------------+------+----------------+------+---------+------+------+----------+-------------+
| id | select_type | table   | partitions | type | possible_keys  | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+---------+------------+------+----------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | tb_user | NULL       | ALL  | idx_user_phone | NULL | NULL    | NULL |   21 |    10.00 | Using where |
+----+-------------+---------+------------+------+----------------+------+---------+------+------+----------+-------------+
1 row in set, 3 warnings (0.00 sec)

 3. 模糊匹配

%前置将失效

mysql> explain select  * from tb_user where profession like '%工程';
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table   | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | tb_user | NULL       | ALL  | NULL          | NULL | NULL    | NULL |   21 |    11.11 | Using where |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

 %后置可以走索引

mysql> explain select  * from tb_user where profession like '软件%';
+----+-------------+---------+------------+-------+----------------------+----------------------+---------+------+------+----------+-----------------------+
| id | select_type | table   | partitions | type  | possible_keys        | key                  | key_len | ref  | rows | filtered | Extra                 |
+----+-------------+---------+------------+-------+----------------------+----------------------+---------+------+------+----------+-----------------------+
|  1 | SIMPLE      | tb_user | NULL       | range | idx_user_pro_age_sta | idx_user_pro_age_sta | 47      | NULL |    4 |   100.00 | Using index condition |
+----+-------------+---------+------------+-------+----------------------+----------------------+---------+------+------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)

 4. or连接 

通过指令查询当前表中并没有age索引,此时使用or连接两个条件进行查询,name索引也失效,若要索引生效该字段条件必须都同时拥有索引 

mysql> show index from tb_user;
+---------+------------+----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table   | Non_unique | Key_name             | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+---------+------------+----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| tb_user |          0 | PRIMARY              |            1 | id          | A         |          21 |     NULL |   NULL |      | BTREE      |         |               | YES     | NULL       |
| tb_user |          0 | idx_user_phone       |            1 | phone       | A         |          21 |     NULL |   NULL |      | BTREE      |         |               | YES     | NULL       |
| tb_user |          1 | idx_user_name        |            1 | name        | A         |          21 |     NULL |   NULL |      | BTREE      |         |               | YES     | NULL       |
| tb_user |          1 | idx_user_pro_age_sta |            1 | profession  | A         |          14 |     NULL |   NULL | YES  | BTREE      |         |               | YES     | NULL       |
| tb_user |          1 | idx_user_pro_age_sta |            2 | age         | A         |          19 |     NULL |   NULL | YES  | BTREE      |         |               | YES     | NULL       |
| tb_user |          1 | idx_user_pro_age_sta |            3 | status      | A         |          21 |     NULL |   NULL | YES  | BTREE      |         |               | YES     | NULL       |
+---------+------------+----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
6 rows in set (0.01 sec)


mysql> explain select * from tb_user where name = '妲己' or age = 10;
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table   | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | tb_user | NULL       | ALL  | idx_user_name | NULL | NULL    | NULL |   21 |    14.29 | Using where |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

 5. 数据分布影响(范围查询)

当使用范围查询时,MySQL会进行评估,若走索引更慢则直接进行全表扫描 

mysql> explain select * from tb_user where phone >= '17799990010';
+----+-------------+---------+------------+-------+----------------+----------------+---------+------+------+----------+-----------------------+
| id | select_type | table   | partitions | type  | possible_keys  | key            | key_len | ref  | rows | filtered | Extra                 |
+----+-------------+---------+------------+-------+----------------+----------------+---------+------+------+----------+-----------------------+
|  1 | SIMPLE      | tb_user | NULL       | range | idx_user_phone | idx_user_phone | 46      | NULL |   11 |   100.00 | Using index condition |
+----+-------------+---------+------------+-------+----------------+----------------+---------+------+------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)

mysql> explain select * from tb_user where phone >= '17799990005';
+----+-------------+---------+------------+------+----------------+------+---------+------+------+----------+-------------+
| id | select_type | table   | partitions | type | possible_keys  | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+---------+------------+------+----------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | tb_user | NULL       | ALL  | idx_user_phone | NULL | NULL    | NULL |   21 |    76.19 | Using where |
+----+-------------+---------+------------+------+----------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

5.3 SQL提示

use index

explain select * from 表名 use index(建议使用的索引名) where 条件;

该语句对SQL进行索引建议,建议SQL使用哪个索引查询数据,MySQL权衡后再决定使用哪个索引查询数据,即该查询方式非强制

mysql> explain select * from tb_user use index(primary) where profession = '软件工程';
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table   | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | tb_user | NULL       | ALL  | NULL          | NULL | NULL    | NULL |   21 |     7.14 | Using where |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

ignore index

explain select * from 表名 ignore index(不建议使用的索引名) where 条件;

该语句不建议SQL使用某个索引

mysql> explain select * from tb_user ignore index(idx_user_pro_age_sta) where profession = '软件工程';
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table   | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | tb_user | NULL       | ALL  | NULL          | NULL | NULL    | NULL |   21 |     7.14 | Using where |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

froce index

explain select * from 表名 force index(强制使用的索引名) where 条件;

该语句强制SQL通过某个索引查询数据

mysql> explain select * from tb_user force index(primary) where profession = '软件工程';
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table   | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | tb_user | NULL       | ALL  | NULL          | NULL | NULL    | NULL |   21 |     7.14 | Using where |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

 5.4 索引覆盖

索引覆盖
如:查询name、id、gender这三个字段的信息,此时已经将name、gender两个字段建立联合索引(二级索引),而二级索引底下的叶子节点悬挂着聚集索引的信息(即此时悬挂着id 字段的信息)通过一次联合索引返回了name、gender的信息,而底下的索引即要查询的id,此时也直接返回未进行回表查询即该情况称为索引覆盖

mysql> explain select * from tb_user ignore index(idx_user_pro_age_sta) where profession = '软件工程';
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table   | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | tb_user | NULL       | ALL  | NULL          | NULL | NULL    | NULL |   21 |     7.14 | Using where |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

using where / using index:代表直接通过二级索引查询到了对应数据,无需进行回表查询

using index condition:此时需要进行回表查询

null:无需回表查询

5.5 前缀索引

当字段类型为字符串类型(如:长文本,文章,内容有几万字的)此时直接通过该字段类型建立索引会浪费大量的磁盘io,影响查询效率,可以通过前缀索引解决该问题,即用该字段指定的前多少个空间作为索引。

 创建前缀索引

create index 索引名 from 表名(关联的索引字段名(要截取该字段的多长的部分作为索引)); 

mysql> #计算选择性,选择性越高说明查询效率越好
mysql> select count(distinct substring(email, 1, 10)) / count(*) from tb_user;
+----------------------------------------------------+
| count(distinct substring(email, 1, 10)) / count(*) |
+----------------------------------------------------+
|                                             1.0000 |
+----------------------------------------------------+
1 row in set (0.06 sec)

mysql>  select count(distinct substring(email, 1, 5)) / count(*) from tb_user;
+---------------------------------------------------+
| count(distinct substring(email, 1, 5)) / count(*) |
+---------------------------------------------------+
|                                            0.9524 |
+---------------------------------------------------+
1 row in set (0.00 sec)
mysql> #创建前缀索引
mysql> create index idx_tb_email from tb_user(email(5));

 最终效果

+---------+------------+----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table   | Non_unique | Key_name             | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+---------+------------+----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| tb_user |          1 | idx_tb_email         |            1 | email       | A         |          20 |        5 |   NULL | YES  | BTREE      |         |               | YES     | NULL       |
+---------+------------+----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
8 rows in set (0.01 sec)

执行流程

先通过前缀索引查询到符合条件的数据,然后进行回表查询将完整的查询数据和条件对比,若符合即完成查询 

相关推荐

  1. MySQL索引

    2024-03-15 05:04:07       52 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-03-15 05:04:07       98 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-03-15 05:04:07       106 阅读
  3. 在Django里面运行非项目文件

    2024-03-15 05:04:07       87 阅读
  4. Python语言-面向对象

    2024-03-15 05:04:07       96 阅读

热门阅读

  1. thinkphp6.1~8.0 快速创建CRUD

    2024-03-15 05:04:07       37 阅读
  2. 高德地图2025届算法笔试3.12

    2024-03-15 05:04:07       41 阅读
  3. 计算机视觉

    2024-03-15 05:04:07       37 阅读
  4. Linux——GlusterFS分布式文件系统群集

    2024-03-15 05:04:07       30 阅读
  5. OpenCV亮度对比度调节

    2024-03-15 05:04:07       39 阅读
  6. 机器视觉学习(三)—— 保存视频流

    2024-03-15 05:04:07       38 阅读
  7. 动态规划矩阵

    2024-03-15 05:04:07       40 阅读
  8. 云计算有什么作用

    2024-03-15 05:04:07       40 阅读