MySql 数据的索引

96 阅读8分钟

image.png 本文正在参加「技术专题19期 漫谈数据库技术」活动

什么是索引

MySQL 官方对索引的定义为:索引(Index)是帮助 MySQL 高效获取数据的数据结构。可以得到索引的本质: 索引是数据结构。
在数据库中查询出来的数据都是以页为单位的,如图示

数据库中的数据就像一本书,而索引就像字典的目录

在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据, 这样就可以在这些数据结构上实现高级查找算法。这种数据结构,就是索引。下图就是一种可能的索引方式示例:

左边是数据表,一共有两列七条记录,最左边的是数据记录的物理地址。为了加快 Col2 的查找,可以维护一个右边所示的二叉查找树,每个节点分别包含索引键值和一个指向对应数据记录物理地址的指针,这样就可以运用 二叉查找在一定的复杂度内获取到相应数据,从而快速的检索出符合条件的记录
一般来说索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储的磁盘上

索引的优缺点

优点
提高数据检索的效率,降低数据库的IO成本
通过索引列对数据进行排序,降低数据排序的成本,降低了CPU的消耗
缺点
虽然索引大大提高了查询速度,同时却会降低更新表的速度,如对表进行INSERT、UPDATE和DELETE。因为更新表时,MySQL不仅要保存数据,还要保存一下索引文件每次更新添加了索引列的字段,都会调整因为更新所带来的键值变化后的索引信息
实际上索引也是一张表,该表保存了主键与索引字段,并指向实体表的记录,所以索引列也是要占用空间

索引的分类

基本语法

创建

CREATE [UNIQUE] INDEX [indexName] ON table_name(column);

删除

DROP INDEX [indexName] ON table_name;

查看

SHOW INDEX FROM table_name;

分类

单值索引

定义:即一个索引只包含单个列,一个表可以有多个单列索引

语法:

--和表一起创建
CREATE TABLE customer (
id INT(10) UNSIGNED AUTO_INCREMENT,
customer_no VARCHAR(200),
customer_name VARCHAR(200), 
PRIMARY KEY(id), 
KEY (customer_name) --单值索引
);

--单独创建单值索引

CREATE INDEX idx_customer_name ON 表名(字段);

唯一索引
定义:索引列的值必须唯一,但允许有空值

语法:

--和表一起创建
CREATE TABLE customer (
id INT(10) UNSIGNED AUTO_INCREMENT,
customer_no VARCHAR(200),
customer_name VARCHAR(200), 
PRIMARY KEY(id), 
KEY (customer_name), --单值索引
UNIQUE (customer_no) --唯一索引
);

--单独创建唯一索引

CREATE UNIQUE INDEX idx_customer_no ON 表名(字段);

主键索引

定义:设定为主键后数据库会自动建立索引,innodb为聚簇索引

语法:

--和表一起创建
CREATE TABLE customer (
id INT(10) UNSIGNED AUTO_INCREMENT,
customer_no VARCHAR(200),
customer_name VARCHAR(200), 
PRIMARY KEY(id) --主键索引
);

--单独创建主键索引
ALTER TABLE customer ADD PRIMARY KEY 表名(字段);

--删除主键索引
ALTER TABLE customer DROP PRIMARY KEY;

--修改建主键索引
必须先删除掉(drop)原索引,再新建(add)索引

复合索引

定义:即一个索引包含多个列

语法:

--和表一起创建
CREATE TABLE customer (
id INT(10) UNSIGNED AUTO_INCREMENT,
customer_no VARCHAR(200),
customer_name VARCHAR(200), 
PRIMARY KEY(id), 
KEY (customer_name), --单值索引
UNIQUE (customer_no), --唯一索引
KEY (customer_no,customer_name) --复合索引
);

--单独创建复合索引
CREATE INDEX idx_no_name ON 表名(字段1,字段2);

MySQL的索引

B树与B+树



