0
点赞
收藏
分享

微信扫一扫

MySQL索引基础入门详解

索引:系统根据某种算法,将已有的数据(未来可能新增的数据),单独建立一个文件。文件能够实现快速的匹配数据,并且能够快速的找到对应表中的记录。但是添加索引会导致插入、更新变慢–因为需要去更新相关的索引文件。

索引用于快速找出在某个列中有一特定值的行,不使用索引,MySQL必须从第一条记录开始读完整个表,直到找出相关的行,表越大,查询数据所花费的时间就越多,如果表中查询的列有一个索引,MySQL能够快速到达一个位置去搜索数据文件。

索引是一个单独的、物理的数据库结构,它是​​某个表中一字段或若干字段值​​​的集合 。表的存储由两部分组成,​​一部分用来存放数据 ,另一部分存放索引页面​​。通常,索引页面相对于数据页面来说小得多。数据检索花费的大部分开销是磁盘读写,没有索引就需要从磁盘上读表的每一 个数据页,如果有索引,则只需查找索引页面就可以了。所以建立合理的索引,就能加速数据的检索过程。

【1】索引概述

① 索引的意义

  • ① 提升查询数据的效率;
  • ② 约束数据的有效性(唯一性等);

缺点:

  • 创建索引和维护索引要耗费时间,并且随着数据量的增加所耗费的时间也会增加;
  • 索引本身会产生索引文件,有时候甚至会比数据文件还大,非常耗费磁盘空间。
  • 当对表中的数据进行增加、删除、修改时,索引也需要动态的维护,降低了数据的维护速度。

② 索引增加的前提条件

  • 如果某个字段要作为查询条件经常使用,那么可以使用索引;
  • 如果某个字段需要进行数据的有效性约束,也可能使用索引(主键,唯一键)。
  • 对经常更新的表就避免对其进行过多的索引,对经常用于查询的字段应该创建索引,
  • 数据量小的表最好不要使用索引,因为由于数据较少,可能查询全部数据花费的时间比遍历索引的时间还要短,索引就可能不会产生优化效果。
  • 在一同值少的列上(字段上)不要建立索引,比如在学生表的"性别"字段上只有男,女两个不同值。相反的,在一个字段上不同值较多可是建立索引。

③ MySQL中索引的分类

索引按是否聚集可以分为聚簇索引和非聚簇索引两种,聚簇索引是按照数据存放的物理位置为顺序的,而非聚簇索引就不一样了;聚簇索引能提高多行检索的速度,而非聚簇索引对于单行的检索很快。

按照索引类别可以分为以下四种:

  • 主键索引:primary key ;
  • 唯一索引:unique key ;
  • 全文索引:fulltext index ;
  • 普通索引:index 。

这里需要注意,全文索引。MySQL从3.23.23版开始支持全文索引和全文检索,FULLTEXT索引仅可用于 MyISAM 表;他们可以从CHAR、VARCHAR或TEXT列中作为CREATE TABLE语句的一部分被创建,或是随后使用ALTER TABLE 或CREATE INDEX被添加。

【2】索引表操作实践

① 添加/删除普通索引-index

  • 添加索引,指定名字为index_name;
alter table tb_sys_user
add index index_name(column_name)
USING BTREE;
  • 删除索引,如果没有指定名字,默认使用列名
alter table tb_sys_user drop index index_name
  • 添加组合索引
alter table tb_sys_user add index index_name(column1,column2...)
  • 添加唯一索引
ALTER TABLE `tb_user_add`
ADD UNIQUE INDEX `age_index` (`age`) USING HASH;

这里可以发现,USING BTREE/USING HASH 分别是使用不同的算法进行索引。

② 组合索引(最左前缀)

平时用的SQL查询语句一般都有比较多的限制条件,所以为了进一步榨取MySQL的效率,就要考虑建立组合索引。例如针对表user中字段user_name和user_pass建立一个组合索引:​​ALTER TABLE userADD INDEX index_name_pass (user_name(50),user_pass(10))​​。建立这样的组合索引,其实是相当于分别建立了下面两组组合索引:

  • user_name,user_pass
  • user_name

