现在的位置: 首页 > 综合 > 正文

索引

2017年12月12日 ⁄ 综合 ⁄ 共 4716字 ⁄ 字号 评论关闭

一、为什么要使用索引

无索引的表就是一个无序的行集,如果我们要查询属性为特定值的某些记录,那么必须扫描所有的行,这是一个全表扫描,如果表中只有少数几条记录与搜索条件匹配,那么其效率是非常低的;如我们从一张学生表中查找数学不及格的同学名字及其分数,那么当同学的数量非常大时,全表扫描就不是一个好的做法。如果我们在分数这个字段上建立一个索引,此索引包括表中每行的一项,并且是排序的,那么我们就可以快速定位到第一个匹配的值,以节省大量的搜索时间,并且这样查出来的记录是按照分数排序的。也就是说,索引消除了全表扫描,极大的加快了搜索的速度。

表的数据行存放在数据文件中,而索引值存放在索引文件中,一个表上可以有不止一个索引,它们都保存在一个索引文件中。索引文件中的每个索引由排过序的用来快速访问数据文件的键记录数组成。

一个例子:有三张表t1,t2和t3,它们均只有一列c1,c2和c3,每个表值均为由1到1000的1000个值组成的行,对于如下的操作

SELECT c1,c2,c3 FROM t1,t2,t3 WHERE t1.c1=t2.c2 AND t1.c1=t3.c3

如果表中没有建立索引,那么需要对所有可能的1000*1000*1000条行记录进行筛选;反之如果建立了索引,我们取出t1的值,在t2的索引中找到与t1的值匹配的行,然后在t3的索引中找到与t1的值匹配的行,这样我们只全表扫描了t1一个表,而对于t2和t3则通过索引直接取出这些表的行。索引的作用:1.加速WHERE子句中与条件匹配的行的搜索,或者说在执行连接时加快了与其他表中的行匹配的行的搜索;2.使用MAX和MIN函数时能够快速找到索引列的最小和最大值;3.能够利用索引来完成ORDER BY子句的排序操作;4.有时可以避免对整个数据文件的读取,假如从一个索引列中选择值,而且不选择表中其它列,这是通过对索引值的读取,就已经得到了读取文件所要的值,没有对相同的值进行两次读取的必要,因此无需涉及数据文件。

二、索引的弊端

1.索引要占用磁盘空间,如果有大量索引,索引文件可能会比数据文件更快的达到最大的文件尺寸;

2.索引文件加快了检索,但是增加了插入删除以及更新索引列中的值的时间,因为写操作不仅涉及数据行,还常常涉及索引,一个表拥有的索引越多,则写操作的平均性能下降就越大;

三、选择索引

1.搜索的索引列,不一定是所要选择的列,最适合索引的列是出现在WHERE子句中的列,或者连接子句中的列,而不是出现在SELECT关键字后的选择列表中的列;

2.对于唯一值的列,索引的效果最好,而具有很多重复值的列,其索引效果差,如存放年龄的列就比存放性别的列更适合做索引列;

3.使用短索引,如果对串列进行索引,应该制定一个前缀的长度,只要有可能就这样做。如果有一个char(200)列,如果在前10个或20个字符内,多数值是唯一的,那么就不要对整个列进行索引,对前10个或20个字符进行索引能够节省大量的索引空间,也能够使查询更快。对于较短的索引键值,索引高速缓存中的块能够更多的键值,因此,mysql可以在内存中 容纳更多的键值,这增加了找到行而不用读取索引中较多块的可能。

4.利用最左前缀,在创建一个n列索引时,实际上是创建了n个索引,多列索引可以起到几个索引的作用,因为可以利用索引中最左边的列集来进行匹配行,这样的列集为最左前缀。例如某个表在state/city/zip三个列上有一个索引,那么state,city,zip或者state,city或者state都可以使用该索引,但是一个city或zip就不可以,也就是不能使用不涉及最左前缀的列;

5.索引可以用于"<"、">="等和between运算,在模式具有一个直接量前缀时,索引也可用于LIKE运算,如果只将某个列用于其它类型的运算如STRCMP(),对其索引没有意义;在以通配符&和_开头做查询时,mysql不会使用索引;

四、如何创建索引

普通索引:

①create index index_name on table_name(table_columns(length))

如果是char,varchar类型,length可以小于字段实际长度,如果是blob和text类型,必须指定length;

②alter table_name add index [index_name] on table_columns(length);

