mysql的各类索引

MySQL的索引分三类:单列索引(普通索引、唯一索引、主键索引)、多列索引(联合主键,组合索引)、全文索引

一、单列索引:一个索引只包含单个列,除了主键索引,一个表中可以有多个单列索引。

(1)普通索引(二级索引,非聚簇索引):MySQL中基本索引类型,没有什么限制,允许在定义索引的列中插入重复值和空值,纯粹是为了查询数据更快一点。

(2)唯一索引(二级索引,非聚簇索引):索引列中的值必须是唯一的,但是允许为空值。

(3)主键索引(聚簇索引):是一种特殊的唯一索引,非空且唯一。(主键约束,就是一个主键索引)。

主键索引与唯一索引的区别:

(1)主键是一种约束,唯一索引是一种索引,两者在本质上是不同的。

(2)主键创建后一定包含一个唯一性索引,唯一性索引并不一定是主键。

(3)唯一性索引列允许空值,而主键列不允许为空值。

(4)主键索引在创建时,已经默认为非空值+唯一索引了。

(5)一个表最多只能创建一个主键索引,但可以创建多个唯一索引。

(6)主键更适合那些不容易更改的唯一标识,如自动递增列、身份证号等。

(7)主键可以被其他表引用为外键,而唯一索引不能。

二、多列索引:一个多列索引包含多个列

1.联合主键

1)什么是联合主键

(也叫复合主键、组合主键),一个表中有多个主键列组合在一起,叫联合主键,一个表中只能有一个联合主键.

create table test
(
   name varchar(19),
   id number,
   value varchar(10),
   primary key (name,id)
)

2)为什么要使用联合主键

比如一个部门人员表,你不想单独建一个id列作为主键的话,就可以用 部门列 和 员工name 列都设为主键,作为表的联合主键,这样也可以保证表中每行数据都不重复.

2.组合索引

1)什么是组合索引

(也叫复合索引、联合索引),组合索引是不含主键索引的,是多个普通索引组合在一起的.一个表中可以有多个组合索引.

ALTER TABLE mytable ADD INDEX name_city_age (name,age,city);

把一个表的name,age,city三个非主键列设为一个组合索引,数据库实际上自动为我们设立了3个索引,分别是(name)        (name,age)       (name,age,city)

2)为什么要使用组合索引

  • 减少开销。
    建一个联合索引(col1,col2,col3),实际相当于建了(col1),(col1,col2),(col1,col2,col3)三个索引。比分别建立索引(col1),(col2),(col3)少了两个索引.        每多一个索引,都会增加写操作的开销和磁盘空间的开销。对于大量数据的表,使用联合索引会大大的减少开销!
  • 覆盖索引。
    对联合索引(col1,col2,col3),如果有如下的sql: select col1,col2,col3 from test where col1=1 and col2=2。那么MySQL可以直接通过遍历索引取得数据,而无需回表,这减少了很多的随机io操作。减少io操作,特别的随机io其实是dba主要的优化策略。所以,在真正的实际应用中,覆盖索引是主要的提升性能的优化手段之一。

3.最左原则

联合主键和组合索引都要遵守最左原则.

怎么查询才能保证全部走索引呢,那么就是要保证最左原则.

1.where条件后面必须含有第一个列name, 

2.顺序不能跳,就是不能是name和city,而没有age.

3.where条件后面的name和age在写在前后都无所谓.

走全部索引的例子:

SELECT * FROM mytable WHREE name="admin"


SELECT * FROM mytable WHREE name="admin" AND age= 20

SELECT * FROM mytable WHREE age= 20 AND  name="admin" 

SELECT * FROM mytable WHREE name="admin" AND age= 20  AND city ="北京"

SELECT * FROM mytable WHREE city ="北京" AND name="admin" AND age= 20  

走部分索引的例子:

SELECT * FROM mytable WHREE name="admin" AND city ="北京"

完全没走索引的例子:

SELECT * FROM mytable WHREE age= 20
SELECT * FROM mytable WHREE age= 20 AND   city ="北京"

三、全文索引:

在大量数据中,通过其中的某个关键字,就能找到该字段所属的记录行。全文索引在开发中很少用,因为其占用很大的物理空间和降低了记录修改性。


 

四. 索引在B+树的工作原理

1.我先建了一个表