为什么没有user_pass这样的组合索引呢?这是因为MySQL组合索引​​“最左前缀”​​的结果。简单的理解就是只从最左面的开始组合。并不是只要包含这两列的查询都会用到该组合索引,如下面的几个SQL所示:

explain select * from `user` where user_name='admin' and user_pass='123456';

explain select * from `user` where user_name='admin' ;

explain select * from `user` where user_pass='123456';

MySQL索引基础入门详解_索引
MySQL索引基础入门详解_索引_02
MySQL索引基础入门详解_字段_03

③ 查看某个表的索引

show index from table_name;

MySQL索引基础入门详解_数据_04

【3】索引的优化

过多的使用索引将会造成滥用。因此索引也会有它的缺点:虽然索引大大提高了查询速度,同时却会降低更新表的速度,如对表进行INSERT、UPDATE和DELETE。因为更新表时,MySQL不仅要保存数据,还要保存一下索引文件。建立索引会占用磁盘空间的索引文件。一般情况这个问题不太严重,但如果你在一个大表上创建了多种组合索引,索引文件的会膨胀很快。

索引只是提高效率的一个因素,如果你的MySQL有大数据量的表,就需要花时间研究建立最优秀的索引,或优化查询语句。下面是一些总结以及收藏的MySQL索引的注意事项和优化方法。

① 何时使用聚集索引或非聚集索引

动作描述

使用聚集索引

使用非聚集索引

列经常被分组排序

使用

使用

返回某范围内的数据

使用

不使用

一个或极少不同值

不使用

不使用

小数目的不同值

使用

不使用

大数目的不同值

不使用

使用

频繁更新的列

不使用

使用

外键列

使用

使用

主键列

使用

使用

频繁修改索引列

不使用

使用

② 索引列的数据类型

  • 越小的数据类型通常更好
    越小的数据类型通常在磁盘、内存和CPU缓存中都需要更少的空间,处理起来更快。
  • 简单的数据类型更好
    整型数据比起字符,处理开销更小,因为字符串的比较更复杂。在MySQL中,应该用内置的日期和时间数据类型,而不是用字符串来存储时间;以及用整型数据类型存储IP地址。
  • 索引不会包含有NULL值的列。
    只要列中包含有NULL值都将不会被包含在索引中,复合索引中只要有一列含有NULL值,那么这一列对于此复合索引就是无效的。所以我们在数据库设计时不要让字段的默认值为NULL。

③ 使用短索引

对串列进行索引,如果可能应该指定一个前缀长度。例如,如果有一个CHAR(255)的列,如果在前10个或20个字符内,多数值是惟一的,那么就不要对整个列进行索引。短索引不仅可以提高查询速度而且可以节省磁盘空间和I/O操作。

④ MySQL查询只使用一个索引

如下图所示,user_name和user_email都建立了索引,但是查询的时候只使用到了user_name:
MySQL索引基础入门详解_索引_05
再如下图,主键索引存在时,优先使用主键索引:
MySQL索引基础入门详解_索引_06

⑤ 索引列排序

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

⑥ like语句操作

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

⑦ 不要在列上进行运算

例如:​​select * from users where YEAR(adddate)<2007​​​,将在每个行上进行运算,这将导致索引失效而进行全表扫描,因此我们可以改成:​​select * from users where adddate<’2007-01-01′​​​。
MySQL索引基础入门详解_数据_07
MySQL索引基础入门详解_字段_08
最后总结一下,MySQL只对一下操作符才使用索引:​​​<,<=,=,>,>=,between,in,以及某些时候的like(不以通配符%或_开头的情形)​​​。而理论上每张表里面最多可创建​​16​​个索引,不过除非是数据量真的很多,否则过多的使用索引也不是那么好玩的。

【4】BTREE和HASH索引

① B-Tree索引

假设有如下一个表:

CREATE TABLE People (
last_name varchar(50) not null,
first_name varchar(50) not null,
dob date not null,
gender enum('m', 'f') not null,
key(last_name, first_name, dob)
);

