写在前面:本篇文章须要你对索引和SQL中数据的存储方式有必定了解.标题中高级两个字仅仅是由于本篇文章须要个人T-SQL进阶系列文章的一些内容做为基础.html
在SQL Server中,存储数据的最小单位是页,每一页所能容纳的数据为8060字节.而页的组织方式是经过B树结构(表上没有汇集索引则为堆结构,不在本文讨论之列)以下图:数据库
在汇集索引B树中,只有叶子节点实际存储数据,而其余根节点和中间节点仅仅用于存放查找叶子节点的数据.性能
每个叶子节点为一页,每页是不可分割的. 而SQL Server向每一个页内存储数据的最小单位是表的行(Row).当叶子节点中新插入的行或更新的行使得叶子节点没法容纳当前更新或者插入的行时,分页就产生了.在分页的过程当中,就会产生碎片.学习
首先,理解外部碎片的这个“外”是相对页面来讲的。外部碎片指的是因为分页而产生的碎片.好比,我想在现有的汇集索引中插入一行,这行正好致使现有的页空间没法知足容纳新的行。从而致使了分页:测试
由于在SQL SERVER中,新的页是随着数据的增加不断产生的,而汇集索引要求行之间连续,因此不少状况下分页后和原来的页在磁盘上并不连续.spa
这就是所谓的外部碎片.htm
因为分页会致使数据在页之间的移动,因此若是插入更新等操做常常须要致使分页,则会大大提高IO消耗,形成性能降低.blog
而对于查找来讲,在有特定搜索条件,好比where子句有很细的限制或者返回无序结果集时,外部碎片并不会对性能产生影响。但若是要返回扫描汇集索引而查找连续页面时,外部碎片就会产生性能上的影响.索引
在SQL Server中,比页更大的单位是区(Extent).一个区能够容纳8个页.区做为磁盘分配的物理单元.因此当页分割若是跨区后,须要屡次切区。须要更多的扫描.由于读取连续数据时会不能预读,从而形成额外的物理读,增长磁盘IO.内存
和外部碎片同样,内部碎片的”内”也是相对页来讲的.下面咱们来看一个例子:
咱们建立一个表,这个表每一个行由int(4字节),char(999字节)和varchar(0字节组成),因此每行为1003个字节,则8行占用空间1003*8=8024字节加上一些内部开销,能够容纳在一个页面中:
当咱们随意更新某行中的col3字段后,形成页内没法容纳下新的数据,从而形成分页:
分页后的示意图:
而当分页时若是新的页和当前页物理上不连续,则还会形成外部碎片
外部碎片对于性能的影响上面说过,主要是在于须要进行更多的跨区扫描,从而形成更多的IO操做.
而内部碎片会形成数据行分布在更多的页中,从而加剧了扫描的页树,也会下降查询性能.
下面经过一个例子看一下,咱们人为的为刚才那个表插入一些数据形成内部碎片:
经过查看碎片,咱们发现这时碎片已经达到了一个比较高的程度:
经过查看对碎片整理以前和以后的IO,咱们能够看出,IO大大降低了:
基本上全部解决办法都是基于对索引的重建和整理,只是方式不一样
1.删除索引并重建
这种方式并很差.在删除索引期间,索引不可用.会致使阻塞发生。而对于删除汇集索引,则会致使对应的非汇集索引重建两次(删除时重建,创建时再重建).虽然这种方法并很差,可是对于索引的整理最为有效
2.使用DROP_EXISTING语句重建索引
为了不重建两次索引,使用DROP_EXISTING语句重建索引,由于这个语句是原子性的,不会致使非汇集索引重建两次,但一样的,这种方式也会形成阻塞
3.如前面文章所示,使用ALTER INDEX REBUILD语句重建索引
使用这个语句一样也是重建索引,可是经过动态重建索引而不须要卸载并重建索引.是优于前两种方法的,但依旧会形成阻塞。能够经过ONLINE关键字减小锁,但会形成重建时间加长.
4.使用ALTER INDEX REORGANIZE
这种方式不会重建索引,也不会生成新的页,仅仅是整理,当遇到加锁的页时跳过,因此不会形成阻塞。但同时,整理效果会差于前三种.
重建索引当然能够解决碎片的问题.可是重建索引的代价不只仅是麻烦,还会形成阻塞。影响使用.而对于数据比较少的状况下,重建索引代价并不大。而当索引自己超过百兆的时候。重建索引的时间将会很让人蛋疼.
填充因子的做用正是如此。对于默认值来讲,填充因子为0(0和100表示的是一个概念),则表示页面能够100%使用。因此会遇到前面update或insert时,空间不足致使分页.经过设置填充因子,能够设置页面的使用程度:
下面来看一个例子:
仍是上面那个表.我插入31条数据,则占4页:
经过设置填充因子,页被设置到了5页上:
这时我再插入一页,不会形成分页:
上面的概念能够以下图来解释:
能够看出,使用填充因子会减小更新或者插入时的分页次数,但因为须要更多的页,则会对应的损失查找性能.
如何设置填充因子的值并无一个公式或者理念能够准确的设置。使用填充因子虽然能够减小更新或者插入时的分页,但同时由于须要更多的页,因此下降了查询的性能和占用更多的磁盘空间.如何设置这个值进行trade-off须要根据具体的状况来看.
具体状况要根据对于表的读写比例来看,我这里给出我认为比较合适的值:
1.当读写比例大于100:1时,不要设置填充因子,100%填充
2.当写的次数大于读的次数时,设置50%-70%填充
3.当读写比例位于二者之间时80%-90%填充
上面的数据仅仅是个人见解,具体设置的数据还要根据具体状况进行测试才能找到最优.
本文讲述了SQL SERVER中碎片产生的原理,内部碎片和外部碎片的概念。以及解决碎片的办法和填充因子.在数据库中,每每每个对于某一方面性能增长的功能也会伴随着另外一方面性能的减弱。系统的学习数据库知识,从而根据具体状况进行权衡,是dba和开发人员的必修课.