mysql索引相关

目录

一.聚簇索引和非聚簇索引:

(1)聚簇索引也被称为主键索引:

(2)非聚簇索引(也称辅助索引或二级索引):

二.覆盖索引:

2.1.0 概念 

2.1.1 覆盖索引情况下,“不等于”索引生效

2.1.2 覆盖索引情况下,左模糊查询索引生效

2.1.3覆盖索引情况下,左模糊查询索引生效

2.2 覆盖索引的利弊

三.前缀索引

3.1 案例

3.2 前缀索引不能用覆盖索引

四.索引下推

4.1.0概念

4.2 ICP的使用条件

4.3 ICP的开启/关闭

4.4 ICP使用案例

五.索引设计原则


我们可以按照四个角度来分类索引:

按「数据结构」分类:B+tree索引、Hash索引、Full-text索引。
按「物理存储」分类:聚簇索引(主键索引)、二级索引(辅助索引/非聚簇索引)。
按「字段特性」分类:主键索引、唯一索引、普通索引、前缀索引。
按「字段个数」分类:单列索引、联合索引。

一.聚簇索引和非聚簇索引:

索引方法:BTREE 、hash、full-text

根据存储方式分为:聚簇索引和非聚簇索引

根据聚簇索引和非聚簇索引还能继续下分还能分为普通索引、覆盖索引、唯一索引以及联合索引等。

聚簇索引和非聚簇索引是数据库索引的两种存储方式。

在MySQL中,InnoDB存储引擎使用的是聚簇索引,而MyISAM存储引擎使用的是非聚簇索引。

1聚簇索引也被称为主键索引

数据和索引在同一个B-Tree结构中存储。

一个表只能有一个聚簇索引,通常是主键。

聚簇索引的叶子节点包含行的全部数据。

1.1聚簇索引的工作原理:

查询操作:当执行查询操作时,InnoDB引擎会利用B+树的特性,从根节点开始,通过比较索引的键值找到对应的叶子节点(数据页),从而快速找到需要的数据。因为索引的键值和数据是在一起的,所以查询效率非常高。

插入和删除操作:当进行插入或删除操作时,InnoDB引擎需要找到对应的索引键值,然后在对应的位置插入新的数据或删除旧的数据。因为数据是按照键的顺序存储的,所以插入和删除操作可能会引发数据的移动,尤其是在插入时如果插入的数据键值在当前键值范围内则可能会触发数据页的分裂。

更新操作:当进行更新操作时,如果更新的是非索引列,那么只需定位到数据页并进行更新即可;但是如果更新的是索引列,那么可能会引发数据的移动,因为要保持数据的有序性。

聚集索引选取规则

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

(2)非聚簇索引(也称辅助索引或二级索引):

索引和数据分开存储。

非聚簇索引存储的是索引列的值和(MySIAM->指向聚簇索引中对应行的指针)(InnoDB->主键的值)。这意味着,当我们通过非聚簇索引来查询数据时,MySQL需要先通过非聚簇索引找到主键,再通过主键在聚簇索引中找到实际的数据行。

在InnoDB中,聚簇索引默认是主键,如果没有定义主键,MySQL会选择一个非空唯一索引代替,如果没有非空唯一索引,MySQL会自动创建一个隐藏的聚簇索引,InnoDB会隐式定义一个主键,通常是6字节的行ID。

非聚簇索引,也叫二级索引,它的存储与数据行是分离的。在非聚簇索引中,索引树的叶子节点包含相应数据行的指针(MySIAM)或主键的值(InnoDB),而不是行数据本身。其叶子节点只存储索引列对应的数据值和主键值

当使用二级索引进行查询时,首先需要通过索引找到相应的指针,然后再通过指针找到实际的数据行。这通常需要两次磁盘I/O操作:一次是读取索引,另一次是读取实际数据。

二级索引可以为表中的任何列创建,不仅限于主键。这使得用户可以针对非主键列的查询优化。一个表可以有多个非聚簇索引。当查询不包含聚簇索引的列时,数据库系统会使用非聚簇索引来提高查询性能。

像“创建index普通索引”,这类索引在InnoDB中是非聚簇索引。

比如在一个员工表中,聚簇索引可能会基于员工的ID进行设置,而非聚簇索引可能会基于员工的姓名或者部门来设置。这样当查询姓名或者部门时,数据库系统就可以直接利用非聚簇索引进行查找,而不需要扫描整张表,从而提高了查询效率。

