MySQL进阶2——MySQL索引
本文最后更新于 510 天前,如有失效请评论区留言。

前言

索引是整个进阶篇中最为核心也最为重要的一个部分,后面的SQL优化很大程度上也是围绕着索引展开。

索引概述

索引(index)是帮助MySQL高效获取数据的数据结构(有序)。在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查找算法,这种数据结构就是索引。

在没有索引时查询数据,会从上到下依次进行查找整张表,也称为全表扫描,这种方式性能非常低。建立索引能让查询变得高效,比如下图的示例的二叉树索引(并不是真实的索引结构),如果需要找到年龄为45岁的用户,仅需要三步就可以查到:

  • 45岁比36岁大,走二叉树右边,45岁比48岁小,走二叉树左边,找到45岁的用户

recording

优缺点

  • 在使用当中,索引的两个劣势一般都可以忽略。
优势 劣势
提高数据检索的效率,降低数据库的IO成本 索引列也是要占用空间的。
通过索引列对数据进行排序,降低数据排序的成本,降低CPU的消耗。 索引大大提高了查询效率,同时却也降低更新表的速度,如对表进行INSERT、UPDATE、DELETE时,效率降低。

索引结构

MySQL的索引是在存储引擎层实现的,不同的存储引擎有不同的结构,主要包含以下几种:

  • 我们重点了解B+Tree索引
