MySQL的索引分三类:单列索引(普通索引、唯一索引、主键索引)、多列索引(联合主键,组合索引)、全文索引
(1)普通索引(二级索引,非聚簇索引):MySQL中基本索引类型,没有什么限制,允许在定义索引的列中插入重复值和空值,纯粹是为了查询数据更快一点。
(2)唯一索引(二级索引,非聚簇索引):索引列中的值必须是唯一的,但是允许为空值。
(3)主键索引(聚簇索引):是一种特殊的唯一索引,非空且唯一。(主键约束,就是一个主键索引)。
主键索引与唯一索引的区别:
(1)主键是一种约束,唯一索引是一种索引,两者在本质上是不同的。
(2)主键创建后一定包含一个唯一性索引,唯一性索引并不一定是主键。
(3)唯一性索引列允许空值,而主键列不允许为空值。
(4)主键索引在创建时,已经默认为非空值+唯一索引了。
(5)一个表最多只能创建一个主键索引,但可以创建多个唯一索引。
(6)主键更适合那些不容易更改的唯一标识,如自动递增列、身份证号等。
(7)主键可以被其他表引用为外键,而唯一索引不能。
(也叫复合主键、组合主键
),一个表中有多个主键列组合在一起,叫联合主键,一个表中只能有一个联合主键.
create table test
(
name varchar(19),
id number,
value varchar(10),
primary key (name,id)
)
比如一个部门人员表,你不想单独建一个id列作为主键的话,就可以用 部门列 和 员工name 列都设为主键,作为表的联合主键,这样也可以保证表中每行数据都不重复.
(也叫复合索引、联合索引
),组合索引是不含主键索引的,是多个普通索引组合在一起的.一个表中可以有多个组合索引.
ALTER TABLE mytable ADD INDEX name_city_age (name,age,city);
把一个表的name,age,city三个非主键列设为一个组合索引,数据库实际上自动为我们设立了3个索引,分别是(name) (name,age) (name,age,city)
select col1,col2,col3 from test where col1=1 and col2=2
。那么MySQL可以直接通过遍历索引取得数据,而无需回表,这减少了很多的随机io操作。减少io操作,特别的随机io其实是dba主要的优化策略。所以,在真正的实际应用中,覆盖索引是主要的提升性能的优化手段之一。联合主键和组合索引都要遵守最左原则.
怎么查询才能保证全部走索引呢,那么就是要保证最左原则.
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= 20SELECT * 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 ="北京"
在大量数据中,通过其中的某个关键字,就能找到该字段所属的记录行。全文索引在开发中很少用,因为其占用很大的物理空间和降低了记录修改性。
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+条数据。
上图就是mysql的innodb引擎根据我们设的id自动为我们生成的一个主键B+树索引结构,我们给它取了个名字,叫主键索引.又叫做聚簇索引.
主键索引有两个特点:
主键索引是在搜索条件为主键的时候才会发挥作用,但是我要以name='蝉沐风'
为搜索条件怎么办?
innodb的解决办法就是再创建一个B+树(我们称为name索引),这就是普通索引.
这棵普通索引的B+树和聚簇索引的B+树有点区别:
name
列和主键值;name
列排序;name
)和页号之外,同时还存储了主键值;(大家可以想一想,为什么要存储主键值,因为name列可能不是唯一的,如果两个name相同,那么谁排前面谁排后面呢,这时候就要按主键值来排序了.)有了这棵B+树,你就可以通过name
列快速找到主键值了,查找的方式和根据主键值查找用户记录的方式完全一样,只不过前者查到的是主键值,后者查找到的是一条完整的用户记录罢了。
你可能对字符串进行二分法感到有点奇怪,甚至没有接触过的相关知识的读者连对字符串进行排序都会觉得很诧异。其实在创建表的时候我们可以对字符串字段指定字符集和比较规则,如果你不指定,MySQL会默认给你设置,总之,MySQL总会找到一个方式对字符串进行排序。
现在得到主键的id了,然后根据主键id到主键索引中查找到完整的用户记录,这个过程叫做回表。如果没有为name
列设置唯一性约束,那就可能找到多个符合条件的主键id,多回几次表就可以了。
对name
这种单个列添加的索引叫做普通索引,也叫二级索引。
(也叫复合索引、联合索引
)
假设我们为name
列和phone
列建立联合索引(注意我描述的顺序,name列在前,phone列在后,后面的图的顺序也就是按这个顺序排的),自然也是创建一棵B+树,这棵B+树和之前又稍微有点不同:
name
列、phone
列和主键值;name
、phone
)和页号之外,同时还存储了主键值;(大家可以想一想,为什么要存储主键值)name
列排序,如果name
列相同,那就按照phone
列排序;(如果phone
列再一样呢?你现在明白为什么要存储主键值了吗?)再画个图吧(有点偷懒了哈,数据页号没换):
还是和二级索引一样,利用B+树快速定位到数据页,然后页内快速定位到记录,找到记录中的主键id,再回表,如果找到多条符合条件的记录,就多回几次表。
覆盖索引(covering index ,或称为索引覆盖)即从非主键索引中就能查到的记录,而不需要查询主键索引中的记录,避免了回表的产生减少了树的搜索次数,显著提升性能。
例子1:id是主键,name是普通索引
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);
那在我们再次执行如下sql后
SELECT age FROM stu WHERE name = '小李';
流程为:
- 在name,age联合索引树上找到名称为小李的节点
- 此时节点索引里包含信息age 直接返回 12
这没有回表,查询速度快,也是覆盖索引.
如何确定这个查询是覆盖索引
当发起一个索引覆盖查询时,在explain的extra列可以看到using index的信息
这里我们很清楚的看到Extra中Using index表明我们成功使用了覆盖索引
来源一:
MySQL 覆盖索引详解 - 掘金 (juejin.cn)