MyISM中的非聚簇索引工作原理

非聚簇索引是一种索引方式,MyISM采用的是非聚簇索引,其索引文件结构为B+Tree结构。索引文件和数据文件是分离的。索引文件存储B+Tree结构,数据文件存储表中的数据行。

索引文件是按照索引键值和表数据内存地址构建的B+Tree,其结构的叶子节点存储了索引列的值和指向数据文件中记录的物理位置(通常磁盘地址)的指针。

每个索引对应一个B+Tree结构的索引文件,索引文件是独立的。通过辅助索引检索时,无需访问主键索引树。

执行查询时,会利用非聚簇索引中的索引列值对B+Tree从根节点逐层查找,找到叶子节点。从叶子节点中获取记录的物理位置(磁盘地址)找到数据文件,从数据文件中获取响应的记录。当索引覆盖扫描时,可以直接从索引文件中返回这些值,无需再访问数据文件。

以下是创建聚簇索引和非聚簇索引的简单示例:

-- 创建一个带有主键的表,主键是聚簇索引

CREATE TABLE clustered_table (

    id INT PRIMARY KEY,

    data VARCHAR(100)

);

-- 创建一个非聚簇索引的表

CREATE TABLE non_clustered_table (

    id INT,

    data VARCHAR(100),

    INDEX non_clustered_index (id)

);

clustered_table中,id列是聚簇索引。在non_clustered_table中,虽然id列也是索引,但它是非聚簇索引,因为数据和索引是分开存储的。

与聚簇索引相比,非聚簇索引有以下几个特点:

一个表可以有多个非聚簇索引,但只能有一个聚簇索引。

非聚簇索引不会改变表中数据的物理排序,而聚簇索引会根据索引顺序来存储数据行。

非聚簇索引通常比聚簇索引更小,因为它不需要存储实际数据行。

使用非聚簇索引进行查询时,MySQL需要进行两次查找:先查找索引,然后查找实际数据行。

小结

1) InnoDB的的二级索引的叶子节点存放的是KEY字段加主键值。因此,通过二级索引查询首先查到是主键值,然后InnoDB再根据查到的主键值通过主键 索引找到相应的数据块。
2) MyISAM的二级索引叶子节点存放的还是列值与行号的组合,叶子节点中保存的是数据的物理地址。
所以可以看出MYISAM的主键索引和二级索引没有任何区别,主键索引仅仅只是一个叫做PRIMARY的唯一、非空的索引,且MYISAM引擎中可以不设主键

二.覆盖索引:

2.1.0 概念 

覆盖索引:一查询使用了索引,并且需要返回的列 在该索引中已经全部能找到,不需要回表等操作。

索引是高效找到行的一个方法,但是一般数据库也能使用索引找到一个列的数据,因此它不必读取整个行。毕竟索引叶子节点存储了它们索引的数据;当能通过读取索引就可以得到想要的数据,那就不需要读取行了。

覆盖索引是非聚簇索引的一种形式,它包括在查询里的SELECT、JOIN和WHERE子句用到的所有列 (即建索引的字段正好是覆盖查询条件中所涉及的字段)。简单说就是, 索引列+主键 包含 SELECT 到 FROM之间查询的列 。

2.1.1 覆盖索引情况下,“不等于”索引生效

没覆盖索引情况下,“不等于”索引失效:

没覆盖索引的情况下,使用“不等于”导致索引失效。因为如果使用索引,则需要依次遍历非聚簇索引B+树里所有叶节点,时间复杂度O(n),找到记录后还要回表,加在一起效率不如全表扫描,所以查询优化器就选择全表扫描了。

CREATE INDEX index_age_name ON user(phone, nickname);

a)查所有字段,并且使用“不等于”,索引失效

EXPLAIN SELECT  *  FROM `user` where age<>21;

b)覆盖索引情况下,“不等于”索引生效:

覆盖索引,查的两个字段被联合索引给覆盖了,性能更高。虽然还是需要依次遍历非聚簇索引B+树里所有叶节点,时间复杂度O(n),但是不需要回表了,整体效率比不用索引更高,查询优化器就又使用索引了。

CREATE INDEX user_index ON user(phone, nickname);

EXPLAIN SELECT id,nickname,age FROM `user`  where age<>21;

2.1.2 覆盖索引情况下,左模糊查询索引生效

没覆盖索引的情况下,左模糊查询导致索引失效

CREATE INDEX user_index ON user(phone, nickname);