CREATE TABLE `user_innodb` (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `gender` tinyint(1) DEFAULT NULL,
  `phone` varchar(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

我创建了一个存储引擎为InnoDB的表user_innodb,其中包含主键id、姓名字段(name)、性别字段(gender,用0,1表示不同性别)、手机号字段(phone),并批量初始化了500W+条数据。

2.主键索引(聚簇索引)

mysql的各类索引_第1张图片

上图就是mysql的innodb引擎根据我们设的id自动为我们生成的一个主键B+树索引结构,我们给它取了个名字,叫主键索引.又叫做聚簇索引.

主键索引有两个特点:

  1. 按照主键的大小对用户记录和数据页进行排序,记录用单向链表连接,叶子节点使用双向链表连接;
  2. B+树的叶子节点保存了用户的完整记录。

3.普通索引(二级索引,非聚簇索引) 

主键索引是在搜索条件为主键的时候才会发挥作用,但是我要以name='蝉沐风'为搜索条件怎么办?

innodb的解决办法就是再创建一个B+树(我们称为name索引),这就是普通索引.

mysql的各类索引_第2张图片

这棵普通索引的B+树和聚簇索引的B+树有点区别:

  1. 叶子节点存放的不再是完整的用户记录,而是只记录name列和主键值;
  2. 数据页中存放的用户记录和目录项记录由原本的按照主键排序变为按照name列排序;
  3. 目录项记录除了存储索引列(name)和页号之外,同时还存储了主键值;(大家可以想一想,为什么要存储主键值,因为name列可能不是唯一的,如果两个name相同,那么谁排前面谁排后面呢,这时候就要按主键值来排序了.)

有了这棵B+树,你就可以通过name列快速找到主键值了,查找的方式和根据主键值查找用户记录的方式完全一样,只不过前者查到的是主键值,后者查找到的是一条完整的用户记录罢了。

你可能对字符串进行二分法感到有点奇怪,甚至没有接触过的相关知识的读者连对字符串进行排序都会觉得很诧异。其实在创建表的时候我们可以对字符串字段指定字符集和比较规则,如果你不指定,MySQL会默认给你设置,总之,MySQL总会找到一个方式对字符串进行排序。

现在得到主键的id了,然后根据主键id到主键索引中查找到完整的用户记录,这个过程叫做回表。如果没有为name列设置唯一性约束,那就可能找到多个符合条件的主键id,多回几次表就可以了。

name这种单个列添加的索引叫做普通索引,也叫二级索引

4.组合索引

(也叫复合索引、联合索引)

假设我们为name列和phone列建立联合索引(注意我描述的顺序,name列在前,phone列在后,后面的图的顺序也就是按这个顺序排的),自然也是创建一棵B+树,这棵B+树和之前又稍微有点不同:

  1. 叶子节点存放的是name列、phone列和主键值;
  2. 目录项记录除了存储索引列(namephone)和页号之外,同时还存储了主键值;(大家可以想一想,为什么要存储主键值)
  3. 数据页中存放的用户记录和目录项记录由原本的按照主键排序变为按照name列排序,如果name列相同,那就按照phone列排序;(如果phone列再一样呢?你现在明白为什么要存储主键值了吗?)

 再画个图吧(有点偷懒了哈,数据页号没换):

mysql的各类索引_第3张图片

还是和二级索引一样,利用B+树快速定位到数据页,然后页内快速定位到记录,找到记录中的主键id,再回表,如果找到多条符合条件的记录,就多回几次表。

五:补充

补充1:覆盖索引

覆盖索引(covering index ,或称为索引覆盖)即从非主键索引中就能查到的记录,而不需要查询主键索引中的记录,避免了回表的产生减少了树的搜索次数,显著提升性能。

例子1:id是主键,name是普通索引

mysql的各类索引_第4张图片

 selete id from stu where name = 小王,   这个查询就不用回表,查询快,是覆盖索引.

例子2:在例子1基础上,现在出现的业务需求中要求根据名称获取学生的年龄,并且该搜索场景非常频繁,那么先在我们删除掉之前以字段name建立的普通索引,以name和age两个字段建立联合索引,sql命令与建立后的索引树结构如下:

ALTER TABLE student DROP INDEX I_name;

ALTER TABLE student ADD INDEX I_name_age(name, age);

mysql的各类索引_第5张图片

那在我们再次执行如下sql后 

SELECT age FROM stu WHERE name = '小李';

流程为:

  1. 在name,age联合索引树上找到名称为小李的节点
  2. 此时节点索引里包含信息age 直接返回 12

这没有回表,查询速度快,也是覆盖索引.

 

如何确定这个查询是覆盖索引

当发起一个索引覆盖查询时,在explain的extra列可以看到using index的信息

mysql的各类索引_第6张图片

这里我们很清楚的看到Extra中Using index表明我们成功使用了覆盖索引

 

来源一:

MySQL 覆盖索引详解 - 掘金 (juejin.cn)

你可能感兴趣的:(mysql,mysql,数据库)