③create table table_name ([..],index index_name (table_columns(length));

唯一索引:

①create unique index index_name on table_name (table_columns(length));

②alter table_name add unique index_name on table_columns(length);

③create table table_name([...],unique index_name table_columns(length));

主键索引:

create table table_name(id int not null auto_increment,name varchar(19) not null,primary key(id));

删除索引:drop index index_name on table_name;

五、聚集索引和非聚集索引

①聚集索引:物理存储按照索引排序;非聚集索引:物理存储不按照索引排序;

②优缺点:聚集索引插入数据时速度慢(时间花费在“物理存储的排序”上,也就是首先要找到位置然后插入),查询数据比非聚集索引要快;

③索引是通过二叉树的数据结构来描述的,聚集索引的叶节点就是数据节点,二非聚集索引的叶节点仍然是索引节点,只不过有一个指针指向对应的数据块。

④两者的本质区别

我们先来看一道题:

假设有一8000条记录的表,表中每条记录在磁盘上占用1000字节,如果在一个10字节长的字段上建立非聚簇索引主键,需要二叉树节点16000个(这16000个节点中有8000个叶节点,每个页节点都指向一个数据记录),这样数据将占用8000条×1000字节 /8K字节=1000个页面;索引将占用16000个节点×10字节/8K字节=20个页面,共计1020个页面。同样一张表,如果我们在对应字段上建立聚簇索引主键,由于聚簇索引的页节点就是数据节点,所以索引节点仅有8000个,占用10个页面,数据仍然占有1000个页面。

在执行插入操作时,非聚簇索引的主键为什么比聚簇索引主键要快。主键约束要求主键不能出现重复,那么SQL Server是怎么知道不出现重复的呢?唯一的方法就是检索。对于非聚簇索引,只需要检索20个页面中的16000个节点就知道是否有重复,因为所有主键键值在这16000个索引节点中都包含了。但对于聚簇索引,索引节点仅仅包含了8000个中间节点,至于会不会出现重复必须检索另外8000个页数据节点才知道,那么相当于检索10+1000=1010个页面才知道是否有重复。所以聚簇索引主键的插入速度要比非聚簇索引主键的插入速度慢很多。

让我们再来看看数据检索的效率,如果对上述两表进行检索,在使用索引的情况下(有些时候SQL Server执行计划会选择不使用索引,不过我们这里姑且假设一定使用索引),对于聚簇索引检索,我们可能会访问10个索引页面外加1000个数据页面得到结果(实际情况要比这个好),而对于非聚簇索引,系统会从20个页面中找到符合条件的节点,再映射到1000个数据页面上(这也是最糟糕的情况),比较一下,一个访问了1010个页面而另一个访问了1020个页面,可见检索效率差异并不是很大。所以不管非聚簇索引也好还是聚簇索引也好,都适合排序,聚簇索引仅仅比非聚簇索引快一点。

⑤两种索引的使用情景

非聚簇对于更新肯定是有优势的,而它在检索的性能损失也不会太大,所以能不用聚簇当然是最好的了,但是如果使用\order by的话,聚簇的优势也应该是很明显的。当你需要取出一定范围内的数据时,用聚簇索引也比用非聚簇索引好。例如,假设你用一个表来记录访问者在你网点上的活动。如果你想取出在一定时间段内的登录信息,你应该对这个表的DATETIME型字段建立聚簇索引。

对聚簇索引的主要限制是每个表只能建立一个聚簇索引。但是,一个表可以有不止一个非聚簇索引。实际上,对每个表你最多可以建立249个非聚簇索引。你也可以对一个表同时建立聚簇索引和非聚簇索引。假如你不仅想根据日期,而且想根据用户名从你的网点活动日志中取数据。在这种情况下,同时建立一个聚簇索引和非聚簇索引是有效的。你可以对日期时间字段建立聚簇索引,对用户名字段建立非聚簇索引。如果你发现你需要更多的索引方式,你可以增加更多的非聚簇索引。

非聚簇索引需要大量的硬盘空间和内存。另外,虽然非聚簇索引可以提高从表中取数据的速度,它也会降低向表中插入和更新数据的速度。每当你改变了一个建立了非聚簇索引的表中的数据时,必须同时更新索引。因此你对一个表建立非聚簇索引时要慎重考虑。如果你预计一个表需要频繁地更新数据,那么不要对它建立太多非聚簇索引。另外,如果硬盘和内存空间有限,也应该限制使用非聚簇索引的数量。

⑥索引属性

两种索引有一个共同之处:用两者中的任一类型同时对多个字段建立索引(复合索引),两种类型的索引都可以指定为唯一索引。你可以对多个字段建立一个复合索引,甚至是复合的聚簇索引。假如有一个表记录了你的网点访问者的姓和名字。如果你希望根据完整姓名从表中取数据,你需要建立一个同时对姓字段和名字字段进行的索引。这和分别对两个字段建立单独的索引是不同的。当你希望同时对不止一个字段进行查询时,你应该建立一个对多个字段的索引。如果你希望对各个字段进行分别查询,你应该对各字段建立独立的索引。

⑦对以下情况考虑用聚集索引

使用运算符(如BETWEEN、>、<=)返回一系列值;

返回大型结果集;

使用JOIN子句,一般使用该子句的是外键列;

使用ORDER BY 或者 GROUP BY 语句;

⑧列注意事项

一般情况下,定义聚集索引键时使用的列越少越好。考虑具有下列一个或多个属性的列:
唯一或包含许多不重复的值:例如,雇员 ID 唯一地标识雇员。EmployeeID 列的聚集索引或 PRIMARY KEY 约束将改善基于雇员 ID 号搜索雇员信息的查询的性能。另外,可对 LastName、FirstName、MiddleName 列创建聚集索引,因为经常以这种方式分组和查询雇员记录,而且这些列的组合还可提供高区分度。
按顺序被访问:例如,产品 ID 唯一地标识 AdventureWorks 数据库的 Production.Product 表中的产品。在其中指定顺序搜索的查询(如 WHERE ProductID BETWEEN 980 and 999)将从 ProductID 的聚集索引受益。这是因为行将按该键列的排序顺序存储。
由于保证了列在表中是唯一的,所以定义为 IDENTITY。
经常用于对表中检索到的数据进行排序,按该列对表进行聚集(即物理排序)是一个好方法,它可以在每次查询该列时节省排序操作的成本。
聚集索引不适用于具有下列属性的列:
频繁更改的列:这将导致整行移动,因为数据库引擎必须按物理顺序保留行中的数据值。这一点要特别注意,因为在大容量事务处理系统中数据通常是可变的。
宽键:宽键是若干列或若干大型列的组合。所有非聚集索引将聚集索引中的键值用作查找键。为同一表定义的任何非聚集索引都将增大许多,这是因为非聚集索引项包含聚集键,同时也包含为此非聚集索引定义的键列。

【上篇】
【下篇】

抱歉!评论已关闭.