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= 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 ="北京"
三、全文索引:
在大量数据中,通过其中的某个关键字,就能找到该字段所属的记录行。全文索引在开发中很少用,因为其占用很大的物理空间和降低了记录修改性。
四. 索引在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的innodb引擎根据我们设的id自动为我们生成的一个主键B+树索引结构,我们给它取了个名字,叫主键索引.又叫做聚簇索引.
主键索引有两个特点:
- 按照主键的大小对用户记录和数据页进行排序,记录用单向链表连接,叶子节点使用双向链表连接;
- B+树的叶子节点保存了用户的完整记录。
3.普通索引(二级索引,非聚簇索引)
主键索引是在搜索条件为主键的时候才会发挥作用,但是我要以name='蝉沐风'
为搜索条件怎么办?
innodb的解决办法就是再创建一个B+树(我们称为name索引),这就是普通索引.
这棵普通索引的B+树和聚簇索引的B+树有点区别:
- 叶子节点存放的不再是完整的用户记录,而是只记录
name
列和主键值; - 数据页中存放的用户记录和目录项记录由原本的按照主键排序变为按照
name
列排序; - 目录项记录除了存储索引列(
name
)和页号之外,同时还存储了主键值;(大家可以想一想,为什么要存储主键值,因为name列可能不是唯一的,如果两个name相同,那么谁排前面谁排后面呢,这时候就要按主键值来排序了.)
有了这棵B+树,你就可以通过name
列快速找到主键值了,查找的方式和根据主键值查找用户记录的方式完全一样,只不过前者查到的是主键值,后者查找到的是一条完整的用户记录罢了。
你可能对字符串进行二分法感到有点奇怪,甚至没有接触过的相关知识的读者连对字符串进行排序都会觉得很诧异。其实在创建表的时候我们可以对字符串字段指定字符集和比较规则,如果你不指定,MySQL会默认给你设置,总之,MySQL总会找到一个方式对字符串进行排序。
现在得到主键的id了,然后根据主键id到主键索引中查找到完整的用户记录,这个过程叫做回表。如果没有为name
列设置唯一性约束,那就可能找到多个符合条件的主键id,多回几次表就可以了。
对name
这种单个列添加的索引叫做普通索引,也叫二级索引。
4.组合索引
(也叫复合索引、联合索引
)
假设我们为name
列和phone
列建立联合索引(注意我描述的顺序,name列在前,phone列在后,后面的图的顺序也就是按这个顺序排的),自然也是创建一棵B+树,这棵B+树和之前又稍微有点不同:
- 叶子节点存放的是
name
列、phone
列和主键值; - 目录项记录除了存储索引列(
name
、phone
)和页号之外,同时还存储了主键值;(大家可以想一想,为什么要存储主键值) - 数据页中存放的用户记录和目录项记录由原本的按照主键排序变为按照
name
列排序,如果name
列相同,那就按照phone
列排序;(如果phone
列再一样呢?你现在明白为什么要存储主键值了吗?)
再画个图吧(有点偷懒了哈,数据页号没换):
还是和二级索引一样,利用B+树快速定位到数据页,然后页内快速定位到记录,找到记录中的主键id,再回表,如果找到多条符合条件的记录,就多回几次表。