其索引包含表中每一行的last_name、first_name和dob列。其结构大致如下:
MySQL索引基础入门详解_字段_09
索引存储的值按索引列中的顺序排列。可以利用B-Tree索引进行全关键字、关键字范围和关键字前缀查询,当然,如果想使用索引,你必须保证按索引的最左边前缀(leftmost prefix of the index)来进行查询。

(1)匹配全值(Match the full value):对索引中的所有列都指定具体的值。例如,上图中索引可以帮助你查找出生于1960-01-01的Cuba Allen。
(2)匹配最左前缀(Match a leftmost prefix):你可以利用索引查找last name为Allen的人,仅仅使用索引中的第1列。
(3)匹配列前缀(Match a column prefix):例如,你可以利用索引查找last name以J开始的人,这仅仅使用索引中的第1列。
(4)匹配值的范围查询(Match a range of values):可以利用索引查找last name在Allen和Barrymore之间的人,仅仅使用索引中第1列。
(5)匹配部分精确而其它部分进行范围匹配(Match one part exactly and match a range on another part):可以利用索引查找last name为Allen,而first name以字母K开始的人。
(6)仅对索引进行查询(Index-only queries):如果查询的列都位于索引中,则不需要读取元组的值。

由于B-树中的节点都是顺序存储的,所以可以利用索引进行查找(找某些值),也可以对查询结果进行ORDER BY。当然,使用B-tree索引有以下一些限制:

  • (1) 查询必须从索引的最左边的列开始。关于这点已经提了很多遍了。例如你不能利用索引查找在某一天出生的人。
  • (2) 不能跳过某一索引列。例如,你不能利用索引查找last name为Smith且出生于某一天的人。
  • (3) 存储引擎不能使用索引中范围条件右边的列。例如,如果你的查询语句为​​WHERE last_name="Smith" AND first_name LIKE 'J%' AND dob='1976-12-23',​​则该查询只会使用索引中的前两列,因为LIKE是范围查询。

② hash索引

MySQL中,只有MyISAM存储引擎显示支持hash索引,是MyISAM表的默认索引类型,尽管MyISAM表也可以使用B-Tree索引。MyISAM存储引擎支持非唯一hash索引,这在数据库领域是罕见的,如果多个值有相同的hash code,索引把它们的行指针用链表保存到同一个hash表项中。

假设创建如下一个表:

CREATE TABLE testhash (
fname VARCHAR(50) NOT NULL,
lname VARCHAR(50) NOT NULL,
KEY USING HASH(fname)
) ENGINE=MEMORY;

包含的数据如下:
MySQL索引基础入门详解_字段_10
假设索引使用hash函数f( ),如下:

f('Arjen') = 2323

f('Baron') = 7437

f('Peter') = 8784

f('Vadim') = 2458

此时,索引的结构大概如下:
MySQL索引基础入门详解_索引_11
Slots是有序的,但是记录不是有序的。当你执行

mysql> SELECT lname FROM testhash WHERE fname='Peter';

MySQL会计算​​’Peter’​​​的hash值,然后通过它来查询索引的行指针。因为​​f('Peter') = 8784​​​,MySQL会在索引中查找8784,得到指向记录3的指针。
因为索引自己仅仅存储很短的值,所以,索引非常紧凑。Hash值不取决于列的数据类型,一个TINYINT列的索引与一个长字符串列的索引一样大。

Hash索引有以下一些限制:

  • (1)由于索引仅包含hash code和记录指针,所以,MySQL不能通过使用索引避免读取记录。但是访问内存中的记录是非常迅速的,不会对性造成太大的影响。
  • (2)不能使用hash索引排序。
  • (3)Hash索引不支持键的部分匹配,因为是通过整个索引值来计算hash值的。
  • (4)Hash索引只支持等值比较,例如使用​​=,IN( )和<=>​​​。对于​​WHERE price>100​​并不能加速查询。

参考博文:
​MySQL索引使用策略及优化实例MySQL索引使用和优化MySQL索引背后的数据结构及算法原理


举报

相关推荐

0 条评论