Mysql数据库索引

文章目录

  • Mysql数据库索引
    • 概念
    • 索引利弊
    • 建立索引的场景
    • 索引类型
    • Mysql存储引擎
      • 概念
      • 存储引擎的数据结构分析
      • InnoDB的聚集索引和普通索引的回表
    • 覆盖索引
      • 概念
      • 实现覆盖索引
    • 如何优化索引
      • 覆盖索引
      • 创建索引
      • 排序的问题
      • like语句
      • 列运算
      • 不使用NOT IN

Mysql数据库索引

概念

数据库索引是对数据库表中一列或多列的值进行排序的一种结构,使用索引可快速访问数据库表中的特定信息。在数据库中,索引的含义与日常意义上的“索引”一词并无多大区别(想想小时候查字典),它是用于提高数据库表数据访问速度的数据库对象。

索引利弊

1、索引的好处

a、提高数据检索的效率,降低检索过程中必须要读取得数据量,降低数据库IO成本。

b、降低数据库的排序成本。因为索引就是对字段数据进行排序后存储的,如果待排序的字段与索引键字段一致,就在取出数据后不用再次排序了,因为通过索引取得的数据已满足排序要求。另外,分组操作是先排序后分组,所以索引同样可以省略分组的排序操作,降低内存与CPU资源的消耗。

2、索引的弊端

a、索引会增加 增、删、改操作所带来的IO量与调整索引的计算量。

b、索引要占用空间,随着数据量的不断增大,索引还会带来存储空间的消耗。

建立索引的场景

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

2、唯一性太差的字段不适合单独创建索引,即使频繁作为查询条件

3、增、删、改操作较多的数据库字段不适合建索引

索引类型

主键索引 PRIMARY KEY:
它是一种特殊的唯一索引,不允许有空值。一般是在建表的时候同时创建主键索引。注意:一个表只能有一个主键。
唯一索引 UNIQUE:
唯一索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一。

ALTER TABLE table_name ADD UNIQUE (column);

普通索引 INDEX:
这是最基本的索引,它没有任何限制。

ALTER TABLE table_name ADD INDEX index_name (column);

组合索引 INDEX:
即一个索引包含多个列,多用于避免回表查询。
因为MySQL索引查询会遵循最左前缀匹配的原则,即最左优先,在检索数据时从联合索引的最左边开始匹配。所以当我们创建一个联合索引的时候,如(key1,key2,key3),相当于创建了(key1)、(key1,key2)和(key1,key2,key3)三个索引,这就是最左匹配原则。

ALTER TABLE table_name ADD INDEX index_name(column1,column2, column3);

全文索引 FULLTEXT:
也称全文检索,是目前搜索引擎使用的一种关键技术。

ALTER TABLE table_name ADD FULLTEXT (column);

上述索引又可以分为两大类:聚集索引(主键索引)和非聚集索引。

Mysql存储引擎

概念

现如今,mysql存储引擎主要有InnoDB和Myisam两种,其中InnoDB支持事务、行锁,并且使用了聚集索引,myisam使用的是非聚集索引。mysql默认的存储引擎是InnoD。

存储引擎的数据结构分析

为什么mysql要选用B+树?

  1. 哈希结构适合键值对查询,不支持范围查找,而且容易造成哈希碰撞。碰撞:哈希函数可能对不同的key映射同一个结果。解决:链表链接碰撞数据,然后遍历查询。
  2. 二叉树在极端情况下会造成线性链表,时间复杂度编程O(n),效率低。
  3. 红黑树在自增字段下,会呈相似于线性链表,效率低。
  4. 平衡二叉树每个树节点只存储一个数据,导致查询时磁盘IO太多,效率低。
  5. B树一个节点里存储的是数据,所以它一个节点存储的数据有限。B 树的查找性能等于 O(h*logn),其中 h 为树高,n 为每个节点关键词的个数。
  6. B+树一个节点里存储的是索引,所以它可以存储大量的索引,叶子节点存储了大量的数据,所有的叶子节点用链表串联起来,便于查询。

InnoDB的聚集索引和普通索引的回表

create table user (
    id int primary key,
    name varchar(20),
    sex varchar(5),
    index(name)
)engine=innodb;

