数据库为何要建立索引的原因说明

这里想把之前的索引学习笔记总结一下:

首先明白为什么索引会增加速度,DB在执行一条Sql语句的时候,默认的方式是根据搜索条件进行全表扫描,遇到匹配条件的就加入搜索结果集合。如果我们对某一字段增加索引,查询时就会先去索引列表中一次定位到特定值的行数,大大减少遍历匹配的行数,所以能明显增加查询的速度。那么在任何时候都应该加索引么?这里有几个反例:1、如果每次都需要取到所有表记录,无论如何都必须进行全表扫描了,那么是否加索引也没有意义了。2、对非唯一的字段,例如“性别”这种大量重复值的字段,增加索引也没有什么意义。3、对于记录比较少的表,增加索引不会带来速度的优化反而浪费了存储空间,因为索引是需要存储空间的,而且有个致命缺点是对于update/insert/delete的每次执行,字段的索引都必须重新计算更新。

那么在什么时候适合加上索引呢?我们看一个Mysql手册中举的例子,这里有一条sql语句:

SELECT c.companyID, c.companyName FROM Companies c, User u WHERE c.companyID = u.fk_companyID AND c.numEmployees >= 0 AND c.companyName LIKE '%i%' AND u.groupID IN (SELECT g.groupID FROM Groups g WHERE g.groupLabel = 'Executive')

这条语句涉及3个表的联接,并且包括了许多搜索条件比如大小比较,Like匹配等。在没有索引的情况下Mysql需要执行的扫描行数是 77721876行。而我们通过在companyID和groupLabel两个字段上加上索引之后,扫描的行数只需要134行。在Mysql中可以通过 Explain Select来查看扫描次数。可以看出来在这种联表和复杂搜索条件的情况下,索引带来的性能提升远比它所占据的磁盘空间要重要得多。

那么索引是如何实现的呢?大多数DB厂商实现索引都是基于一种数据结构——B树。因为B树的特点就是适合在磁盘等直接存储设备上组织动态查找表。B树的定义是这样的:一棵m(m>=3)阶的B树是满足下列条件的m叉树:

1、每个结点包括如下作用域(j, p0, k1, p1, k2, p2, ... ki, pi) 其中j是关键字个数,p是孩子指针

2、所有叶子结点在同一层上,层数等于树高h

3、每个非根结点包含的关键字个数满足[m/2-1]<=j<=m-1

4、若树非空,则根至少有1个关键字,若根非叶子,则至少有2棵子树,至多有m棵子树

看一个B树的例子,针对26个英文字母的B树可以这样构造:

可以看到在这棵B树搜索英文字母复杂度只为o(m),在数据量比较大的情况下,这样的结构可以大大增加查询速度。然而有另外一种数据结构查询的虚度比B树更快——散列表。Hash表的定义是这样的:设所有可能出现的关键字集合为u,实际发生存储的关键字记为k,而|k|比|u|小很多。散列方法是通过散列函数h将u映射到表T[0,m-1]的下标上,这样u中的关键字为变量,以h为函数运算结果即为相应结点的存储地址。从而达到可以在o(1)的时间内完成查找。
    然而散列表有一个缺陷,那就是散列冲突,即两个关键字通过散列函数计算出了相同的结果。设m和n分别表示散列表的长度和填满的结点数,n/m为散列表的填装因子,因子越大,表示散列冲突的机会越大。
    因为有这样的缺陷,所以数据库不会使用散列表来做为索引的默认实现,Mysql宣称会根据执行查询格式尝试将基于磁盘的B树索引转变为和合适的散列索引以追求进一步提高搜索速度。我想其它数据库厂商也会有类似的策略,毕竟在数据库战场上,搜索速度和管理安全一样是非常重要的竞争点。

基本概念介绍:

索引

使用索引可快速访问数据库表中的特定信息。索引是对数据库表中一列或多列的值进行排序的一种结构,例如 employee 表的姓(lname)列。如果要按姓查找特定职员,与必须搜索表中的所有行相比,索引会帮助您更快地获得该信息。

索引提供指向存储在表的指定列中的数据值的指针,然后根据您指定的排序顺序对这些指针排序。数据库使用索引的方式与您使用书籍中的索引的方式很相似:它搜索索引以找到特定值,然后顺指针找到包含该值的行。

在数据库关系图中,您可以在选定表的“索引/键”属性页中创建、编辑或删除每个索引类型。当保存索引所附加到的表,或保存该表所在的关系图时,索引将保存在数据库中。有关详细信息,请参见创建索引。

注意;并非所有的数据库都以相同的方式使用索引。有关更多信息,请参见数据库服务器注意事项,或者查阅数据库文档。

作为通用规则,只有当经常查询索引列中的数据时,才需要在表上创建索引。索引占用磁盘空间,并且降低添加、删除和更新行的速度。在多数情况下,索引用于数据检索的速度优势大大超过它的。

