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

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

    首先明白为什么索引会增加速度,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"。

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

时间: 2024-12-23 19:29:43

数据库为何要建立索引的原因说明_数据库其它的相关文章

数据库的设计方法、规范与技巧_数据库其它

一.数据库设计过程 数据库技术是信息资源管理最有效的手段.数据库设计是指对于一个给定的应用环境,构造最优的数据库模式,建立数据库及其应用系统,有效存储数据,满足用户信息要求和处理要求. 数据库设计中需求分析阶段综合各个用户的应用需求(现实世界的需求),在概念设计阶段形成独立于机器特点.独立于各个DBMS产品的概念模式(信息世界模型),用E-R图来描述.在逻辑设计阶段将E-R图转换成具体的数据库产品支持的数据模型如关系模型,形成数据库逻辑模式.然后根据用户处理的要求,安全性的考虑,在基本表的基础上

数据库 SQL千万级数据规模处理概要_数据库其它

1. 数据太多.放在一个表肯定不行. 比如月周期表.一个月1000万,一年就1.2亿,如此累计下去肯定不行的.所以都是基于一个周期数据一个表.甚至一个周期数据就要分几个分表.主要是考虑实际的数据量而定.当你创建一个新表时,可能这个表需要有索引,但是都要先取消索引,或者先建立表,导入数据后,再建立索引. 必要时处理完,统计完后,就备份到磁带或者其他介质.然后清掉. 从问题域来看,一个周期内的数据关联性最大.比如统计一个客户某个帐期的话单总额,同比上月增幅,还有就是零话费客户等.如此种种,参照的数据

数据库查询性能需注意几点经验_数据库其它

1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引. 2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如: select id from t where num is null 可以在num上设置默认值0,确保表中num列没有null值,然后这样查询: select id from t where num=0 3.应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放

NoSQL 数据库你应该了解的 10 件事_数据库其它

四分之一个世纪以来,关系型数据库(RDBMS)一直是主流数据库模型.但是现在非关系型数据库,"云"或者"NoSQL"数据库,正在作为一种替代数据库模型获得越来越多的占有率.本文中我们将关注非关系型 NoSQL 数据库的 10 个关键特征:排在前 5 位的优点和前 5 位的挑战.提示:点击链接可以下载本文 英文版PDF NoSQL 的五大有点 1:弹性扩展 多年来,数据库负载需要增加时,数据管理员只能依赖于纵向扩展(scale-up)--买更多更强的服务器,而不是依赖

[数据库] 通用分页存储过程第1/5页_数据库其它

分页存储过程共有四种方式可以实现,行计数.游标.升序-降序.子查询 我记得曾经有人测试过这四种方式的效率分别是 从性能最好到最差的顺序进行的--行计数.游标.升序-降序.子查询 以下是我收集的一些资料供大家参考 QUOTE: 原文地址:http://www.codeproject.com/aspnet/PagingLarge.asp 作者:Jasmin Muharemovic  译者:Tony Qu 下载: 介绍 在Web应用程序中,对一个大数据库结果集进行分页已经是一个家喻户晓的问题了.简单的

SQL Server数据库性能优化技术第1/2页_数据库其它

设计1个应用系统似乎并不难,但是要想使系统达到最优化的性能并不是一件容易的事.在开发工具.数据库设计.应  用程序的结构.查询设计.接口选择等方面有多种选择,这取决于特定的应用需求以及开发队伍的技能.本文以SQL  Server为例,从后台数据库的角度讨论应用程序性能优化技巧,并且给出了一些有益的建议.  1 数据库设计  要在良好的SQL Server方案中实现最优的性能,最关键的是要有1个很好的数据库设计方案.在实际工作中,许多SQL  Server方案往往是由于数据库设计得不好导致性能很差

数据库中union 与union all 的区别_数据库其它

今天晚上在操作两个表时才发现两个的区别.呵呵. union 将两个表连接后删除其重复的项: union all 将两个表连接都不删除其重复的项. 这个东东很简单.不过也记录一哈 .实在是一个小小的收获. 补充资料: 数据库中,UNION和UNION ALL都是将两个结果集合并为一个,但这两者从使用和效率上来说都有所不同. UNION在进行表链接后会筛选掉重复的记录,所以在表链接后会对所产生的结果集进行排序运算,删除重复的记录再返回结果.实际大部分应用中是不会产生重复的记录,最常见的是过程表与历史

asp获取数据库中表名和字段名的代码_数据库相关

复制代码 代码如下: < % '功能:显示数据库中表名.字段名.字段内容 '原创:wangsdong '原创文章,转载请保留此信息,谢谢 set rs=server.CreateObject("adodb.recordset") db="db1.mdb" set conn=server.CreateObject("adodb.connection") connstr="Provider=Microsoft.Jet.OLEDB.4.

数据库设计的完整性约束表现在哪些方面_数据库其它

数据库设计的完整性约束表现哪些方面?主要就是下面写四个方面: 1.域的完整性:数据库表中的列必须满足某种特定的数据类型或约束.其中约束又包括取值范围.精度等规定.用Check来实现约束,在数据库设计工具中,对字段的取值范围进行定义时,有一个Check按钮,通过它定义字段的值城. 2.参照完整性:就是定义外码与主码之间的引用规则.用PK.FK.表级触发器来实现.原理规则就是若属性(或属性组)f是基本关系r的外码,它与基本关系s的主码ks相对应(基本关系r和s不一定是不同的关系),则对于r中每个元组