MySQL 对数据的查询本质上就是与磁盘进行 IO 交换,当一个数据库的记录数量非常庞大时,MySQL 可能就需要进行大量的 IO 才能找到对应的数据,IO 对性能的消耗十分庞大,如果没有索引,MySQL 在查询数据时就需要大量时间检索才能返回结果,甚至在查询的过程中就挂掉了。
索引本质上就是一种数据结构,使数据通过某种有序的结构管理起来,当 MySQL 需要查询一个数据时,就会根据索引构建的有序关系,快速的找到这个数据在磁盘中的位置,减少 IO 消耗。
我们都知道,CPU 访问内存的速度要远快于访问磁盘的速度,MySQL 提高检索速度的策略是:利用索引构建数据的有序关系后,还要占用高内存来提升数据的访问速度。 系统与磁盘交互的基本单位是块,每块 4 K B 4\space \rm{KB} 4 KB 大小,而 MySQL 的内存,如 InnoDB
与磁盘进行 IO 的大小是 16 K B 16\space \rm{KB} 16 KB :
mysql> SHOW GLOBAL STATUS LIKE 'innodb_page_size';
+------------------+-------+
| Variable_name | Value |
+------------------+-------+
| Innodb_page_size | 16384 |
+------------------+-------+
1 row in set (0.01 sec)
即当 MySQL 需要到磁盘中检索数据时,一次会带上 16 K B 16\space \rm{KB} 16 KB 大小的数据到 MySQL 的**内存池(buffer pool)**中,接下来如果需要访问的数据在这 16 K B 16\space \rm{KB} 16 KB 中,就能快速地返回结果。
MySQL 与磁盘交互也是通过系统的,系统与磁盘交互的基本单位是 4 K B 4\space \rm{KB} 4 KB ,那么实践执行 MySQL 的交互就需要系统进行 4 次磁盘交互,才能将 16 K B 16\space \rm{KB} 16 KB 的数据加载到内存中(但可能也会有优化)。
索引本质上就是一种数据结构,其中 InnoDB
和 MyISAM
使用 B+ 树实现,MEMORY
默认使用哈希索引,这里主要介绍最常见的 InnoDB
索引。
B+ 树的特点是叶子节点存储有效数据,而其他节点都是其子节点的索引。形象地说,叶子节点就相当于一本书的内容,而叶子节点的父结点就是这本书的目录,父结点的父结点就是目录的目录。B+ 树的优点就在于,即使存储的数据量庞大,但树的高度很低。且叶子节点兄弟之间存在指向,顺序访问方便,对数据库来说是一种十分合适的数据结构。
InnoDB
对 B+ 树进行了一点小小的改造:不只是叶子节点相连,非叶子节点之间也会相连。InnoDB
使非叶子节点作为目录节点,目录节点内存在多个索引,指向不同的叶子节点。目录节点的兄弟节点之间连接,当检索一个数据时,在第一个目录节点没有找到,也能迅速跳转到它的兄弟节点进行检索,一定程度上提高了检索速度。
InnoDB
中每个 page 就是一个节点,占 16 K B 16\space \rm{KB} 16 KB 大小。一个叶子节点包含了 16 K B 16\space \rm{KB} 16 KB 的数据,一个目录节点包含了 16 K B 16\space \rm{KB} 16 KB 的子节点索引,当目录节点也不够存时,就会增加一个目录节点的目录节点,占 16 K B 16\space \rm{KB} 16 KB 大小。
聚簇索引就是上图展示,InnoDB
使用的 B+ 树,叶子节点既包含了内部目录又包含了数据的结构。而 MyISAM
使用的数据结构也是 B+ 树,但它使用的是非聚簇索引,即叶子节点只有额外的指针存储对应数据的地址。MyISAM
最大的特点是将索引 page 与数据 page 分离开来
但聚簇索引和非聚簇索引是相对于主键索引来说的,如果用户创建了第二个辅助索引,InnoDB
和 MyISAM
都会使用非聚簇索引来存储这种索引结构,以此来节省空间。
索引本质上就是一种数据结构,是一种用空间换时间的策略,每个索引都会创建一个新的数据结构来构建索引关系。
一个表中,只能有一个主键索引,主键索引的效率很高(因为主键不能重复),创建主键的列值不能为 NULL
,一般我们会在 int
类型上使用主键。
使用方法:
-- 1.在字段后面指定primary key
create table tb_name(id int primary key, name varchar(10));
-- 2.在创建表的最后,指定哪个字段为primary key
create table tb_name(id int, name varchar(10), primary key(id));
-- 3.在创建表之后再添加主键
create table tb_name(id int, name varchar(10));
alter table tb_name add primary key(id);
一个表中可以有多个唯一索引,唯一索引的查询效率高,使用唯一索引的列不允许有相同的值,但允许为 NULL
(如果指定该字段为 NOT NULL
效果等价于主键索引)。
使用方法:
-- 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);
一个表中可以有多个普通索引,使用普通索引的列允许有多个相同的值,在实际开发中,我们也只在需要创建索引、且该列有重复的值时使用。
使用方法:
-- 1.在表的定义最后,指定某列为index索引
create table user8(id int primary key,name varchar(10),index(name));
-- 2.创建完表以后指定某列为index索引
create table user9(id int primary key, name varchar(20), alter table user9 add index(name);
-- 3.创建一个索引名为idx_name的索引
create table user10(id int primary key, name varchar(20))
create index idx_name on user10(name);
当字段存储的内容是大量的文字,就需要全文索引来对字段中的内容进行索引。但 MySQL 要求使用全文索引的表的存储引擎必须是 MyISAM,且默认的全文索引支持英文,不支持中文。如果对中文进行全文检索,可以使用 sphinx 的中文版 coreseek。
使用方法:
CREATE TABLE articles (
id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
title VARCHAR(200),
body TEXT,
FULLTEXT (title,body)
)engine=MyISAM;
show keys from tb_name;
或
show index from tb_name;
删除主键索引:
alter table tb_name drop primary key;
删除其他索引:
alter table tb_name drop index 索引名;
注意索引名是使用索引的字段。
或者使用
drop index 索引名 on tb_name;