本文分为两个问题: 第一,碎片是什么;第二,碎片怎么处理;
现在,来找解决这两个问题:
一、碎片是什么
说到碎片,就要提到索引了,索引用着挺爽的啊!是的,一旦索引建立,我们搜索数据的效率就提高了;然后我们就要想一想了,索引将我们的数据排序了,不管聚集还是非聚集索引总归是将数据排序了。这些数据给排序了,那么问题来了,我们个这些已经排序的数据进行插入操作会产生什么后果?人家本来好好排着队呢!插队的来了!
假设一群很瘦的人在排队,突然插入一个胖子,且不说胖子捣乱了正常的排序规则,胖子插进队伍中,一个人占了两个人的空间,这个时候对于瘦的人来说,胖子浪费了一个人的空间,如果不断的有胖子插队,浪费的空间会越来越多。浪费的空间在数据库中就是碎片!这里有一篇博客,举例说明了在数据库中碎片的产生。在数据库中,数据存在于页内,一个页是8K,索引已经建立的前提下,这8K数据是好好的排序待着的,然后,向这个页中插入一条数据(比原来数据中最大行还要大),这时后面数据就会遵循B树规则开始移动,因为这个页已经满了,后面的数据就会被挤出去若干条到下一页,如果下一页也是满的,依次往下排,这对于数据库来说消耗是很大的。这个过程中,在最初的页中,就会产生碎片,因为插入的数据偏大,导致后面的数据后移到别人页,就会空出空间了,而这部分空间就暂时没法利用,造成碎片。这种情况属于内部碎片,是页内的。因为在这个区中可能,页与页之间还是连续的。仅仅是最初的那个页中的数据是断续的。首先,插入数据造成数据移动消耗大,另外,在页中打乱了页内的排序,造成查询性能下降。这就是碎片造成影响。
另一种,情况就是插入的数据使页与页之间造成断续,比如,插入的数据正好在页中最后一行,被挤出到别的页的数据,与原来的页之间没有了连续,这后果就更严重了,这种情况就是外部的碎片。
这样看,在一定程度上索引的性能就下降了。就需要重新维护索引了。怎么维护呢?将原来的索引删除,重新构建?这种做法,在某些场景中是很合适的。比如,插入的动作比较少的情况下,在合适的时候,重现构建索引是可取的。除了,重构呢?
二、碎片怎么处理
我们要进行索引的维护,首先要知道一些查询数据库性能的语句,来获取当前数据库的性能,做出判断之后,采取不同的措施。
此时我们用到的就是元数据函数sys.dm_db_index_physical_stats,它有助于确定数据库中的页和区段有多满。使用这个函数得到当前数据的一些性能信息。图片来源于参考博客。
下面,先来说第一种类似于重建:
1、使用DROP_EXISTING语句重建索引:为了避免重建两次索引,使用DROP_EXISTING语句重建索引,因为这个语句是原子性的,不会导致非聚集索引重建两次,但同样的,这种方式也会造成阻塞。缺点:阻塞:与卸载重建方法类似,这种技术也导致并面临来自其他访问该表(或该表的索引)的查询的阻塞问题。、
使用约束的索引:与卸载重建不同,具有DROP_EXISTING子句的CREATE INDEX语句可以用于重新创建使用约束的索引。如果该约束是一个主键或与外键相关的唯一性约束,在CREATE语句中不能包含UNIQUE。
具有多个碎片化的索引的表:随着表数据产生碎片,索引常常也碎片化。如果使用这种碎片整理技术,表上所有索引都必须单独确认和重建。
CREATE UNIQUE CLUSTERED INDEX IX_C1 ON t1(c1)
WITH (DROP_EXISTING = ON)
2、用ALTER INDEX REBUILD语句重建索引:使用这个语句同样也是重建索引,但是通过动态重建索引而不需要卸载并重建索引.是优于前两种方法的,但依旧会造成阻塞。可以通过ONLINE关键字减少锁,但会造成重建时间加长。
阻塞:这个依然有阻塞问题。
事务回滚:ALTER INDEX REBUILD完全是一个原子操作,如果它在结束前停止,所有到那时为止进行的碎片整理操作都将丢失,可以通过ONLINE关键字减少锁,但会造成重建时间加长。
3、使用ALTER INDEX REORGANIZE:这种方式不会重建索引,也不会生成新的页,仅仅是整理叶级数据,不涉及非叶级,当遇到加锁的页时跳过,所以不会造成阻塞。但同时,整理效果会差于前三种。
4、填充因子:填充因子的概念可以理解为预留一定的空间存放插入和更新新增加的数据,以避免页拆分。 填充因子值的选择:
如何设置填充因子的值并没有一个公式或者理念可以准确的设置。使用填充因子虽然可以减少更新或者插入时的分页,但同时因为需要更多的页,所以降低了查询的性能和占用更多的磁盘空间.如何设置这个值进行trade-off需要根据具体的情况来看。具体情况要根据对于表的读写比例来看,我这里给出我认为比较合适的值:
- 当读写比例大于100:1时,不要设置填充因子,100%填充
- 当写的次数大于读的次数时,设置50%-70%填充
- 当读写比例位于两者之间时80%-90%填充