索引列

可以基于数据库表中的单列或多列创建索引。多列索引使您可以区分其中一列可能有相同值的行。

如果经常同时搜索两列或多列或按两列或多列排序时,索引也很有帮助。例如,如果经常在同一查询中为姓和名两列设置判据,那么在这两列上创建多列索引将很有意义。

确定索引的有效性:

  • 检查查询的 WHERE 和 JOIN 子句。在任一子句中包括的每一列都是索引可以选择的对象。
  • 对新索引进行试验以检查它对运行查询性能的影响。
  • 考虑已在表上创建的索引数量。最好避免在单个表上有很多索引。
  • 检查已在表上创建的索引的定义。最好避免包含共享列的重叠索引。
  • 检查某列中唯一数据值的数量,并将该数量与表中的行数进行比较。比较的结果就是该列的可选择性,这有助于确定该列是否适合建立索引,如果适合,确定索引的类型。
索引类型

根据数据库的功能,可以在数据库设计器中创建三种索引:唯一索引、主键索引和聚集索引。有关数据库所支持的索引功能的详细信息,请参见数据库文档。

提示:尽管唯一索引有助于定位信息,但为获得最佳性能结果,建议改用主键或唯一约束。

唯一索引

唯一索引是不允许其中任何两行具有相同索引值的索引。

当现有数据中存在重复的键值时,大多数数据库不允许将新创建的唯一索引与表一起保存。数据库还可能防止添加将在表中创建重复键值的新数据。例如,如果在 employee 表中职员的姓 (lname) 上创建了唯一索引,则任何两个员工都不能同姓。

主键索引

数据库表经常有一列或列组合,其值唯一标识表中的每一行。该列称为表的主键。

在数据库关系图中为表定义主键将自动创建主键索引,主键索引是唯一索引的特定类型。该索引要求主键中的每个值都唯一。当在查询中使用主键索引时,它还允许对数据的快速访问。

聚集索引

在聚集索引中,表中行的物理顺序与键值的逻辑(索引)顺序相同。一个表只能包含一个聚集索引。

如果某索引不是聚集索引,则表中行的物理顺序与键值的逻辑顺序不匹配。与非聚集索引相比,聚集索引通常提供更快的数据访问速度。

建立方式和注意事项

最普通的情况,是为出现在where子句的字段建一个索引。为方便讲述,我们先建立一个如下的表。

CREATE TABLE mytable (

 id serial primary key,

 category_id int not null default 0,

 user_id int not null default 0,

 adddate int not null default 0

);

如果你在查询时常用类似以下的语句:

 SELECT * FROM mytable WHERE category_id=1;

最直接的应对之道,是为category_id建立一个简单的索引:

 CREATE INDEX mytable_categoryid

 ON mytable (category_id);

OK.如果你有不止一个选择条件呢?例如:

 SELECT * FROM mytable WHERE category_id=1 AND user_id=2;

你的第一反应可能是,再给user_id建立一个索引。不好,这不是一个最佳的方法。你可以建立多重的索引。

CREATE INDEX mytable_categoryid_userid ON mytable (category_id,user_id);

注意到我在命名时的习惯了吗?我使用"表名_字段1名_字段2名"的方式。你很快就会知道我为什么这样做了。

现在你已经为适当的字段建立了索引,不过,还是有点不放心吧,你可能会问,数据库会真正用到这些索引吗?测试一下就OK,对于大多数的数据库来说,这是很容易的,只要使用EXPLAIN命令:

EXPLAIN

 SELECT * FROM mytable

WHERE category_id=1 AND user_id=2;

 This is what Postgres 7.1 returns (exactly as I expected)

 NOTICE: QUERY PLAN:

 Index Scan using mytable_categoryid_userid on

 mytable (cost=0.00..2.02 rows=1 width=16)

EXPLAIN

以上是postgres的数据,可以看到该数据库在查询的时候使用了一个索引(一个好开始),而且它使用的是我创建的第二个索引。看到我上面命名的好处了吧,你马上知道它使用适当的索引了。

接着,来个稍微复杂一点的,如果有个ORDER BY字句呢?不管你信不信,大多数的数据库在使用order by的时候,都将会从索引中受益。

 SELECT * FROM mytable

WHERE category_id=1 AND user_id=2

 ORDER BY adddate DESC;

很简单,就象为where字句中的字段建立一个索引一样,也为ORDER BY的字句中的字段建立一个索引:

 CREATE INDEX mytable_categoryid_userid_adddate

 ON mytable (category_id,user_id,adddate);

 注意: "mytable_categoryid_userid_adddate" 将会被截短为

"mytable_categoryid_userid_addda"

 CREATE

 EXPLAIN SELECT * FROM mytable