EXPLAIN SELECT *  FROM `user` where nickname like '%哈';

2.1.3覆盖索引情况下,左模糊查询索引生效

主要原因也是因为走非聚簇索引B+树遍历叶节点,不回表,效率会比全表扫描时高,查询优化器选择效率高的方案。

CREATE INDEX user_index ON user(phone, nickname);

EXPLAIN SELECT id,phone,nickname FROM `user` where nickname like '%哈';

2.1.4查询除索引字段外其他字段,索引失效

CREATE INDEX user_index ON user(phone, nickname);

EXPLAIN SELECT id,age,nickname,phone FROM `user` where nickname like '%哈';

2.2 覆盖索引的利弊

好处:

1.避免回表(Innodb表进行索引的二次查询)

Innodb是以聚集索引的顺序来存储的,对于lnnodb来说,二级索引在叶子节点中所保存的是行的主键信息,如果是用二级索引查询数据,在查找到相应的键值后,还需通过主键进行二次查询才能获取我们真实所需要的数据。

在覆盖索引中,二级索引的键值中可以获取所要的数据,避免了对主键的二次查询 ,减少了IO操作,提升了查询效率。

2.可以把随机IO变成顺序IO加快查询效率

由于覆盖索引是按键值的顺序存储的,对于IO密集型的范围查找来说,对比随机从磁盘读取每一行的数据I0要少的多,因此利用覆盖索引在访问时也可以把磁盘的随机读取的IO 转变成索引查找的 顺序IO。

由于覆盖索引可以减少树的搜索次数,显著提升查询性能,所以使用覆盖索引是一个常用的性能优化手段。

弊端:

具体问题要具体分析:

索引字段的维护总是有代价的。因此,在建立几余索引来支持覆盖索引时就需要权衡考虑了。这是业务DBA,或者称为业务数据架构师的工作。

三.前缀索引

3.1 案例

有一张教师表,表定义如下:

create table teacher(

ID bigint unsigned primary key,

email varchar(64),

...

)engine=innodb;

讲师要使用邮箱登录,所以业务代码中一定会出现类似于这样的语句:

mysql> select col1, col2 from teacher where email='xxx';

如果email这个字段上没有索引,那么这个语句就只能做 全表扫描 。

MySQL是支持前缀索引的。默认地,如果你创建索引的语句不指定前缀长度,那么索引就会包含整个字符串。

mysql> alter table teacher add index index1(email);

#或

mysql> alter table teacher add index index2(email(6))

这两种不同的定义在数据结构和存储上有什么区别呢?下图就是这两个索引的示意图

如果使用的是index1(索引包含整个字符串),执行顺序是这样的:

从index1索引树找到满足索引值是’ zhangssxyz@xxx.com’的这条记录,取得ID2的值;

回表到主键上查到主键值是ID2的行,判断email的值是正确的,将这行记录加入结果集;

取index1索引树上刚刚查到的位置的下一条记录,发现已经不满足email=' zhangssxyz@xxx.com ’的 条件了,循环结束。

这个过程中,只需要回主键索引取一次数据,所以系统认为只扫描了一行。

如果使用的是index2(索引包含字符串前缀email(6)),执行顺序是这样的:

从index2索引树找到满足索引值是’zhangs’的记录,找到的第一个是ID1;

回表到主键上查到主键值是ID1的行,判断出email的值不是’ zhangssxyz@xxx.com ’,这行记录丢弃;

取index2上刚刚查到的位置的下一条记录,发现仍然是’zhangs’,取出ID2,再到回表到ID索引上取整行然后判断,这次值对了,将这行记录加入结果集;

重复上一步,直到在index2上取到的值不是’zhangs’时,循环结束。

也就是说使用前缀索引,定义好长度,就可以做到既节省空间,又不用额外增加太多的查询成本。前面 已经讲过区分度,区分度越高越好。因为区分度越高,意味着重复的键值越少。

3.2 前缀索引不能用覆盖索引

因为非聚簇索引树查到的数据是前缀和id,前缀不是完整数据,必须要回表到聚簇索引树。

所以使用前缀索引就用不上覆盖索引对查询性能的优化了,这也是你在选择是否使用前缀索引时需要考虑的一个因素。

四.索引下推

4.1.0概念

MySQL的索引下推(Index Condition Pushdown, ICP)是一种优化查询的方式,它可以改善查询性能,特别是对于包含多个条件的查询。在没有索引下推的情况下,MySQL会先从索引中找到满足某一条件的行的指针,然后根据这些指针去表中检索完整的行,最后再应用其他的过滤条件来确定哪些行实际符合查询的要求。

