本文内容主要来源于互联网上主流文章,只是按照我的理解稍做整合,后面附有参考连接。node
本文以MySQL数据库为研究对象,讨论与数据库索引相关的一些话题。特别须要说明的是,MySQL支持诸多存储引擎,而各类存储引擎对索引的支持也各不相同,所以MySQL数据库支持多种索引类型,如BTree索引,哈希索引,全文索引等等。为了不混乱,本文将只关注于BTree索引,由于这是日常使用MySQL时主要打交道的索引,至于哈希索引和全文索引本文暂不讨论。mysql
为何这里要讲查询算法和数据结构呢?由于之因此要创建索引,其实就是为了构建一种数据结构,能够在上面应用一种高效的查询算法,最终提升数据的查询速度。正则表达式
MySQL官方对索引的定义为:索引(Index)是帮助MySQL高效获取数据的数据结构。提取句子主干,就能够获得索引的本质:索引是数据结构。算法
咱们知道,数据库查询是数据库的最主要功能之一。咱们都但愿查询数据的速度能尽量的快,所以数据库系统的设计者会从查询算法的角度进行优化。那么有哪些查询算法可使查询速度变得更快呢?sql
最基本的查询算法固然是顺序查找(linear search),也就是对比每一个元素的方法,不过这种算法在数据量很大时效率是极低的。
数据结构:有序或无序队列
复杂度:O(n)
实例代码:数据库
//顺序查找 int SequenceSearch(int a[], int value, int n) { int i; for(i=0; i<n; i++) if(a[i]==value) return i; return -1; }
比顺序查找更快的查询方法应该就是二分查找了,二分查找的原理是查找过程从数组的中间元素开始,若是中间元素正好是要查找的元素,则搜素过程结束;若是某一特定元素大于或者小于中间元素,则在数组大于或小于中间元素的那一半中查找,并且跟开始同样从中间元素开始比较。若是在某一步骤数组为空,则表明找不到。
数据结构:有序数组
复杂度:O(logn)
实例代码:数组
//二分查找,递归版本 int BinarySearch2(int a[], int value, int low, int high) { int mid = low+(high-low)/2; if(a[mid]==value) return mid; if(a[mid]>value) return BinarySearch2(a, value, low, mid-1); if(a[mid]<value) return BinarySearch2(a, value, mid+1, high); }
二叉排序树的特色是:安全
搜索的原理:数据结构
数据结构:二叉排序树
时间复杂度: O(log2N)
ide
其原理是首先根据key值和哈希函数建立一个哈希表(散列表),燃耗根据键值,经过散列函数,定位数据元素位置。
数据结构:哈希表
时间复杂度:几乎是O(1)
,取决于产生冲突的多少。
分块查找又称索引顺序查找,它是顺序查找的一种改进方法。其算法思想是将n个数据元素”按块有序”划分为m块(m ≤ n)。每一块中的结点没必要有序,但块与块之间必须”按块有序”;即第1块中任一元素的关键字都必须小于第2块中任一元素的关键字;而第2块中任一元素又都必须小于第3块中的任一元素,依次类推。
算法流程:
这种搜索算法每一次比较都使搜索范围缩小一半。它们的查询速度就有了很大的提高,复杂度为。若是稍微分析一下会发现,每种查找算法都只能应用于特定的数据结构之上,例如二分查找要求被检索数据有序,而二叉树查找只能应用于二叉查找树上,可是数据自己的组织结构不可能彻底知足各类数据结构(例如,理论上不可能同时将两列都按顺序进行组织),因此,在数据以外,数据库系统还维护着知足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就能够在这些数据结构上实现高级查找算法。这种数据结构,就是索引。
上面讲到了二叉树,它的搜索时间复杂度为O(log2N)
,因此它的搜索效率和树的深度有关,若是要提升查询速度,那么就要下降树的深度。要下降树的深度,很天然的方法就是采用多叉树,再结合平衡二叉树的思想,咱们能够构建一个平衡多叉树结构,而后就能够在上面构建平衡多路查找算法,提升大数据量下的搜索效率。
B树(Balance Tree)又叫作B- 树(其实B-是由B-tree翻译过来,因此B-树和B树是一个概念) ,它就是一种平衡多路查找树。下图就是一个典型的B树:
从上图中咱们能够大体看到B树的一些特色,为了更好的描述B树,咱们定义记录为一个二元组[key, data]
,key为记录的键值,data表示其它数据(上图中只有key,没有画出data数据 )。下面是对B树的一个详细定义:
1. 有一个根节点,根节点只有一个记录和两个孩子或者根节点为空; 2. 每一个节点记录中的key和指针相互间隔,指针指向孩子节点; 3. d是表示树的宽度,除叶子节点以外,其它每一个节点有[d/2,d-1]条记录,而且些记录中的key都是从左到右按大小排列的,有[d/2+1,d]个孩子; 4. 在一个节点中,第n个子树中的全部key,小于这个节点中第n个key,大于第n-1个key,好比上图中B节点的第2个子节点E中的全部key都小于B中的第2个key 9,大于第1个key 3; 5. 全部的叶子节点必须在同一层次,也就是它们具备相同的深度;
因为B-Tree的特性,在B-Tree中按key检索数据的算法很是直观:首先从根节点进行二分查找,若是找到则返回对应节点的data,不然对相应区间的指针指向的节点递归进行查找,直到找到节点或找到null指针,前者查找成功,后者查找失败。B-Tree上查找算法的伪代码以下:
BTree_Search(node, key) { if(node == null) return null; foreach(node.key){ if(node.key[i] == key) return node.data[i]; if(node.key[i] > key) return BTree_Search(point[i]->node); } return BTree_Search(point[i+1]->node); } data = BTree_Search(root, my_key);
关于B-Tree有一系列有趣的性质,例如一个度为d的B-Tree,设其索引N个key,则其树高h的上限为logd((N+1)/2)
,检索一个key,其查找节点个数的渐进复杂度为O(logdN)
。从这点能够看出,B-Tree是一个很是有效率的索引数据结构。
另外,因为插入删除新的数据记录会破坏B-Tree的性质,所以在插入删除时,须要对树进行一个分裂、合并、转移等操做以保持B-Tree性质,本文不打算完整讨论B-Tree这些内容,由于已经有许多资料详细说明了B-Tree的数学性质及插入删除算法,有兴趣的朋友能够查阅其它文献进行详细研究。
其实B-Tree有许多变种,其中最多见的是B+Tree,好比MySQL就广泛使用B+Tree实现其索引结构。B-Tree相比,B+Tree有如下不一样点:
下面是一个简单的B+Tree示意。
因为并非全部节点都具备相同的域,所以B+Tree中叶节点和内节点通常大小不一样。这点与B-Tree不一样,虽然B-Tree中不一样节点存放的key和指针可能数量不一致,可是每一个节点的域和上限是一致的,因此在实现中B-Tree每每对每一个节点申请同等大小的空间。通常来讲,B+Tree比B-Tree更适合实现外存储索引结构,具体缘由与外存储器原理及计算机存取原理有关,将在下面讨论。
通常在数据库系统或文件系统中使用的B+Tree结构都在经典B+Tree的基础上进行了优化,增长了顺序访问指针。
如图所示,在B+Tree的每一个叶子节点增长一个指向相邻叶子节点的指针,就造成了带有顺序访问指针的B+Tree。作这个优化的目的是为了提升区间访问的性能,例如图4中若是要查询key为从18到49的全部数据记录,当找到18后,只需顺着节点和指针顺序遍历就能够一次性访问到全部数据节点,极大提到了区间查询效率。
这一节对B-Tree和B+Tree进行了一个简单的介绍,下一节结合存储器存取原理介绍为何目前B+Tree是数据库系统实现索引的首选数据结构。
上文说过,二叉树、红黑树等数据结构也能够用来实现索引,可是文件系统及数据库系统广泛采用B-/+Tree做为索引结构,这一节将结合计算机组成原理相关知识讨论B-/+Tree做为索引的理论基础。
在计算机系统中通常包含两种类型的存储,计算机主存(RAM)和外部存储器(如硬盘、CD、SSD等)。在设计索引算法和存储结构时,咱们必需要考虑到这两种类型的存储特色。主存的读取速度快,相对于主存,外部磁盘的数据读取速率要比主从慢好几个数量级,具体它们之间的差异后面会详细介绍。 上面讲的全部查询算法都是假设数据存储在计算机主存中的,计算机主存通常比较小,实际数据库中数据都是存储到外部存储器的。
通常来讲,索引自己也很大,不可能所有存储在内存中,所以索引每每以索引文件的形式存储的磁盘上。这样的话,索引查找过程当中就要产生磁盘I/O消耗,相对于内存存取,I/O存取的消耗要高几个数量级,因此评价一个数据结构做为索引的优劣最重要的指标就是在查找过程当中磁盘I/O操做次数的渐进复杂度。换句话说,索引的结构组织要尽可能减小查找过程当中磁盘I/O的存取次数。下面详细介绍内存和磁盘存取原理,而后再结合这些原理分析B-/+Tree做为索引的效率。
目前计算机使用的主存基本都是随机读写存储器(RAM),现代RAM的结构和存取原理比较复杂,这里本文抛却具体差异,抽象出一个十分简单的存取模型来讲明RAM的工做原理。
从抽象角度看,主存是一系列的存储单元组成的矩阵,每一个存储单元存储固定大小的数据。每一个存储单元有惟一的地址,现代主存的编址规则比较复杂,这里将其简化成一个二维地址:经过一个行地址和一个列地址能够惟必定位到一个存储单元。上图展现了一个4 x 4的主存模型。
主存的存取过程以下:
当系统须要读取主存时,则将地址信号放到地址总线上传给主存,主存读到地址信号后,解析信号并定位到指定存储单元,而后将此存储单元数据放到数据总线上,供其它部件读取。写主存的过程相似,系统将要写入单元地址和数据分别放在地址总线和数据总线上,主存读取两个总线的内容,作相应的写操做。
这里能够看出,主存存取的时间仅与存取次数呈线性关系,由于不存在机械操做,两次存取的数据的“距离”不会对时间有任何影响,例如,先取A0再取A1和先取A0再取D3的时间消耗是同样的。
上文说过,索引通常以文件形式存储在磁盘上,索引检索须要磁盘I/O操做。与主存不一样,磁盘I/O存在机械运动耗费,所以磁盘I/O的时间消耗是巨大的。
磁盘读取数据靠的是机械运动,当须要从磁盘读取数据时,系统会将数据逻辑地址传给磁盘,磁盘的控制电路按照寻址逻辑将逻辑地址翻译成物理地址,即肯定要读的数据在哪一个磁道,哪一个扇区。为了读取这个扇区的数据,须要将磁头放到这个扇区上方,为了实现这一点,磁头须要移动对准相应磁道,这个过程叫作寻道,所耗费时间叫作寻道时间,而后磁盘旋转将目标扇区旋转到磁头下,这个过程耗费的时间叫作旋转时间,最后即是对读取数据的传输。 因此每次读取数据花费的时间能够分为寻道时间、旋转延迟、传输时间三个部分。其中:
那么访问一次磁盘的时间,即一次磁盘IO的时间约等于5+4.17 = 9ms左右,听起来还挺不错的,但要知道一台500 -MIPS的机器每秒能够执行5亿条指令,由于指令依靠的是电的性质,换句话说执行一次IO的时间能够执行40万条指令,数据库动辄十万百万乃至千万级数据,每次9毫秒的时间,显然是个灾难。
因为存储介质的特性,磁盘自己存取就比主存慢不少,再加上机械运动耗费,磁盘的存取速度每每是主存的几百分分之一,所以为了提升效率,要尽可能减小磁盘I/O。为了达到这个目的,磁盘每每不是严格按需读取,而是每次都会预读,即便只须要一个字节,磁盘也会从这个位置开始,顺序向后读取必定长度的数据放入内存。这样作的理论依据是计算机科学中著名的局部性原理:当一个数据被用到时,其附近的数据也一般会立刻被使用。程序运行期间所须要的数据一般比较集中。
因为磁盘顺序读取的效率很高(不须要寻道时间,只需不多的旋转时间),所以对于具备局部性的程序来讲,预读能够提升I/O效率。预读的长度通常为页(page)的整倍数。页是计算机管理存储器的逻辑块,硬件及操做系统每每将主存和磁盘存储区分割为连续的大小相等的块,每一个存储块称为一页(在许多操做系统中,页得大小一般为4k),主存和磁盘以页为单位交换数据。当程序要读取的数据不在主存中时,会触发一个缺页异常,此时系统会向磁盘发出读盘信号,磁盘会找到数据的起始位置并向后连续读取一页或几页载入内存中,而后异常返回,程序继续运行。
到这里终于能够分析为什么数据库索引采用B-/+Tree存储结构了。上文说过数据库索引是存储到磁盘的而咱们又通常以使用磁盘I/O次数来评价索引结构的优劣。先从B-Tree分析,根据B-Tree的定义,可知检索一次最多须要访问h-1
个节点(根节点常驻内存)。数据库系统的设计者巧妙利用了磁盘预读原理,将一个节点的大小设为等于一个页,这样每一个节点只须要一次I/O就能够彻底载入。为了达到这个目的,在实际实现B-Tree还须要使用以下技巧:每次新建节点时,直接申请一个页的空间,这样就保证一个节点物理上也存储在一个页里,加之计算机存储分配都是按页对齐的,就实现了一个node只需一次I/O。
B-Tree中一次检索最多须要h-1
次I/O(根节点常驻内存),渐进复杂度为O(h)=O(logdN)
。通常实际应用中,出度d是很是大的数字,一般超过100,所以h很是小(一般不超过3)。
综上所述,若是咱们采用B-Tree存储结构,搜索时I/O次数通常不会超过3次,因此用B-Tree做为索引结构效率是很是高的。
从上面介绍咱们知道,B树的搜索复杂度为O(h)=O(logdN)
,因此树的出度d越大,深度h就越小,I/O的次数就越少。B+Tree偏偏能够增长出度d的宽度,由于每一个节点大小为一个页大小,因此出度的上限取决于节点内key和data的大小:
dmax=floor(pagesize/(keysize+datasize+pointsize))//floor表示向下取整
因为B+Tree内节点去掉了data域,所以能够拥有更大的出度,从而拥有更好的性能。
B-树和B+树查找过程基本一致。如上图所示,若是要查找数据项29,那么首先会把磁盘块1由磁盘加载到内存,此时发生一次IO,在内存中用二分查找肯定29在17和35之间,锁定磁盘块1的P2指针,内存时间由于很是短(相比磁盘的IO)能够忽略不计,经过磁盘块1的P2指针的磁盘地址把磁盘块3由磁盘加载到内存,发生第二次IO,29在26和30之间,锁定磁盘块3的P2指针,经过指针加载磁盘块8到内存,发生第三次IO,同时内存中作二分查找找到29,结束查询,总计三次IO。真实的状况是,3层的b+树能够表示上百万的数据,若是上百万的数据查找只须要三次IO,性能提升将是巨大的,若是没有索引,每一个数据项都要发生一次IO,那么总共须要百万次的IO,显然成本很是很是高。
这一章从理论角度讨论了与索引相关的数据结构与算法问题,下一章将讨论B+Tree是如何具体实现为MySQL中索引,同时将结合MyISAM和InnDB存储引擎介绍非汇集索引和汇集索引两种不一样的索引实现形式。
在MySQL中,索引属于存储引擎级别的概念,不一样存储引擎对索引的实现方式是不一样的,本文主要讨论MyISAM和InnoDB两个存储引擎的索引实现方式。
MyISAM引擎使用B+Tree做为索引结构,叶节点的data域存放的是数据记录的地址。下图是MyISAM索引的原理图:
这里设表一共有三列,假设咱们以Col1为主键,则上图是一个MyISAM表的主索引(Primary key)示意。能够看出MyISAM的索引文件仅仅保存数据记录的地址。在MyISAM中,主索引和辅助索引(Secondary key)在结构上没有任何区别,只是主索引要求key是惟一的,而辅助索引的key能够重复。若是咱们在Col2上创建一个辅助索引,则此索引的结构以下图所示:
一样也是一颗B+Tree,data域保存数据记录的地址。所以,MyISAM中索引检索的算法为首先按照B+Tree搜索算法搜索索引,若是指定的Key存在,则取出其data域的值,而后以data域的值为地址,读取相应数据记录。
MyISAM的索引方式也叫作“非汇集”的,之因此这么称呼是为了与InnoDB的汇集索引区分。
虽然InnoDB也使用B+Tree做为索引结构,但具体实现方式却与MyISAM大相径庭。
第一个重大区别是InnoDB的数据文件自己就是索引文件。从上文知道,MyISAM索引文件和数据文件是分离的,索引文件仅保存数据记录的地址。而在InnoDB中,表数据文件自己就是按B+Tree组织的一个索引结构,这棵树的叶节点data域保存了完整的数据记录。这个索引的key是数据表的主键,所以InnoDB表数据文件自己就是主索引。
上图是InnoDB主索引(同时也是数据文件)的示意图,能够看到叶节点包含了完整的数据记录。这种索引叫作汇集索引。由于InnoDB的数据文件自己要按主键汇集,因此InnoDB要求表必须有主键(MyISAM能够没有),若是没有显式指定,则MySQL系统会自动选择一个能够惟一标识数据记录的列做为主键,若是不存在这种列,则MySQL自动为InnoDB表生成一个隐含字段做为主键,这个字段长度为6个字节,类型为长整形。
第二个与MyISAM索引的不一样是InnoDB的辅助索引data域存储相应记录主键的值而不是地址。换句话说,InnoDB的全部辅助索引都引用主键做为data域。例如,下图为定义在Col3上的一个辅助索引:
这里以英文字符的ASCII码做为比较准则。汇集索引这种实现方式使得按主键的搜索十分高效,可是辅助索引搜索须要检索两遍索引:首先检索辅助索引得到主键,而后用主键到主索引中检索得到记录。
了解不一样存储引擎的索引实现方式对于正确使用和优化索引都很是有帮助,例如知道了InnoDB的索引实现后,就很容易明白为何不建议使用过长的字段做为主键,由于全部辅助索引都引用主索引,过长的主索引会令辅助索引变得过大。再例如,用非单调的字段做为主键在InnoDB中不是个好主意,由于InnoDB数据文件自己是一颗B+Tree,非单调的主键会形成在插入新记录时数据文件为了维持B+Tree的特性而频繁的分裂调整,十分低效,而使用自增字段做为主键则是一个很好的选择。
下一章将具体讨论这些与索引有关的优化策略。
MySQL的优化主要分为结构优化(Scheme optimization)和查询优化(Query optimization)。本章讨论的高性能索引策略主要属于结构优化范畴。本章的内容彻底基于上文的理论基础,实际上一旦理解了索引背后的机制,那么选择高性能的策略就变成了纯粹的推理,而且能够理解这些策略背后的逻辑。
首先介绍一下联合索引。联合索引其实很简单,相对于通常索引只有一个字段,联合索引能够为多个字段建立一个索引。它的原理也很简单,好比,咱们在(a,b,c)字段上建立一个联合索引,则索引记录会首先按照A字段排序,而后再按照B字段排序而后再是C字段,所以,联合索引的特色就是:
当第一个字段值相等的时候,第二个字段又是有序的,好比下表中当A=2时全部B的值是有序排列的,依次类推,当同一个B值得全部C字段是有序排列的
| A | B | C |
| 1 | 2 | 3 |
| 1 | 4 | 2 |
| 1 | 1 | 4 |
| 2 | 3 | 5 |
| 2 | 4 | 4 |
| 2 | 4 | 6 |
| 2 | 5 | 5 |
其实联合索引的查找就跟查字典是同样的,先根据第一个字母查,而后再根据第二个字母查,或者只根据第一个字母查,可是不能跳过第一个字母从第二个字母开始查。这就是所谓的最左前缀原理。
咱们再来详细介绍一下联合索引的查询。仍是上面例子,咱们在(a,b,c)
字段上建了一个联合索引,因此这个索引是先按a 再按b 再按c进行排列的,因此:
如下的查询方式均可以用到索引
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;
上面三个查询按照 (a ), (a,b ),(a,b,c )
的顺序均可以利用到索引,这就是最左前缀匹配。
若是查询语句是:
select * from table where a=1 and c=3; 那么只会用到索引a。
若是查询语句是:
select * from table where b=2 and c=3; 由于没有用到最左前缀a,因此这个查询是用户到索引的。
若是用到了最左前缀,可是顺序颠倒会用到索引码?
好比:
select * from table where b=2 and a=1; select * from table where b=2 and a=1 and c=3;
若是用到了最左前缀而只是颠倒了顺序,也是能够用到索引的,由于mysql查询优化器会判断纠正这条sql语句该以什么样的顺序执行效率最高,最后才生成真正的执行计划。但咱们仍是最好按照索引顺序来查询,这样查询优化器就不用从新编译了。
除了联合索引以外,对mysql来讲其实还有一种前缀索引。前缀索引就是用列的前缀代替整个列做为索引key,当前缀长度合适时,能够作到既使得前缀索引的选择性接近全列索引,同时由于索引key变短而减小了索引文件的大小和维护开销。
通常来讲如下状况可使用前缀索引:
一些文章中也提到:
MySQL 前缀索引能有效减少索引文件的大小,提升索引的速度。可是前缀索引也有它的坏处:MySQL 不能在 ORDER BY 或 GROUP BY 中使用前缀索引,也不能把它们用做覆盖索引(Covering Index)。
SELECT * FROM
houdunwangWHERE
unameLIKE'后盾%' -- 走索引
SELECT * FROM
houdunwangWHERE
unameLIKE "%后盾%" -- 不走索引
CREATE TABLE
a(
achar(10));
EXPLAIN SELECT * FROM
aWHERE
a="1"
– 走索引 a
WHERE a
=1 – 不走索引
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
惟一索引
惟一索引能够确保索引列不包含重复的值。在多列惟一索引的状况下,该索引能够确保索引列中每一个值组合都是惟一的。例如,若是在 last_name、first_name 和 middle_initial 列的组合上建立了惟一索引 full_name,则该表中任何两我的都不能够具备相同的全名。
汇集索引和非汇集索引均可以是惟一的。所以,只要列中的数据是惟一的,就能够在同一个表上建立一个惟一的汇集索引和多个惟一的非汇集索引。
只有当惟一性是数据自己的特征时,指定惟一索引才有意义。若是必须实施惟一性以确保数据的完整性,则应在列上建立 UNIQUE 或 PRIMARY KEY 约束,而不要建立惟一索引。例如,若是打算常常查询雇员表(主键为 emp_id)中的社会安全号码 (ssn) 列,并但愿确保社会安全号码的惟一性,则在 ssn 列上建立 UNIQUE 约束。若是用户为一个以上的雇员输入了同一个社会安全号码,则会显示错误。
(1)非汇集索引非汇集索引与课本中的索引相似。数据存储在一个地方,索引存储在另外一个地方,索引带有指针指向数据的存储位置。索引中的项目按索引键值的顺序存储,而表中的信息按另外一种顺序存储(这能够由汇集索引规定)。若是在表中未建立汇集索引,则没法保证这些行具备任何特定的顺序。典型的桌面数据库使用的是非汇集索引。在这类索引中,索引键值是有序的,而每一个索引节点所指向的数据行是无序的。一个SQL Server表最多能够拥有255个非汇集索引。非汇集索引与汇集索引同样有 B-树结构,可是有两个重大差异:l 数据行不按非汇集索引键的顺序排序和存储。l 非汇集索引的叶层不包含数据页。相反,叶节点包含索引行。每一个索引行包含非汇集键值以及一个或多个行定位器,这些行定位器指向有该键值的数据行(若是索引不惟一,则多是多行)。非汇集索引能够在有汇集索引的表、堆集或索引视图上定义。在 SQL Server中,非汇集索引中的行定位器有两种形式:l 若是表是堆集(没有汇集索引),行定位器就是指向行的指针。该指针用文件标识符 (ID)、页码和页上的行数生成。整个指针称为行 ID。l 若是表是堆集(没有汇集索引),行定位器就是指向行的指针。该指针用文件标识符 (ID)、页码和页上的行数生成。整个指针称为行 ID。因为非汇集索引将汇集索引键做为其行指针存储,所以使汇集索引键尽量小很重要。若是表还有非汇集索引,请不要选择大的列做为汇集索引的键。在建立非汇集索引以前,应先了解您的数据是如何被访问的。可考虑将非汇集索引用于:l 包含大量非重复值的列,如姓氏和名字的组合(若是汇集索引用于其它列)。若是只有不多的非重复值,如只有 1 和 0,则大多数查询将不使用索引,由于此时表扫描一般更有效。l 不返回大型结果集的查询。l 返回精确匹配的查询的搜索条件(WHERE 子句)中常用的列。l 常常须要联接和分组的决策支持系统应用程序。应在联接和分组操做中使用的列上建立多个非汇集索引,在任何外键列上建立一个汇集索引。l 在特定的查询中覆盖一个表中的全部列。这将彻底消除对表或汇集索引的访问。(2)汇集索引汇集索引肯定表中数据的物理顺序。汇集索引相似于电话簿,后者按姓氏排列数据。因为汇集索引规定数据在表中的物理存储顺序,所以一个表只能包含一个汇集索引。但该索引能够包含多个列(组合索引),就像电话簿按姓氏和名字进行组织同样。汇集索引在系统数据库表sysindexes 内有一行,其 indid = 1。数据链内的页和其内的行按汇集索引键值排序。全部插入都在所插入行中的键值与排序顺序相匹配时执行。SQL Server将索引组织为B-树。索引内的每一页包含一个页首,页首后面跟着索引行。每一个索引行都包含一个键值以及一个指向较低级页或数据行的指针。索引的每一个页称为索引节点。B-树的顶端节点称为根节点。索引的底层节点称为叶节点。每级索引中的页连接在双向连接列表中。在汇集索引内数据页组成叶节点。根和叶之间的任何索引级统称为中间级。对于汇集索引,sysindexes.root 指向它的顶端。SQL Server 沿着汇集索引浏览以找到汇集索引键对应的行。为找到键的范围,SQL Server 浏览索引以找到这个范围的起始键值,而后用向前或向后指针扫描数据页。为找到数据页链的首页,SQL Server 从索引的根节点开始沿最左边的指针进行扫描.汇集索引对于那些常常要搜索范围值的列特别有效。使用汇集索引找到包含第一个值的行后,即可以确保包含后续索引值的行在物理相邻。例如,若是应用程序执行的一个查询常常检索某一日期范围内的记录,则使用汇集索引能够迅速找到包含开始日期的行,而后检索表中全部相邻的行,直到到达结束日期。这样有助于提升此类查询的性能。一样,若是对从表中检索的数据进行排序时常常要用到某一列,则能够将该表在该列上汇集(物理排序),避免每次查询该列时都进行排序,从而节省成本。对于汇集索引,人们每每有一些错误的认识。其中,最多见的错误有:l 汇集索引会下降insert操做的速度,由于必需要向后移动一半的数据来为新插入的行腾出空间。这种认识是错误的,由于能够利用填充因子控制填充的百分比,从而在索引页上为新插入的数据保留空间。若是索引页填满了,SQL Server将会进行页拆分,在这种状况下只有第一个页才会受到影响。l 在使用标识列的主键上建立汇集索引是一种好的设计方法,它可使对表的操做达到最快速度。这种认识是错误的,它浪费了建立其它更有效的汇集索引的机会。而且,使用这种方法会把每一个新插入的记录行都存储到表尾部的同一个的数据页中,这将致使数据库的热点和锁争用。笔者曾经见过采用这种方法设计的数据库,对于每个新订单,客户服务人员都不得不等待数分钟来加以确认。l 汇集索引是具备魔力的。若是哪一个查询的速度不够快,那么就在该列上建立汇集索引,对于表的操做速度必定会获得提升。这种认识也是错误的,汇集索引只是比非汇集索引稍稍快了那么一点点。由于在每一个表上只能建立一个汇集索引,因此它也是一种宝贵的性能资源,只有在那些常常做为条件查询一组记录行的列上才应该创建汇集索引。在建立汇集索引以前,应先了解数据是如何被访问的。可考虑将汇集索引用于:l 包含大量非重复值的列。l 使用下列运算符返回一个范围值的查询:BETWEEN、>、>=、< 和 <=。l 被连续访问的列。l 返回大型结果集的查询。l 常常被使用联接或 GROUP BY 子句的查询访问的列;通常来讲,这些是外键列。对 ORDER BY 或 GROUP BY 子句中指定的列进行索引,可使 SQL Server 没必要对数据进行排序,由于这些行已经排序。这样能够提升查询性能。l OLTP 类型的应用程序,这些程序要求进行很是快速的单行查找(通常经过主键)。应在主键上建立汇集索引。注意,汇集索引不适用于:l 频繁更改的列,这将致使整行移动(由于 SQL Server 必须按物理顺序保留行中的数据值)。这一点要特别注意,由于在大数据量事务处理系统中数据是易失的。l 宽键,来自汇集索引的键值由全部非汇集索引做为查找键使用,所以存储在每一个非汇集索引的叶条目内。