区别
B树的关键字和记录是放在一起的,叶子节点可以看作外部节点,不包含任何信息;B+树的非叶子节点中只有关键字和指向下一个节点的索引,记录只放在叶子节点中
在 B树中,越靠近根节点的记录查找时间越快,只要找到关键字即可确定记录的存在;而 B+树中每个记录 的查找时间基本是一样的,都需要从根节点走到叶子节点,而且在叶子节点中还要再比较关键字。从这个角度看 B树的性能好像要比 B+树好,而在实际应用中却是 B+树的性能要好些。因为 B+树的非叶子节点不存放实际的数据, 这样每个节点可容纳的元素个数比 B树多,树高比 B树小,这样带来的好处是减少磁盘访问次数。尽管 B+树找到 一个记录所需的比较次数要比 B树多,但是一次磁盘访问的时间相当于成百上千次内存比较的时间,因此实际中 B+树的性能可能还会好些,而且 B+树的叶子节点使用指针连接在一起,方便顺序遍历(例如查看一个目录下的所有 文件,一个表中的所有记录等),这也是很多数据库和文件系统使用 B+树的缘故

MySQL中的B+树

主键索引


MySQL在创建表时,会根据主键来创建主键索引(如果没有主键,会用一个隐藏值来作为主键)。主键索引所构建的B+树,表中所有的记录都存放在了树的最后一层。且与一般的B+树不同的是:叶子节点间的指针是双向的

复合索引


创建复合索引时,会将作为复合索引字段的值进行排序并放在B+树的最后一层中,同时还会将其对应的主键值放在其后。

a (主键)bcde
1234a

其中字段a为主键,字段bcd共同作为复合索引,此时存放在最后一层的数据就是:111(复合索引) 2(主键索引)

根据这个特点,可以看出复合索引具有以下使用方法

最佳左前缀:使用复合索引的顺序必须和创建的顺序一致

覆盖索引的同时,可以带上主键字段,如

SELECT a, b, c, d FROM t_emp;

因为主键字段和复合索引一起存放在了复合索引说产生的B+树的最后一层。如果需要a字段,无需进行全表扫描

如果进行范围查找,可能会进行全表扫描,这取决于处在范围内记录的多少

记录多会直接进行全表扫描 因为从复合索引映射到主键索引的次数过多,成本过高,

EXPLAIN SELECT * FROM t_emp WHERE age > 1;


记录少,先使用复合索引,然后映射到全表中的对应记录上

EXPLAIN SELECT age, name FROM t_emp WHERE age > 1;


使用覆盖索引(查询索引字段),无论记录多少,都会用到索引

EXPLAIN SELECT age, name FROM t_emp WHERE age > 1;

索引的使用场景

适合

  • 主键自动建立唯一索引

  • 频繁作为查询条件的字段应该创建索引

  • 查询中与其它表关联的字段,外键关系建立索引

  • 单键/组合索引的选择问题,组合索引性价比更高

  • 查询中排序的字段,排序字段若通过索引去访问将大大提高排序速度

  • 查询中统计或者分组字段

不适合

  • 表记录太少(有无索引差别不大)

  • 经常增删改的表或者字段

  • Where 条件里用不到的字段不创建索引

  • 过滤性不好的不适合建索引(重复性较高,比如国籍、性别之类的字段)

索引的失效场景

1.条件中用or,即使其中有条件带索引,也不会使用索引查询(这就是查询尽量不要用or的原因,用in吧);

注意:使用or,又想索引生效,只能将or条件中的每个列都加上索引

2.like 不能使用通配符 的模糊查询以%开头,索引失效;

3.数据类型的转换  如果列类型是字符串,那一定要在条件中将数据使用引号引用起来,否则不会使用索引;

4.谓词运算 不能对索引列进行函数运算,这也包括加减乘除的谓词运算,也会使索引失效。
例如

Explain select * from sunyang where id/2=:type_id;

5.如果MySQL预计使用全表扫描要比使用索引快,则不使用索引。

总结

对于后端程序员来说 数据库不可不学,明白MySql数据库的索引的使用方式 能帮忙解决日常开发中常常会遇到的问题,但行好事,莫问前程。

ok,这次就到这里,我是良杰哥哥,青山不改,绿水常流,咱们有缘再见。