写在前面:本篇文章需要你对索引和SQL中数据的存储方式有一定了解.标题中高级两个字仅仅是因为本篇文章需要我的T-SQL进阶系列文章的一些内容作为基础.

简介


在SQL Server中,存储数据的最小单位是页,每一页所能容纳的数据为8060字节.而页的组织方式是通过B树结构(表上没有聚集索引则为堆结构,不在本文讨论之列)如下图:

在聚集索引B树中,只有叶子节点实际存储数据,而其他根节点和中间节点仅仅用于存放查找叶子节点的数据.

每一个叶子节点为一页,每页是不可分割的. 而SQL Server向每个页内存储数据的最小单位是表的行(Row).当叶子节点中新插入的行或更新的行使得叶子节点无法容纳当前更新或者插入的行时,分页就产生了.在分页的过程中,就会产生碎片.

理解外部碎片


首先,理解外部碎片的这个“外”是相对页面来说的。外部碎片指的是由于分页而产生的碎片.比如,我想在现有的聚集索引中插入一行,这行正好导致现有的页空间无法满足容纳新的行。从而导致了分页:

因为在SQL SERVER中,新的页是随着数据的增长不断产生的,而聚集索引要求行之间连续,所以很多情况下分页后和原来的页在磁盘上并不连续.

这就是所谓的外部碎片.

由于分页会导致数据在页之间的移动,所以如果插入更新等操作经常需要导致分页,则会大大提升IO消耗,造成性能下降.

而对于查找来说,在有特定搜索条件,比如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和开发人员的必修课.

T-SQL查询高级—SQL Server索引中的碎片和填充因子的更多相关文章

  1. 转: SQL Server索引的维护 - 索引碎片、填充因子

    转:http://www.cnblogs.com/kissdodog/archive/2013/06/14/3135412.html 实际上,索引的维护主要包括以下两个方面: 页拆分 碎片 这两个问题 ...

  2. SQL Server索引的维护 - 索引碎片、填充因子 <第三篇>

    实际上,索引的维护主要包括以下两个方面: 页拆分 碎片 这两个问题都和页密度有关,虽然两者的表现形式在本质上有所区别,但是故障排除工具是一样的,因为处理是相同的. 对于非常小的表(比64KB小得多), ...

  3. [转帖]SQL Server 索引中include的魅力(具有包含性列的索引)

    SQL Server 索引中include的魅力(具有包含性列的索引) http://www.cnblogs.com/gaizai/archive/2010/01/11/1644358.html 上个 ...

  4. SQL SERVER 索引中聚集索引分析和Transact-SQL语句优化

    一. 聚集索引B树分析1.聚集索引按B树结构进行组织的,索引B树种的每一页称为一个索引节点.B树的顶端节点称为根节点.  索引中的低层节点称为叶节点.根节点与叶节点之间的任何索引级别统称为中间级.在聚 ...

  5. SQL Server 索引中include

    SQL Server 索引中include的魅力(具有包含性列的索引) http://www.cnblogs.com/gaizai/archive/2010/01/11/1644358.html 开文 ...

  6. 用SQL查询方式显示GROUP BY中的TOP解决方法[转]

    用SQL查询方式显示GROUP BY中的TOP怎样用一个SQL语句来显示 分组后每个组的前几位 比如把一个学校所有学生的成绩按班级分组,再显示每个班级前五名的信息. 班级     学生   成绩 一班 ...

  7. SQL Server 索引中include的魅力(具有包含性列的索引)

    2010-01-11 20:44 by 听风吹雨, 22580 阅读, 24 评论, 收藏, 编辑 开文之前首先要讲讲几个概念 [覆盖查询] 当索引包含查询引用的所有列时,它通常称为“覆盖查询”. [ ...

  8. SQL Server 索引中include的魅力(具有包含性列的索引)(转载)

    开文之前首先要讲讲几个概念 [覆盖查询] 当索引包含查询引用的所有列时,它通常称为“覆盖查询”.  [索引覆盖] 如果返回的数据列就包含于索引的键值中,或者包含于索引的键值+聚集索引的键值中,那么就不 ...

  9. MySQL 基本语法(1.表字段操作,2表记录管理 3.运算符管理4.SQL查询 5.约束6.索引

    .表字段的操作 .语法:alter table 表名 执行动作; .添加字段(add) .添加到末尾 alter table 表名 add 字段名 数据类型; .添加到第一列 alter table ...

随机推荐

  1. UnicodeDecodeError: 'ascii' codec can't decode byte 0xe6 in position 0: ordinal not in range(128)

    2017-03-16 11:23:29.601 1238 ERROR nova.compute.manager [instance: 3f195047-250a-4eb5-8da0-63bea6e26 ...

  2. 在浏览器端用JS创建和下载文件

    前端很多项目中,都有文件下载的需求,特别是JS生成文件内容,然后让浏览器执行下载操作(例如在线图片编辑.在线代码编辑.iPresst等). 但受限于浏览器,很多情况下我们都只能给出个链接,让用户点击打 ...

  3. day1 java基础回顾- Properties类与配置文件

    Properties配置文件说明 Properties类对应.properties文件.文件内容是键值对,键值对之间使用"="或空格隔开.开头是"#"的表示注释 ...

  4. Ubuntu下CodeBlocks更改调试终端

    Ubuntu下CodeBlocks更改调试终端   Ubuntu下的CodeBlocks自带的调试终端xterm不能进行复制粘贴操作,更换调试终端就可以解决了,就是把ubuntu下的gnome-ter ...

  5. SharePoint 2010 将带有工作流的模板移动到另一个站点集

    HOWTO Move or Migrate SharePoint 2010 List-based Workflows between Sites and Site Collections I’ve e ...

  6. JS Guid生成

    function numToGuid(uid) { var str = "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"; var l = uid.to ...

  7. [poj]1050 To the Max dp

    Description Given a two-dimensional array of positive and negative integers, a sub-rectangle is any ...

  8. underscore.js and moment.js

    underscore.js and moment.js underscore.js 一.简介Underscore.js是一个JavaScript实用库,提供了一整套函数式编程的实用功能.它提供了100 ...

  9. appium自动化测试框架——封装获取设备信息类

    在上一节中,我们已经解决了如何在python中执行cmd,并获取执行结果.下面就小小实战一下,获取设备信息. 一.思路 1.windows上获取设备信息的方法 输入dos命令“adb devices” ...

  10. mysql-5.5.56免安装版配置方法

    1. 下载mysql-5.5.56-winx64 网址:dev.mysql.com/downloads/mysql/ 2. 解压MySQL压缩包    将以下载的MySQL压缩包解压到自定义目录下,我 ...