WHERE category_id=1 AND user_id=2

 ORDER BY adddate DESC;

 NOTICE: QUERY PLAN:

 Sort (cost=2.03..2.03 rows=1 width=16)

-> Index Scan using mytable_categoryid_userid_addda

 on mytable (cost=0.00..2.02 rows=1 width=16)

 EXPLAIN

看看EXPLAIN的输出,数据库多做了一个我们没有要求的排序,这下知道性能如何受损了吧,看来我们对于数据库的自身运作是有点过于乐观了,那么,给数据库多一点提示吧。

为了跳过排序这一步,我们并不需要其它另外的索引,只要将查询语句稍微改一下。这里用的是postgres,我们将给该数据库一个额外的提示--在 ORDER BY语句中,加入where语句中的字段。这只是一个技术上的处理,并不是必须的,因为实际上在另外两个字段上,并不会有任何的排序操作,不过如果加入,postgres将会知道哪些是它应该做的。

 EXPLAIN SELECT * FROM mytable

WHERE category_id=1 AND user_id=2

 ORDER BY category_id DESC,user_id DESC,adddate DESC;

 NOTICE: QUERY PLAN:

 Index Scan Backward using

mytable_categoryid_userid_addda on mytable

 (cost=0.00..2.02 rows=1 width=16)

 EXPLAIN

现在使用我们料想的索引了,而且它还挺聪明,知道可以从索引后面开始读,从而避免了任何的排序。

以上说得细了一点,不过如果你的数据库非常巨大,并且每日的页面请求达上百万算,我想你会获益良多的。不过,如果你要做更为复杂的查询呢,例如将多张表结合起来查询,特别是where限制字句中的字段是来自不止一个表格时,应该怎样处理呢?我通常都尽量避免这种做法,因为这样数据库要将各个表中的东西都结合起来,然后再排除那些不合适的行,搞不好开销会很大。

如果不能避免,你应该查看每张要结合起来的表,并且使用以上的策略来建立索引,然后再用EXPLAIN命令验证一下是否使用了你料想中的索引。如果是的话,就OK。不是的话,你可能要建立临时的表来将他们结合在一起,并且使用适当的索引。

要注意的是,建立太多的索引将会影响更新和插入的速度,因为它需要同样更新每个索引文件。对于一个经常需要更新和插入的表格,就没有必要为一个很少使用的where字句单独建立索引了,对于比较小的表,排序的开销不会很大,也没有必要建立另外的索引。

以上介绍的只是一些十分基本的东西,其实里面的学问也不少,单凭EXPLAIN我们是不能判定该方法是否就是最优化的,每个数据库都有自己的一些优化器,虽然可能还不太完善,但是它们都会在查询时对比过哪种方式较快,在某些情况下,建立索引的话也未必会快,例如索引放在一个不连续的存储空间时,这会增加读磁盘的负担,因此,哪个是最优,应该通过实际的使用环境来检验。

在刚开始的时候,如果表不大,没有必要作索引,我的意见是在需要的时候才作索引,也可用一些命令来优化表,例如MySQL可用"OPTIMIZE TABLE"。

综上所述,在如何为数据库建立恰当的索引方面,你应该有一些基本的概念了。

(0)

