zl程序教程

您现在的位置是:首页 >  数据库

当前栏目

MySQL 索引

2023-09-27 14:26:01 时间

索引的优缺点

索引的优点如下:

  • 索引大大减小了服务器需要扫描的数据量。
  • 索引可以帮助服务器避免排序和临时表。
  • 索引可以将随机 I/O 变成顺序 I/O。

索引的缺点如下:

  • 虽然索引大大提高了查询速度,同时却会降低更新表的速度,如对表进行 INSERT、UPDATE 和 DELETE。因为更新表时,MySQL 不仅要保存数据,还要保存索引文件。
  • 建立索引会占用磁盘空间的索引文件。一般情况这个问题不算严重,但如果你在一个大表上创建了多种组合索引,且伴随大量数据量插入,索引文件大小也会快速膨胀。
  • 如果某个数据列包含许多重复的内容,为它建立索引就没有太大的实际效果。
  • 对于非常小的表,大部分情况下简单的全表扫描更高效。

一. 普通索引

作用:加速查找

# 1.创建表时直接创建索引
create table user(
    nid int not null auto_increment primary key,
    name varchar(32) not null,
    email varchar(64) not null,
    extra text,
    index ix_name (name)        # ix_name 索引名,括号后面指定索引所在的列
);

# 2.创建表后再创建索引
create index ix_name on user(name);
#注意:对于创建索引时如果是BLOB 和 TEXT 类型,必须指定length,例如使用列名称的前10个字符:
create index ix_extra on user(extra(10));

# 3.删除索引
drop ix_name on user;

# 4.查看索引
show index from user;

二. 唯一索引

作用:加速查找,约束列数据不能重复,数据可以为 null

# 1.创建表时直接创建唯一索引
create table user(
    nid int not null auto_increment primary key,
    name varchar(32) not null,
    email varchar(64) not null,
    extra text,
    unique ix_name (name)        
);

# 2.创建表后再创建唯一索引
create  unique index ix_name on user(name);

# 3.删除唯一索引
drop unique ix_name on user;

三. 主键索引

作用:加速查找,约束列数据不能重复,数据不能为 null

# 1.创建表时直接创建主键索引
create table user(
    nid int not null auto_increment primary key,    # 指定 nid 为主键索引
    name varchar(32) not null,
    email varchar(64) not null,
    extra text      
);

# 2.创建表后添加主键索引
alter table user add primary key(nid);

# 3.删除主键索引
alter table user drop primary key;

四. 组合索引

作用:多列可以创建一个索引文件

# 1.创建组合索引
create table user(
    nid int not null auto_increment primary key,   
    name varchar(32) not null,
    email varchar(64) not null,
    extra text      
);

# 将 name 和 email 两列组合成一个索引
create index ix_name_email on user(name,email);

# 组合索引遵循最左前缀,即 where条件必须跟 name 列才会使用索引。

覆盖索引

只需要在索引表中就能获取到数据。

合并索引

# 有两个单独的索引,搜索时使用两个索引
create table user(
    nid int not null auto_increment primary key,
    name varchar(32) not null,
    email varchar(64) not null,
    extra text,
    index ix_name(name) ,
    index ix_email(email)
);

select * from user where name = 'klvchen' or email = 'klvchen@126.com'; 

回表查询

比如你创建了 name, age 索引 name_age_index,查询数据时使用了:

select * from table where name ='陈哈哈' and age = 26;

由于附加索引中只有 name 和 age,因此命中索引后,数据库还必须回去聚集索引中查找其他数据,这就是回表,这也是你背的那条:少用 select * 的原因。

索引覆盖

结合回表会更好理解,比如上述 name_age_index 索引,有查询:

select name, age from table where name ='陈哈哈' and age = 26;

此时 select 的字段 name,age 在索引 name_age_index 中都能获取到,所以不需要回表,满足索引覆盖,直接返回索引中的数据,效率高。是 DBA 同学优化时的首选优化方式。

最左前缀原则

B+树的节点存储索引顺序是从左向右存储,在匹配的时候自然也要满足从左向右匹配。