引入索引下推后,MySQL能够在使用索引检索数据时就应用所有相关的过滤条件,从而减少需要从存储引擎中检索的数据行数。这意味着如果一个查询可以部分或完全通过索引来判断数据行是否符合条件,MySQL就会在索引层面就过滤掉那些不符合条件的行,避免了对它们的进一步检查。这样做的结果是减少了IO操作和提高了查询的效率。

举例:

例如,考虑一个简单的表employees,包含字段id(主键),name,和department_id,并且department_id上有一个索引。如果我们执行一个查询来找出特定部门中名字以某个字母开头的员工,如:

SELECT * FROM employees WHERE department_id = 10 AND name LIKE 'A%';

如果没有索引下推,MySQL会首先使用department_id索引找到所有属于部门10的员工,然后逐个检查这些员工的名字是否以’A’开头。如果使用了索引下推,MySQL会在使用department_id索引的同时,检查名字是否以’A’开头,这样就能直接过滤掉更多的不符合条件的行,减少了需要进一步检查的数据量。

简而言之,索引下推让MySQL在尽可能早的阶段就过滤掉不符合条件的数据,从而提升查询性能。

不支持索引下推的联合索引:例如索引(name,age),查询name like 'z%' and age=?,模糊查询导致age无序。在联合索引树查询时只会查name,后面的age乱序不能直接进行条件判断,必须回表后再判断age。

而支持索引下推的联合索引:例如索引(name,age),查询name like 'z%' and age and address,在联合索引树查询时不止查name,还会判断后面的age,过滤后再回表判断address。

CREATE INDEX idx_name_age ON student(name,age);

#索引失败;非覆盖索引时,左模糊导致索引失效

EXPLAIN SELECT * FROM student WHERE name like '%bc%' AND age=30;

#索引成功;MySQL5.6引入索引下推,where后面的name和age都在联合索引里,可以又过滤又索引,不用回表,索引生效

EXPLAIN SELECT * FROM student WHERE `name` like 'bc%' AND age=30;

#索引成功;name走索引,age用到索引下推过滤,classid不在联合索引里,需要回表。

EXPLAIN SELECT * FROM student WHERE `name` like 'bc%' AND age=30 AND classid=2;

4.2 ICP的使用条件

表的访问类型为 range 、 ref 、 eq_ref 或者 ref_or_null 。

存储引擎:ICP可以用于InnDB和MyISAM存储引擎

必须二级索引:对于InnoDB表,ICP仅用于二级索引。ICP的目标是减少全行读取次数,从而减少I/O操作。

必须不是覆盖索引:当SQL使用覆盖索引时,不支持ICP优化方法。因为这种情况下使用ICP不会减少I/O。

相关子查询的条件不能使用ICP

必须5.6版本及以上:MySQL 5.6版本引入并默认开启,之前版本不支持索引下推。

必须where字段在索引列中:并非全部where条件都可以用ICP筛选,如果where条件的字段不在索引列中,还是要读取整表的记录到server端做where过滤。

4.3 ICP的开启/关闭

当使用索引条件下推是,EXPLAIN语句输出结果中Extra列内容显示为Using index condition如下图所示:

默认情况下启动索引条件下推。可以通过设置系统变量optimizer_switch控制:index_condition_pushdown

# 打开索引下推

SET optimizer_switch = 'index_condition_pushdown=on';

# 关闭索引下推

SET optimizer_switch = 'index_condition_pushdown=off';

4.4 ICP使用案例

五.索引设计原则

针对于数据量较大,且查询比较频繁的表建立索引

针对于常作为查询条件(where)、排序(order by)、分组(group by)操作的字段建立索引

尽量选择区分度高的列作为索引,尽量建立唯一索引,区分度越高,使用索引的效率越高

如果是字符串类型的字段,字段长度较长,可以针对于字段的特点,建立前缀索引

尽量使用联合索引,减少单列索引,查询时,联合索引很多时候可以覆盖索引,节省存储空间,避免回表,提高查询效率

要控制索引的数量,索引并不是多多益善,索引越多,维护索引结构的代价就越大,会影响增删改的效率

  1. 如果索引列不能存储NULL值,请在创建表时使用NOT NULL约束它。当优化器知道每列是否包含NULL值时,它可以更好地确定哪个索引最有效地用于查询
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值