索引结构 描述
B+Tree索引 最常见的索引类型,大部分引擎都支持B+树索引
Hash索引 底层数据结构是用哈希表实现的,只有精确匹配索引列的查询才有效,不支持范围查询
R-tree(空间索引) 空间索引是MyISAM引擎的一个特殊索引类型,主要用于地理空间数据类型,通常使用较少
Full-text(全文索引 是一种通过建立倒排索引l快速匹配文档的方式。类似于Lucene,Solr,ES

MySQL存储引擎支持的索引结构:

索引 InnoDB MyISAM Memory
B+tree索引 支持 支持 支持
Hash 索引 不支持 不支持 支持
R-tree 索引 不支持 支持 不支持
Full-text 5.6版本之后支持 支持 不支持

我们平常所说的MySQL索引,如果没有特别指明,都是指InnoDB引擎的B+树结构的索引。

B-Tree

以一颗最大度数(max-degree)为5(5阶)的b-tree为例(每个节点最多存储4个key,5个指针):

  • 树的度数指的是一个节点的子节点个数;
  • 每个指针指向下面的子节点。

image-20240806170318176

示例:我们通过数据结构可视化网来进行演示B-Tree。

image-20240806180021279

B+Tree

下面的图中是一颗最大度数(max-degree)为4(4阶)的b+tree。

  • 在B+Tree中,所有的元素都会出现在叶子节点,非叶子节点作为索引,不存储数据;
  • 在B+Tree中,叶子节点形成了一条单向链表;

image-20240807073803784

示例:我们通过数据结构可视化网来进行演示B+Tree。

image-20240807074423958

MySQL中的B+Tree

MySQL索引数据结构对经典的B+Tree进行了优化。在原B+Tree的基础上,增加一个指向相邻叶子节点的链表指针,就形成了带有顺序指针的B+Tree,提高区间访问的性能,利于数据库数据排序。

  • 叶子节点存储了所有数据,分叶子节点存储索引。

image-20240807074615649

HASH索引

哈希索引就是采用一定的hash算法,将键值换算成新的hash值,映射到对应的槽位上,然后存储在hash表中。

  • 在下图的示例中,将name字段的每一个值进行了hash。比如金庸,对应的槽位是005,hash值是58dda。

image-20240807075128229

在大数据场景中,可能会出现多个key计算出来的hash值对应同一个槽位,如果两个(或多个)键值映射到一个相同的槽位上,他们就产生了hash冲突(也称为hash碰撞),这种情况下可以通过链表来解决。

image-20240807075617053

HASH索引的的特点

  1. Hash索引只能用于对等比较(=,in),不支持范围查询(between,>,<,….);
  2. 无法利用索引完成排序操作;
  3. 查询效率高,通常只需要一次检索就可以了(无hash碰撞的情况下),效率通常要高于B+tree索引。
  4. 在MySQL中,支持hash索引的是Memory引擎,而lnnoDB中具有自适应hash功能,hash索引是存储引擎根据B+Tree索引引在指定条件下自动构建的。

InnoDB存储引擎选择B+Tree的原因

  1. 相对于二叉树,层级更少,搜索效率高;
  2. 相对于B-tree,无论是叶子节点还是非叶子节点,都会保存数据,这样导致一
    页中存储的键值减少,指针跟着减少,要同样保存大量数据,只能增加树的
    高度,导致性能降低;
  3. 相对Hash索引,B+tree支持范围匹配及排序操作;

索引分类

  • 每当为字段指定一个PRIMARY/UNIQUE约束时,会自动为这个字段创建一个对应的索引。
分类 含义 特点 关键字
主键索引 针对于表中主键创建的索引 默认自动创建,只能有一个 PRIMARY
唯一索引 避免同一个表中某数据列中的值重复 可以有多个 UNIQUE
常规索引 快速定位特定数据 可以有多个
全文索引 全文索引查找的是文本中的关键词,而不是比较索引中的值 可以有多个 FULLTEXT

在lnnoDB存储引擎中,根据索引的存储形式,又可以分为以下两种:

分类 含义 特点
聚集索引l(Clustered Index) 将数据存储与索引放到了一块,索引结构的叶子节点保存了行数据 必须有,而且只有一个
二级索引l(SecondaryIndex) 将数据与索引分开存储,索引结构的叶子节点关联的是对应的主键 可以存在多个

聚集索引的选取规则:

  • 果存在主键,主键索引就是聚集索引。
  • 如果不存在主键,将使用第一个唯一(UNIQUE)索引I作为聚集索引。
  • 如果表没有主键,或没有合适的唯一索引,则nnoDB会自动生成一个rowid作为隐藏的聚集索引。

image-20240807082149425

如上图所示,id字段作为聚集索引,下挂的row对应每一行的数据。而name字段建立了二级索引,下挂对应的id值。

查询的过程——回表查询

select * from user where name = 'Arm';
  • 先通过二级索引查找到Arm对应的主键值,然后通过聚集索引查找到对应的行数据。

image-20240807082658156

以下SQL语句,哪个执行效率高?为什么?

select*from userwhere id=10;
select*from userwhere name='Arm';
#备注:id为主键,name字段创建的有索引;
  • 通过主键索引去查,仅需查询一次,而通过二级索引name字段去查,需要进行回表查询。

InnoDB主键索引的B+tree高度为多高呢?

image-20240807083804493

假设:
一行数据大小为1k,一页中可以存储16行这样的数据。InnoDB的指针占用6个字节的空间,主键为bigint,占用字节数为8。

高度为2时:非叶子节点不存储数据,只存储key和指针,指针永远比key多一个,设主键存储的key数量为n,可以得到

n * 主键占用的字节数 + (指针数量)* 6 = 页的总存储数量

n 8+(n+1) 6=16 * 1024,算出n约为1170,

即1个2层的非叶子节点可以存放1170个key,对应了1171个指针,再指向1171个叶子节点,可以存放记录数约为:

1171* 16 = 18736

高度为3时,可以存放记录数约为:

1171 1171 16 = 21939856

如果在InnoDB引擎下存储四五千万的记录,这个时候树的结构可能超过3层,建议要进行分库分表。

索引语法

创建索引

  • unique 唯一索引,表名该索引字段不能重复
  • fulltext 全文索引
  • 不指定索引,默认创建常规索引
  • 一个索引可以关联多个字段,单列索引:关联一个字段;联合索引(组合索引):关联多个字段
create [unique] [fulltext] index index_name on table_name(index_column_name,...);

查看索引

show index from table_name;

删除索引

drop index index_name on table_name;

示例:

创建表环境

create table tbe_user(
    id int not null primary key unique,
    name varchar(20),
    phone varchar(15),
    email varchar(30),
    profession varchar(50),
    age int,
    gender int,
    status int,
    createtime datetime
);

insert into tbe_user() values(1,'吕布',17799990000,'lvbu666@163.com','软件工程',23,1,6,'2001-02-02 00:00:00'),
(2,'曹操',17799990001,'caocao666@qq.com','通讯工程',33,1,1,'2001-03-05 00:00:00'),
(3,'赵云',17799990002,'17799990@139.com','英语',34,1,2,'2002-03-02 00:00:00'),
(4,'孙悟空',17799990003,'17799990@sina.com','工程造价',54,1,1,'2001-07-02 00:00:00'),
(5,'花木兰',17799990004,'19980729@sina.com','软件工程',23,2,1,'2001-04-22 00:00:00'),
(6,'大乔',17799990005,'daqiao666@sina.com','舞蹈',22,1,2,'2001-02-07 00:00:00'),
(7,'露娜',17799990006,'luna_love@sina.com','应用数学',24,1,2,'2001-02-08 00:00:00'),
(8,'程咬金',17799990007,'chengyaojin@163.com','化工',38,1,5,'2001-05-23 00:00:00'),
(9,'项羽',17799990008,'xiaoyu666@qq.com','金属材料',43,1,0,'2001-09-18 00:00:00'),
(10,'白起',17799990009,'baiqi666@sina.com','机械工程及其自动化',27,1,2,'2001-08-16 00:00:00'),
(11,'韩信',17799990010,'hanxin520@163.com','无机非金属材料工程',27,1,1,'2001-06-12 00:00:00'),
(12,'荆轲',17799990011,'jingke123@163.com','会计',29,1,1,'2001-05-11 00:00:00'),
(13,'兰陵王',17799990012,'lanlinwang666@126.com','工程造价',44,1,1,'2001-04-09 00:00:00'),
(14,'狂铁',17799990013,'kuangtie@sina.com','应用数学',43,1,1,'2001-04-10 00:00:00'),
(15,'貂蝉',17799990014,'184958948374@qq.com','软件工程',40,2,3,'2001-02-12 00:00:00'),
(16,'妲己',17799990015,'12783238293@qq.com','软件工程',31,1,2,'2001-01-30 00:00:00'),
(17,'华月',17799990016,'xiaomin2001@sina.com','工业经济',35,2,1,'2000-05-03 00:00:00'),
(18,'赢政',17799990017,'I8839434342@qq.com','化工',38,1,1,'2001-08-08 00:00:00'),
(19,'狄仁杰',17799990018,'jujiamlm8166@163.com','国际贸易',30,1,0,'2007-03-12 00:00:00'),
(20,'安琪拉',17799990019,'jdodm1h@126.com','城市规划',51,1,0,'2001-08-15 00:00:00'),
(21,'典韦',17799990020,'ycaunanjian@163.com','城市规划',52,1,2,'2000-04-12 00:00:00'),
(22,'廉颇',17799990021,'lianpo321@126.com','土木工程',19,1,3,'2002-07-18 00:00:00'),
(23,'后羿',17799990022,'altycj2000@139.com','城市园林',20,1,1,'2002-03-10 00:00:00'),
(24, '姜子牙', '17799990023', '37483844@qq.com', '工程造价', 29, 1, 4, '2003-05-26 00:00:00');

image-20240807114320123

按照下列需求,完成对应的索引创建。

1、name字段为姓名字段,该字段的值可能会重复,为该字段创建索引。

create index idx_user_name on tbe_user(name);
show index from tbe_user;

2、
phone手机号字段的值,是非空,且唯一的,为该字段创建唯一索引。

create unique index idx_user_phone on tbe_user(phone);
show index from tbe_user;

3、为profession、age、status创建联合索引。

create index idx_user_pro_age_sta on tbe_user(profession,age,status);
show index from tbe_user;

image-20240807123932986

4、为email建立合适的索引来提升查询效率。

create index idx_user_email on tbe_user(email);
show index from tbe_user;

5、删除email的索引

drop index idx_user_email on tbe_user;
show index from tbe_user;

SQL性能分析

SQL执行频率

SQL优化实际上主要针对查询语句,对于增删改的优化优先级没有查询高。所以我们首先要对数据库的SQL执行频率做分析,看数据库是执行什么类型的语句为主。MySQL客户端连接成功后,通过show[sessionlglobal]status命令可以提供服务器状态信息。通过如下指令,可以查看当前数据库的
INSERT、UPDATE、DELETE、SELECT的访问频次:

show global status like 'Com_______'; -- 跟7个下划线,代表7个字符

image-20240807124907243

慢日志查询

在上一节中我们看到查询语句执行的频率最高,但是此时我们只知道是查询类型的语句执行频率高,并不知道具体是哪一些语句,这个时候就需要借助慢查询日志来进行查询。

慢查询日志是MySQL中预设的日志,主要用于记录查询时间超过10s(默认值)的SQL语句。

查看慢日志是否开启(默认没有开启,我这里提前打开了)

show variables like 'slow_query_log';

image-20240807125403594

如果慢日志没有打开,需要修改MySQL配置文件并重启MySQL打开慢日志。

vim my.cnf
#慢日志
long_query_time=3 #超过3秒定义为慢查询
slow_query_log=ON #开启慢日志
slow_query_log_file=/data/database/logs/slow_query.log #慢日志文件

profiles详情

对于查询时间超过限定时长的SQL语句,我们可以通过慢日志来定位,但是还有一种情况,对于一个很小的表,查询时间也达到临近慢查询限定值,比如2.7s,这样的SQL也肯定是有问题的。这种情况下可以通过profile来定位。

1、查看每一条sql的耗时
show profiles;

2、查看指定query_id的sql语句各个阶段的耗时情况
show profile for query query_id;

3、查看指定query_id的sql语句cpu使用情况
show profile cpu for query query_id;

show profiles能够在做SQL优化时帮助我们了解时间都耗费到哪里去了。通过have_profiling参数,能够看到当前MySQL是否支持
profile操作:

select @@have_profiling;

默认profiling是关闭的,可以通过set语句在session/global级别开启profiling:

set [session,global] profiling = 1;

执行查询语句,并分析查询语句

select * from tbe_user where name = '白起';
select count(*) from tbe_user;
show profiles;

image-20240807131934523

show profile for query 1;

image-20240807132325023

show profile cpu for query 1;

image-20240807132503634

explain执行计划

EXPLAIN或者DESC命令获取MySQL如何执行SELECT语句的信息,包括在SELECT语句执行过程中表如何连接和连接的顺序。

  • 查看查询的执行顺序时,可以看到有id,select_type等字段,一般我们主要关注的是type, possible_keys,key,key_len,extra这几个字段。
#直接在select语句之前加上关键字explain/desc
EXPLAIN SELECT 字段列表 FROM 表名 WHERE 条件;

desc select * from tbe_user where name = '白起';

image-20240807132816437

那么这些字段在查询中都分别起到什么作用呢?

1、id:select查询的序列号,表示查询中执行select子句或者是操作表的顺序(id相同,执行顺序从上到下;id不同,值越大,越先执行)。

接下来演示id值相同的执行顺序,我们此前做过学生课程的案例,这里可以复用表环境进行查询。

select * from coures;
select * from student;
select s.*,c.* from student s, coures c, student_course sc where s.id = sc.studentid and c.id = sc.courseid;

image-20240807142158931

现在看一下执行计划

explain select s.*,c.* from student s, coures c, student_course sc where s.id = sc.studentid and c.id = sc.courseid;

image-20240807142406489

然后再看一下id值不同的执行顺序。

示例:查询选修了MySQL课程的学生(子查询)

#没有子查询:
select id from coures c where c.name = 'MySQL';
select studentid from student_course sc where sc.courseid = 3;
select * from student s where s.id in (1,2);
 #子查询
 select * from student s where s.id in (select studentid from student_course sc where sc.courseid = (select id from coures c where c.name = 'MySQL'));

查看执行计划,可以看到优先是执行子查询。

desc select * from student s where s.id in (select studentid from student_course sc where sc.courseid = (select id from coures c where c.name = 'MySQL'));

image-20240807143103841

2、select_type:表示SELECT的类型,常见的取值有SIMPLE(简单表,即不使用表连接或者子查询)、PRIMARY(主查询,即外层的查询)、
UNION(UNION中的第二个或者后面的查询语句)、SUBQUERY(SELECT/WHERE之后包含了子查询)等。

这个字段的查询参考意义并不大,只是说明了当前查询的类型。了解即可。

3、type:表示连接类型,性能由好到差的连接类型为NuLL、system、const、eq_ref、ref、range、index、all。

  • 在sql编写时,应尽量优化SQL语句的性能;
  • Null:一般优化不到Null这个type,只有当不查询任何表时,才能达到Null这个级别。比如:select 'A';
  • system:访问系统表时一般是system;
  • const:查询主键和唯一索引时,一般是const;
  • ref:使用非唯一性的索引查询时,是ref;
  • index:代表用了索引,但是会对索引进行遍历,虽然比all快,但是性能也很低;
  • all:代表出现了全表扫描,性能低。

4、possible_key:显示可能应用在这张表上的索引,一个或多个。

5、Key:实际使用的索引,如果为NULL,则没有使用索引。

6、Key_len:表示索引中使用的字节数,该值为索引字段最大可能长度,并非实际使用长度,在不损失精确性的前提下,长度越短越好。

7、rows
MySQL认为必须要执行查询的行数,在innodb引擎的表中,是一个估计值,可能并不总是准确的。

8、filtered
表示返回结果的行数占需读取行数的百分比,filtered的值越大越好。

索引使用规则

最左前缀法则

针对联合索引:如果索引了多列(联合索引),要遵守最左前缀法则。最左前缀法则指的是查询从索引的最左列开始,并且不跳过索引中的列。如果跳跃了左边第一列,索引将全部失效,如果跳跃中间某一列,索引将部分失效(后面的字段索引失效)。

遵守最左前缀法则查询时:使用全部的3个索引,从左到右不跳过任何索引。

explain select * from tbe_user where profession = '软件工程' and age = '31' and status = '2';

image-20240807152920302

减少最后一列索引查询时:只使用2个索引

desc select * from tbe_user where profession = '软件工程' and age = '31';

image-20240807153116377

再减少后面一列索引进行查询:只使用1个索引

desc select * from tbe_user where profession = '软件工程';

image-20240807153734611

跳过第一列索引进行查询:只使用两个索引

  • 此时不满足最左前缀法则,最左边的列没有出现,索引全部实现。
desc select * from tbe_user where  age = '31' and status = '2';

image-20240807153900217

跳过第二列索引,使用第一列索引和最后一列索引进行查询。

  • 此时仍然可以通过索引进行查询,不过通过key_len可以看出,第三列索引已经失效。
desc select * from tbe_user where profession = '软件工程' and status = '2';

image-20240807154117195

在where条件中交换条件的位置,并不会影响查询调用索引的结果。

desc select * from tbe_user where age = '31' and status = '2' and  profession = '软件工程';

image-20240807154530378

使用范围查询时,该索引右边的联合索引会失效。

  • 要想规避这种现象,需要把>改为>= 或者<=这样的比较运算符。
desc select * from tbe_user where profession = '软件工程' and  age > '31' and status = '2';

image-20240807154813351

导致索引失效的情况

索引列运算

不要在索引列上进行运算操作,不然索引将会失效。

select * from tbe_user where phone = '17799990015';
select * from tbe_user where substring(phone,10,2) = '15';

我们通过上述两条SQL语句来进行查询,phone字段设置了unique索引。下面看一下他们的执行效率。

  • 可以看到,在第二条查询中,因为使用了函数运算,phone索引已经失效。
desc select * from tbe_user where phone = '17799990015';
desc select * from tbe_user where substring(phone,10,2) = '15';

image-20240807155426959

字符串不加引号

字符串类型字段使用时不加引号,索引将失效。

select * from tbe_user where phone = '17799990015';
select * from tbe_user where phone = 17799990015;

我们通过上述两条SQL语句来进行查询,phone字段设置了unique索引。下面看一下他们的执行效率。

  • 可以看到,在第二条的where条件中因为没有加引号,索引已经失效。
desc select * from tbe_user where phone = '17799990015';
desc select * from tbe_user where phone = 17799990015;

image-20240807155834640

这里的案例用的时单列索引,在使用联合索引中,不加引号也会导致索引部分失效或者全部失效。

模糊查询

在查询中我们会通过like字段加%来进行模糊查询,在对索引字段进行模糊查询时,如果仅仅是尾部进行模糊查询索引不会失效,如果是头部模糊匹配将导致索引失效。

  • 可以看到在后两条SQL语句中,索引已经失效。
  • 大数据的场景下,一定要避免在前面使用%。
desc select * from tbe_user where profession like '软件%';
desc select * from tbe_user where profession like '%工程';
desc select * from tbe_user where profession like '%工%';

image-20240807160503925

or连接的条件

用or分割开的条件,如果or前的条件中的列有索引,而后面的列中没有索引,那么涉及的索引都不会被用到。

  • 可以看到虽然id有索引,但因为age没用索引,导致查询中的索引失效;
  • 要解决这种情况,需要在age添加索引,再次查询看到是调用了索引查询。
desc select * from tbe_user where id = 10 or age = 23;
desc select * from tbe_user where phone = '17799990000' or age = 23;

image-20240807160953037

create index idx_user_age on tbe_user(age);
desc select * from tbe_user where id = 10 or age = 23;
desc select * from tbe_user where phone = '17799990000' or age = 23;

image-20240807161212883

数据分布影响

如果MySQL评估使用索引比全表更慢,则不使用索引。

  • 在这个例子中,第二条SQL语句使用了全表扫描。
explain select * from tbe_user where phone >= '17799990022';
explain select * from tbe_user where phone >= '17799990000';

image-20240807164607069

  • 这里并不是null就走索引,not null 不走索引,而是却决于当前数据库中满足条件的数据的分布情况,绝大部分数据都满足条件的情况下,MySQL会放弃索引走全表扫描。
explain select * from tbe_user where profession is null;
explain select * from tbe_user where profession is not null;

image-20240807164924134

SQL 提示

SQL提示,是优化数据库的一个重要手段,简单来说,就是在SQL语句中加入一些人为的提示来达到优化操作的目的。

我们通过一个例子来说明什么是SQL提示。

  • 此时提示可能使用单列索引和联合索引,但是最后使用了联合索引,这是数据库自动选择的结果。
  • 如果我们想让MySQL使用我们指定的索引,而不是自己去自动选择,就可以通过SQL提示的方法。
create  index idx_user_pro on tbe_user(profession);
desc select * from tbe_user where profession = '软件工程';

image-20240807165440601

SQL提示分类

1、use index:指定建议使用的索引

  • 这里只是建议使用,MySQL还是会自己去判断是否要使用,如果效率不高,MySQL就不适用。
explain select * from tbe_user use index(idx_user_pro) where profession = '软件工程';

image-20240807165933725

2、ignore index:忽略指定的索引

explain select * from tbe_user ignore index(idx_user_pro) where profession = '软件工程';

image-20240807170149424

3、force index:强制使用某个索引

explain select * from tbe_user force index(idx_user_pro) where profession = '软件工程';

image-20240807170238557

覆盖索引&回表查询

尽量使用覆盖索引(查询使用了索引,并且需要返回的列,在该索引引中已经全部能够找到),减少select *。

由于前面建立了不少的索引,现在清理一些不用的。

drop index idx_user_age on tbe_user;
drop index idx_user_pro on tbe_user;
show index from tbe_user;

image-20240807171021934

desc select id,profession from tbe_user where profession = '软件工程' and age > '31' and status = '2'  ;
desc select id,profession,age,status from tbe_user where profession = '软件工程' and age > '31' and status = '2';
desc select id,profession,age,status,name from tbe_user where profession = '软件工程' and age > '31' and status = '2';
desc select * from tbe_user where profession = '软件工程' and age > '31' and status = '2'  ;
  • using index condition:查找使用了索引,但是需要回表查询数据;
  • using where;using index:查找使用了索引,但是需要的数据都在索引列中能找到,所以不需要回表查询数据;
  • 查询id时,可以直接从聚集索引查找;
  • 查询id,age,profession时,通过二级索引直接能找到所有的字段。
  • 查询name时,因为name字段没有索引,所以它需要回表查询,查询效率就会低一些。

image-20240807171258517

前缀索引

当字段类型为字符串(varchar,text等)时,有时候需要索引很长的字符串,这会让索引变得很大,查询时,浪费大量的磁盘iO,影响查
询效率。此时可以只将字符串的一部分前缀,建立索引,这样可以大大节约索引空间,从而提高索引效率。

语法:create index idx_xxx on table_name(column(n));
  • n代表前几个字符

前缀长度

可以根据索引的选择性来决定,而选择性是指不重复的索引值(基数)和数据表的记录总数的比值,索引选择性越高则查询效率越高,
唯一索引的选择性是1,这是最好的索引选择性,性能也是最好的。

求取选择性:

select count(distinct email)/count(*) from tbe_user;
select count(distinct substring(email,1,5))/count(*) from tbe_user;

image-20240807175923377

create index idx_user_email on tbe_user(email(5));

image-20240807180106226

desc select * from tbe_user where email = 'caocao666@qq.com';

image-20240807180227216

  • 首先查询辅助索引,查询前缀进行匹配,拿到匹配的id;
  • 回表查询对应id的记录值,对比字段值是否是我们查询的,如果不是就查询下一条记录,直到匹配。

单列&联合索引

单列索引:即一个索引只包含单个列。

联合索引:即一个索引包含了多个列。

desc select id,phone,name from tbe_user where phone = '17799990001' and name = '曹操';

image-20240807180859060

这里只走了一个字段的索引,在单列索引中只会走一个索引,另一个字段必然会涉及回表查询。

create unique index idx_user_phone_name on tbe_user(phone,name);
desc select id,phone,name from tbe_user use index(idx_user_phone_name) where phone = '17799990001' and name = '曹操';

image-20240807181510451

  • 所以建议建立联合索引,而不是单列索引;
  • 但联合索引必须遵循最左前缀法则。

索引设计原则

  1. 对于数据量较大,且查询比较频繁的表建立索引。
  2. 针对于常作为查询条件(where)、排序(order by)、分组(group by)操作的字段建立索引。
  3. 尽量选择区分度高的列作为索引,尽量建立唯一索引,区分度越高,使用索引的效率越高。
  4. 如果是字符串类型的字段,字段的长度较长,可以针对于字段的特点,建立前缀索引。
  5. 尽量使用联合索引,减少单列索引,查询时,联合索引很多时候可以覆盖索引,节省存储空间,避免回表,提高查询效率。
  6. 要控制索引的数量,索引并不是多多益善,索引越多,维护索引结构的代价也就越大,会影响增删改的效率。
  7. 如果索引列不能存储NULL值,请在创建表时使用NOTNULL约束它。当优化器知道每列是否包含NULL值时,它可以更好地确定哪个索引最有效地用于查询。
版权声明:除特殊说明,博客文章均为cuckooyang原创,依据CC BY-SA 4.0许可证进行授权,转载请附上出处链接及本声明。 | 博客订阅:RSS | 广告招租:留言板 | 博客VPS |
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