通常我们在建立联合索引的时候,也就是对多个字段建立索引,相信建立过索引的同学们会发现,无论是 Oracle 还是 MySQL 都会让我们选择索引的顺序。

比如我们想在 a,b,c 三个字段上建立一个联合索引,我们可以选择自己想要的优先级,a、b、c,或者是 b、a、c 或者是 c、a、b 等顺序。

为什么数据库会让我们选择字段的顺序呢?不都是三个字段的联合索引么?这里就引出了数据库索引的最左前缀原理。

在我们开发中经常会遇到明明这个字段建了联合索引,但是 SQL 查询该字段时却不会使用索引的问题。

比如索引 abc_index:(a,b,c)是 a,b,c 三个字段的联合索引,下列 sql 执行时都无法命中索引 abc_index 的。

select * from table where c = '1';
select * from table where b ='1' and c ='2';

以下三种情况却会走索引:

select * from table where a = '1';
select * from table where a = '1' and b = '2';
select * from table where a = '1' and b = '2'  and c='3';

索引 abc_index:(a,b,c),只会在(a)、(a,b)、(a,b,c)三种类型的查询中使用。
其实这里说的有一点歧义,其实(a,c)也会走,但是只走 a 字段索引,不会走 c 字段。
另外还有一个特殊情况说明下,下面这种类型的也只会有 a 与 b 走索引,c 不会走。

select * from table where a = '1' and b > '2'  and c='3';

像上面这种类型的 sql 语句,在 a、b 走完索引后,c 已经是无序了,所以 c 就没法走索引,优化器会认为还不如全表扫描 c 字段来的快。

最左前缀:顾名思义,就是最左优先,上例中我们创建了 a_b_c 多列索引,相当于创建了(a)单列索引,(a,b)组合索引以及(a,b,c)组合索引。

因此,在创建多列索引时,要根据业务需求,where 子句中使用最频繁的一列放在最左边。

索引下推优化

还是索引 name_age_index,有如下 sql:

select * from table where name like '陈%' and age > 26;

该语句有两种执行可能:
命中 name_age_index 联合索引,查询所有满足 name 以"陈"开头的数据, 然后回表查询所有满足的行。

命中 name_age_index 联合索引,查询所有满足 name 以"陈"开头的数据,然后顺便筛出 age>20 的索引,再回表查询全行数据。

显然第 2 种方式回表查询的行数较少,I/O 次数也会减少,这就是索引下推。所以不是所有 like 都不会命中索引。

使用索引注意事项

  • 避免使用select *
  • count(1)或count(列) 代替 count(*)
  • 创建表时尽量时 char 代替 varchar
  • 表的字段顺序固定长度的字段优先
  • 组合索引代替多个单列索引(经常使用多个条件查询时)
  • 尽量使用短索引;
    例如,如果有一个 char(255)的列,如果在前 10 个或 20 个字符内,多数值是惟一的,那么就不要对整个列进行索引。短索引不仅可以提高查询速度而且可以节省磁盘空间和 I/O 操作。
  • 使用连接(JOIN)来代替子查询(Sub-Queries)
  • 连表时注意条件类型需一致
  • 索引散列值(重复少)不适合建索引,例:性别不适合
  • 索引不会包含有 null 值的列;
    只要列中包含有 null 值都将不会被包含在索引中,复合索引中只要有一列含有 null 值,那么这一列对于此复合索引就是无效的。所以我们在数据库设计时建议不要让字段的默认值为 null。
  • 索引列排序;
    查询只使用一个索引,因此如果 where 子句中已经使用了索引的话,那么 order by 中的列是不会使用索引的。
  • like 语句操作;
    一般情况下不推荐使用 like 操作,如果非使用不可,如何使用也是一个问题。like “%陈%” 不会使用索引而 like “陈%”可以使用索引。
  • 不要在列上进行运算
    这将导致索引失效而进行全表扫描,例如:
   SELECT * FROM table_name WHERE YEAR(column_name)<2017;
  • 不使用 not in 和 <> 操作;
    这不属于支持的范围查询条件,不会使用索引。