相关推荐

  • 数据库为何要建立索引的原因说明

    这里想把之前的索引学习笔记总结一下: 首先明白为什么索引会增加速度,DB在执行一条Sql语句的时候,默认的方式是根据搜索条件进行全表扫描,遇到匹配条件的就加入搜索结果集合.如果我们对某一字段增加索引,查询时就会先去索引列表中一次定位到特定值的行数,大大减少遍历匹配的行数,所以能明显增加查询的速度.那么在任何时候都应该加索引么?这里有几个反例:1.如果每次都需要取到所有表记录,无论如何都必须进行全表扫描了,那么是否加索引也没有意义了.2.对非唯一的字段,例如"性别"这种大量重复值的字段,

  • Oracle数据库中建立索引的基本方法讲解

    怎样建立最佳索引? 1.明确地创建索引 create index index_name on table_name(field_name) tablespace tablespace_name pctfree 5 initrans 2 maxtrans 255 storage ( minextents 1 maxextents 16382 pctincrease 0 ); 2.创建基于函数的索引 常用与UPPER.LOWER.TO_CHAR(date)等函数分类上,例: create index

  • 数据库建立索引的一般依据小结

    建立索引常用的规则如下: 1.表的主键.外键必须有索引: 2.数据量超过300的表应该有索引: 3.经常与其他表进行连接的表,在连接字段上应该建立索引: 4.经常出现在Where子句中的字段,特别是大表的字段,应该建立索引: 5.索引应该建在选择性高的字段上: 6.索引应该建在小字段上,对于大的文本字段甚至超长字段,不要建索引: 7.复合索引的建立需要进行仔细分析:尽量考虑用单字段索引代替: A.正确选择复合索引中的主列字段,一般是选择性较好的字段: B.复合索引的几个字段是否经常同时以AND方

  • 详解mysql建立索引的使用办法及优缺点分析

    前言 索引(index)是帮助MySQL高效获取数据的数据结构. 它对于高性能非常关键,但人们通常会忘记或误解它. 索引在数据越大的时候越重要.规模小.负载轻的数据库即使没有索引,也能有好的性能, 但是当数据增加的时候,性能就会下降很快. 为什么要创建索引呢? 这是因为,创建索引可以大大提高系统的性能. 第一.通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性. 第二.可以大大加快数据的检索速度,这也是创建索引的最主要的原因. 第三.可以加速表和表之间的连接,特别是在实现数据的参考完整性方

  • mssql 建立索引第1/2页

    表的索引与附在一本书后面的索引非常相似.它可以极大地提高查询的速度.对一个较大的表来说,通过加索引,一个通常要花费几个小时来完成的查询只要几分钟就可以完成.因此没有理由对需要频繁查询的表增加索引. 注意: 当你的内存容量或硬盘空间不足时,也许你不想给一个表增加索引.对于包含索引的数据库,SQL Sever需要一个可观的额外空间.例如,要建立一个聚簇索引,需要大约1.2倍于数据大小的空间.要看一看一个表的索引在数据库中所占的空间大小,你可以使用系统存储过程sp_spaceused,对象名指定为被索

  • MySQL数据库优化技术之索引使用技巧总结

    本文实例总结了MySQL数据库优化技术的索引用法.分享给大家供大家参考,具体如下: 这里紧接上一篇<MySQL数据库优化技术之配置技巧总结>,进一步分析索引优化的技巧: (七)表的优化 1. 选择合适的数据引擎 MyISAM:适用于大量的读操作的表 InnoDB:适用于大量的写读作的表 2.选择合适的列类型 使用 SELECT * FROM TB_TEST PROCEDURE ANALYSE()可以对这个表的每一个字段进行分析,给出优化列类型建议 3.对于不保存NULL值的列使用NOT NUL

  • MySQL选错索引的原因以及解决方案

    MySQL 中,可以为某张表指定多个索引,但在语句具体执行时,选用哪个索引是由 MySQL 中执行器确定的.那么执行器选择索引的原则是什么,以及会不会出现选错索引的情况呢? 先看这样一个例子: 创建表 Y,设置两个普通索引, 创建一个存储过程用于插入数据. MySQL: 5.7.27, 隔离级别: RR CREATE TABLE `Y` ( `id` int(11) NOT NULL AUTO_INCREMENT, `a` int(11) DEFAULT NULL, `b` int(11) DE

  • 浅谈Mysql哪些字段适合建立索引

    1 数据库建立索引常用的规则如下: 1.表的主键.外键必须有索引: 2.数据量超过300的表应该有索引: 3.经常与其他表进行连接的表,在连接字段上应该建立索引: 4.经常出现在Where子句中的字段,特别是大表的字段,应该建立索引: 5.索引应该建在选择性高的字段上: 6.索引应该建在小字段上,对于大的文本字段甚至超长字段,不要建索引: 7.复合索引的建立需要进行仔细分析:尽量考虑用单字段索引代替: A.正确选择复合索引中的主列字段,一般是选择性较好的字段: B .复合索引的几个字段是否经常同

  • MySQL为数据表建立索引的原则详解

    目录 1.索引是什么? 2.索引的优点? 3.索引的缺点? 4.在建立索引的时候,都有哪些需要考虑的因素呢? 1.只为用于搜索.排序.分组的列创建索引 2.索引列的类型尽量小 3.为列前缀建立索引 4.覆盖索引 5.让索引列以列名的形式在搜索条件中单独出现 6.新插入记录时主键大小对效率的影响 7.冗余和重复索引 总结 面试题: 索引是什么? 索引的优点? 索引的缺点? 在建立索引的时候都有哪些需要考虑的因素呢? 为数据表建立索引的原则有哪些? 什么是索引覆盖? 非聚簇索引一定会回表查询吗? 1

  • Mysql多表关联不走索引的原因及分析

    目录 一.准备过程 二.比较 三.千万级别的数据查询个人优化建议 四.MYSQL多表查询的区别 总结 刚入职第一天,有个大佬写了一个统计函数count(*)需要对两张表a,b做统计.咋一看挺简单的,可是表a有1000万条数据,表b有300万条数据.使用LEFT JOIN进行查询.结果,一直查询不出来,可能时间就很久了.然后,这个锅就甩给第一天入职的我(我???). 接下来,就研究一下如何对海量数据的查询进行优化. 一.准备过程 1.创建两张表,表A large_student_tb(幼儿园大班学

随机推荐