InnoDB聚集索引的叶子节点存储行记录,因此, InnoDB必须要有,且只有一个聚集索引:
1.如果表定义了PrimaryKey,则PrimaryKey就是聚集索引;
2.如果表没有定义PrimaryKey,则第一个not NULL unique列是聚集索引;
3.否则,InnoDB会创建一个隐藏的row-id作为聚集索引;
画外音: 所以PrimaryKey查询非常快,直接定位行记录。

InnoDB普通索引(非聚集索引)的叶子节点存储主键值。
画外音: 注意,不是存储行记录头指针,MyISAM的索引叶子节点存储记录指针。

举个栗子,不妨设有表:

t(id PK, name KEY, sex, flag);

画外音: id是聚集索引,name是普通索引。
表中有四条记录:

1, shenjian, m, A

3, zhangsan, m, A

5, lisi, m, A

9, wangwu, f, B
Mysql数据库索引_第1张图片
两个B+树索引分别如上图:

(1)id为PK,聚集索引,叶子节点存储行记录;

(2)name为KEY,普通索引,叶子节点存储PK值,即id;

既然从普通索引无法直接定位行记录,那普通索引的查询过程是怎么样的呢?
通常情况下,需要扫码两遍索引树。
例如:

select * from t where name='lisi'; 

执行过程:
Mysql数据库索引_第2张图片
如粉红色路径,需要扫码两遍索引树:

(1)先通过普通索引定位到主键值id=5;

(2)在通过聚集索引定位到行记录;

这就是所谓的回表查询,先定位主键值,再定位行记录,它的性能较扫一遍索引树更低。

覆盖索引

概念

MySQL的官网并没有有关的概念描述。
借用一下SQL-Server官网的说法。
Mysql数据库索引_第3张图片
即explain的输出结果Extra字段为Using index时,能够触发索引覆盖。
在这里插入图片描述
不管是SQL-Server官网,还是MySQL官网,都表达了:只需要在一棵索引树上就能获取SQL所需的所有列数据,无需回表,速度更快。

实现覆盖索引

常见的方法是:将被查询的字段,建立到联合索引里去。

create table user (
    id int primary key,
    name varchar(20),
    sex varchar(5),
    index(name)
)engine=innodb;

第一个SQL语句

explain select id,name from user where name='shenjian'; 

Mysql数据库索引_第4张图片
能够命中name索引,索引叶子节点存储了主键id,通过name的索引树即可获取id和name,无需回表,符合索引覆盖,效率较高。

第二个SQL语句:

explain select id,name,sex from user where name='shenjian'; 

Mysql数据库索引_第5张图片
能够命中name索引,索引叶子节点存储了主键id,但sex字段必须回表查询才能获取到,不符合索引覆盖,需要再次通过id值扫码聚集索引获取sex字段,效率会降低。

如果把(name)单列索引升级为联合索引(name, sex)就会和第一个SQL效果一样。

如何优化索引

覆盖索引

建立两列以上的索引,即可查询复合索引里的列的数据而不需要进行回表二次查询,依据最左前缀原则,我们在创建覆盖索引时应该将最常用作限制条件的列放在最左边,依次递减。上文已经介绍,这里不在此赘述。

创建索引

对于查询占主要的应用来说,索引显得尤为重要。很多时候性能问题很简单的就是因为我们忘了添加索引而造成的,或者说没有添加更为有效的索引导致。如果不加索引的话,那么查找任何哪怕只是一条特定的数据都会进行一次全表扫描,如果一张表的数据量很大而符合条件的结果又很少,那么不加索引会引起致命的性能下降。但是也不是什么情况都非得建索引不可,比如性别可能就只有两个值,建索引不仅没什么优势,还会影响到更新速度,这被称为过度索引。

排序的问题

mysql查询只使用一个索引,因此如果where子句中已经使用了索引的话,那么order by中的列是不会使用索引的。因此数据库默认排序可以符合要求的情况下不要使用排序操作;尽量不要包含多个列的排序,如果需要最好给这些列创建复合索引。

like语句

一般情况下不鼓励使用like操作,如果非使用不可,如何使用也是一个问题。like “%aaa%” 不会使用索引而like “aaa%”可以使用索引。

列运算

select * from users where YEAR(adddate)<2007;
将在每个行上进行运算,这将导致索引失效而进行全表扫描,因此我们可以改成
select * from users where adddate<‘2007-01-01’;

不使用NOT IN

NOT IN不会使用索引将进行全表扫描。NOT IN可以用NOT EXISTS代替,id3则可使用id>3 or id<3来代替。

你可能感兴趣的:(mysql)