文章目录
- MySQL
- 5. 索引的实现
- 5.1 B+ vs B
- 5.2 聚簇索引 VS 非聚簇索引
- 6. 索引操作
- 6.1 创建主键索引
- 6.2 创建唯一索引
- 6.3 创建普通索引
- 6.4 创建全文索引
- 6.5 查询索引
- 6.6 删除索引
MySQL
5. 索引的实现
因为MySQL和磁盘交互的基本单位为Page(页)。
MySQL 中要管理很多数据表文件,而要管理好这些文件,就需要 先描述,在组织 ,我们目前可以简单理解成一个个独立文件是有一个或者多个Page构成的。
MySQL 会默认按照主键给我们的数据进行排序,从上面的Page内数据记录可以看出,数据是有序且彼此关联的。
数据页与数据页之间通过指针连成双向链表。
在页模式中,只有一个功能,就是在查询某条数据的时候直接将一整页的数据加载到内存中,以减少硬盘IO次数,从而提高性能。但是,我们也可以看到,现在的页模式内部,实际上是采用了链表的结构。
但是多个Page彼此使用双链表链接起来,且每个Page内部的数据是基于链表的。那么,查找特定一条记录,也一定是线性查找,效率低。
针对上面的单页Page,我们引入目录。
此时给每个数据页建立起对应的目录,就是索引。
在单表数据不断被插入的情况下, MySQL 会在容量不足的时候,自动开辟新的Page来保存新的数据,然后通过指针的方式,将所有的Page组织起来——给Page也带上目录。
其实目录页的本质也是页,普通页中存的数据是用户数据,而目录页中存的数据是普通页的地址。
这个就是B+树。
Page分为目录页和数据页。目录页只放各个下级Page的最小键值。
查找的时候,自定向下找,只需要加载部分目录页到内存,即可完成算法的整个查找过程,大大减少了IO次数。
5.1 B+ vs B
InnoDB 在建立索引结构管理数据时,其他数据结构不合适的原因如下:
链表:线性遍历效率低。
二叉搜索树:可能退化成为线性结构。
AVL 和红黑树:虽是平衡或近似平衡的二叉结构,但相比多阶 B+树,树整体过高,导致系统与硬盘的 IO Page 交互更多。
Hash:MySQL 支持但 InnoDB 和 MyISAM 不支持。Hash 算法虽有时查找快(O(1)),但范围查找不行。
B 树:非叶子节点也存储数据,这会导致在相同大小的磁盘页中,B 树能存储的索引项比 B+ 树少。从而使得 B 树的阶数相对较低,树的高度可能更高,增加了磁盘 I/O 操作的次数。
B+ 树:非叶子节点只存储索引信息,磁盘页能容纳更多的索引项,进一步降低了树的高度,减少了 I/O 次数,提高了查询性能。
数据结构演示
B树:
B+树:
5.2 聚簇索引 VS 非聚簇索引
MyISAM 这种用户数据与索引数据分离的索引方案,叫做非聚簇索引。
MyISAM 引擎同样使用B+树作为索引结果,叶节点的data域存放的是数据记录的地址。
其中, MyISAM 最大的特点是,将索引Page和数据Page分离,也就是叶子节点没有数据,只有对应数据的地址。
mysql> create table mtest(
-> id int primary key,
-> name varchar(11) not null
-> )engine=MyISAM;
而InnoDB 是将索引和数据放在一起的。
所以,InnoDB 这种用户数据与索引数据在一起索引方案,叫做聚簇索引。
当然, MySQL 除了默认会建立主键索引外,我们用户也有可能建立按照其他列信息建立的索引,一般这种索引可以叫做辅助(普通)索引。
对于 MyISAM ,建立辅助(普通)索引和主键索引没有差别,无非就是主键不能重复,而非主键可重复。
mysql> create table itest(
-> id int primary key,
-> name varchar(11) not null
-> )engine=InnoDB;
6. 索引操作
6.1 创建主键索引
(1)在创建表的时候,直接在字段名后指定 primary key。
create table user1(id int primary key, name varchar(30));
(2)在创建表的最后,指定某列或某几列为主键索引。
create table user2(id int, name varchar(30), primary key(id));
(3)创建表以后再添加主键。
create table user3(id int, name varchar(30));
alter table user3 add primary key(id);
主键索引的特点:
一个表中,最多有一个主键索引,当然可以使符合主键。
主键索引的效率高(主键不可重复)。
创建主键索引的列,它的值不能为null,且不能重复。
主键索引的列基本上是int。
6.2 创建唯一索引
(1)在表定义时,在某列后直接指定unique唯一属性。
create table user4(id int primary key, name varchar(30) unique);
(2)创建表时,在表的后面指定某列或某几列为unique
create table user5(id int primary key, name varchar(30), unique(name));
(3)创建表以后再添加。
create table user6(id int primary key, name varchar(30));
alter table user6 add unique(name);
唯一索引的特点:
一个表中,可以有多个唯一索引。
查询效率高。
如果在某一列建立唯一索引,必须保证这列不能有重复数据。
如果一个唯一索引上指定not null,等价于主键索引。
6.3 创建普通索引
(1)在表的定义最后,指定某列为索引。
create table user8(id int primary key,
name varchar(20),
email varchar(30),
index(name)
);
(2)创建完表以后指定某列为普通索引。
create table user9(id int primary key, name varchar(20), email
varchar(30));
alter table user9 add index(name);
(3)创建一个索引名为 idx_name 的索引。
create table user10(id int primary key, name varchar(20), email
varchar(30));
create index idx_name on user10(name);
普通索引的特点:
一个表中可以有多个普通索引,普通索引在实际开发中用的比较多。
如果某列需要创建索引,但是该列有重复的值,那么我们就应该使用普通索引。
6.4 创建全文索引
当对文章字段或有大量文字的字段进行检索时,会使用到全文索引。MySQL提供全文索引机制,但是有要求,要求表的存储引擎必须是MyISAM,而且默认的全文索引支持英文,不支持中文。如果对中文进行全文检索,可以使用sphinx的中文版(coreseek)。
如果使用如下查询方式,虽然查询出数据,但是没有使用到全文索引。
可以用explain工具看一下,是否使用到索引。
key为null表示没有用到索引。
如何使用全文索引呢?
通过explain来分析这个sql语句。
6.5 查询索引
第一种方法: show keys from 表名;
第二种方法: show index from 表名;
第三种方法(信息比较简略): desc 表名;
6.6 删除索引
第一种方法-删除主键索引: alter table 表名 drop primary key;
第二种方法-其他索引的删除: alter table 表名 drop index 索引名; 索引名就是show keys from 表名中的 Key_name 字段
mysql> alter table user10 drop index idx_name;
第三种方法方法: drop index 索引名 on 表名
mysql> drop index name on user8
索引创建原则:
比较频繁作为查询条件的字段应该创建索引。
唯一性太差的字段不适合单独创建索引,即使频繁作为查询条件。
更新非常频繁的字段不适合作创建索引。
不会出现在where子句中的字段不该创建索引。
其他概念:
复合索引:
复合索引是指基于多个列创建的索引。 例如,在一个表中有 col1、col2 和 col3 列,如果创建了一个复合索引 (col1, col2, col3),那么在查询时可以利用这个索引来提高查询效率。
索引最左匹配原则:
这是指在使用复合索引进行查询时,必须从索引的最左侧开始匹配。 例如,如果复合索引是 (col1, col2, col3) ,那么查询条件中只有以 col1 开头,或者以 col1 和 col2 开头,或者以 col1、col2 和 col3 开头的查询才能使用到这个复合索引。
例如,如果有查询语句 WHERE col1 = 1 AND col2 = 2 或者 WHERE col1 = 1 可以使用该复合索引,但 WHERE col2 = 2 则无法使用。
索引覆盖:
索引覆盖是指查询所需的所有列的数据都可以从索引中直接获取,无需回表查询数据行。
例如,如果有一个索引 (col1, col2) ,并且查询语句只是 SELECT col1, col2 FROM table ,那么就实现了索引覆盖,因为所需的列都在索引中可以直接获取,不需要再根据索引找到数据行获取其他列的值。 这样可以大大提高查询效率。