对于MS SQL server 数据库,有几个容易让人产生误解的问题,对于这几个问题,即使很多 SQL server DBA 都有错误认识或者认识不充分,所以我想撰文几篇,把这些容易理解错误的问题前前后后深入阐述一下,也希望纠正一下网上对这几个问题的讹传(我也可能有认识不对的地方,欢迎批评指正)。

第一个问题:聚集表的物理顺序问题。这个问题很有迷惑性,因为很多教科书在讲到聚集索引的时候都会出现“聚集索引是按照聚集键的排序顺序物理地存储数据” 类似的说法,因此我们很容易产生以下几种误解:

误解一:“按顺序物理地存储”就是磁盘本身;

误解二:聚集表的页在物理上是顺序的;

误解三:聚集表的页在物理上是顺序的,并且是连续的。

下面我们分别说明上面三种观点确实是误解。

对于误解一,我们必须要了解数据行如何被存储在数据页上。

上图是我从MSDN上截来的,从上图可以看到,在每个数据页的末尾,都有一个“行偏移量”的数组,这个数组记录了每一个数据行的开头在页面中的起始位置,即每行数据开头应该从页头偏移多少个字节。我将上图改了一下,可能更便于正确理解:

我改编的这个图中表示,从这个数据页头偏移96个字节即是第一行数据的开始位置,偏移200字节即是第二行数据的开始位置,偏移300个字节即是第三行数据的开始位置。

下面我们举个实际的例子来说明聚集表的数据在一个数据页上可能的样子。

CREATE TABLE test
(
RowId int not null primary key ,
Column1 char(100)
) INSERT INTO test(RowId,Column1)
Select 1, ' '
Union
Select 2,' '
Union
Select 10,' '

对于上表,数据第一次插入时,这三行数据在数据页上的表现如下:

而如若我再往里面插入如下数据:

INSERT INTO test(RowId,Column1)
Select 7, ' '

因为这个表的RowId 列有聚集索引(primary key 默认创建聚集索引),而数字7大约2,且小于10,那么SQL server 在执行INSERT 操作时,是不是会把RowId = 10 这行数据往下挪呢?显然SQL server 不会这么笨,而只会将RowId = 7这行数据数据附加到RowId = 10 这行数据的后面,然后再修改行偏移量数组,示意图如下:

现在我们应该可以明白:一个聚集索引表数据页上数据行的物理顺序,仅依靠行偏移列表来决定,并不取决于在磁盘上的物理位置。

对于误解二和误解三可以放在一起论述,在论述之前,我们需要先了解一下SQL server 存储引擎中页跟区的概念。
         1. SQL server 中数据存储的基本单位是页。
         2. 区是八个逻辑上连续的页的集合,用来有效的管理页,这也说明,所有的数据页一定属于某个区。
         3. 区分为混合区和统一区。混合区中的页可以被分配给多个数据库对象;统一区中的页一定是被分配给了某一个数据库对象。
         4. SQL server 在为某个数据库对象申请空间时,需要使用GAM,SGAM,PFS 系统页的信息,同时在空间被分配后,也会维护好GAM,SGAM,PFS系统页的息                      。

上面我简单列出了几个要点,更多详细信息大家可以参考下面的链接信息:
         http://msdn.microsoft.com/zh-cn/library/cc280360(v=sql.100).aspx

然后我将列举具体的情况来证明二跟三确实是误解:
         1. 当我们新建一个聚集表,并且往表里插数据,当这个表所占用空间不够8个数据页时,SQL server存储引擎都将从混合区寻找空闲的页面分配给表。而在这个阶段内,很容易出现一个混合区被同时分配给多个数据库对象(最多可达8个数据库对象)。那么,如果我们的聚集表需要再次申请磁盘空间,就很可能在起初的混合区内分配到不连续的页(相对于已分配给这个聚集表的页来说),或者要从另外一个混合区查找空闲页面,这样,是无论如何也保证不了“聚集表的页在物理上是顺序的”,更保证不了“页是连续的”。

2. 如果一个聚集表满8个页,那么,后续所有的空间申请都将分配完整的统一区,这后续的统一区是否可以顺序,或者连续呢? 当然不行,当一个申请空间的动作发出后,存储引擎都会从表所在数据文件的第一个GAM去遍历查找空闲的统一区,如果某个统一区在之前已经分配给某个其它的数据库对象,但当前这个区已经被释放,那么,SQL server 就会将这个空闲的区分配给我们的聚集表,这样也自然保证不了页在物理上的“顺序和连续”;另外,即使SQL server每次都分配从来没有分配给任何数据库对象的区给我们的聚集表,也没有办法保证页在物理上的“顺序和连续”。

3. 如果聚集表出现分页的情况,那么新申请页的页面链的“上一页”会指向被分割的数据页,“下一页”会指向被分割页在分割前指向的下一页。显然,这种情况也没有办法保证页在物理上的“顺序和连续”,而且它是索引碎片情况中的一种。

结论:对于聚集索引表数据行的物理顺序问题,在页与页的角度来看,唯一能指明聚集索引表数据页顺序的是数据页上的页面链表。因为页面链表清楚的指明了本页的上一页,及下一页的页面号分别是啥,而页面号就决定了上一页及下一页的物理位置;如果将眼光缩小到一个数据页的范围内,决定数据行物理位置的因素只有页脚的行偏移量数数组。

(转)SQL server 容易让人误解的问题之 聚集表的物理顺序问题的更多相关文章

  1. c#Winform程序调用app.config文件配置数据库连接字符串 SQL Server文章目录 浅谈SQL Server中统计对于查询的影响 有关索引的DMV SQL Server中的执行引擎入门 【译】表变量和临时表的比较 对于表列数据类型选择的一点思考 SQL Server复制入门(一)----复制简介 操作系统中的进程与线程

    c#Winform程序调用app.config文件配置数据库连接字符串 你新建winform项目的时候,会有一个app.config的配置文件,写在里面的<connectionStrings n ...

  2. SQL Server ->> 深入探讨SQL Server 2016新特性之 --- Temporal Table(历史表)

    原文:SQL Server ->> 深入探讨SQL Server 2016新特性之 --- Temporal Table(历史表) 作为SQL Server 2016(CTP3.x)的另一 ...

  3. 【SQL Server高可用性】数据库复制:SQL Server 2008R2中通过数据库复制,把A表的数据复制到B表

    原文:[SQL Server高可用性]数据库复制:SQL Server 2008R2中通过数据库复制,把A表的数据复制到B表 经常在论坛中看到有人问数据同步的技术,如果只是同步少量的表,那么可以考虑使 ...

  4. 【SQL server初级】数据库性能优化二:数据库表优化

    数据库优化包含以下三部分,数据库自身的优化,数据库表优化,程序操作优化.此文为第二部分 数据库性能优化二:数据库表优化 优化①:设计规范化表,消除数据冗余 数据库范式是确保数据库结构合理,满足各种查询 ...

  5. SQL Server时间粒度系列----第7节日历数据表详解

    本文目录列表: 1.时间粒度有关描述 2.时间维度有关功能函数3.日历数据表 4.日历数据表数据填充 5.总结语 6.参考清单列表   时间粒度有关描述   将该系列涉及到的时间粒度以及分钟以下的粒度 ...

  6. SQL SERVER 中 实现主表1行记录,子表多行记录 整合成一条虚拟列

    表中有这样的记录,简单的主子表,现要想通过left join 语句把两表关联起来 select * from tbl_diary_reback a left join tbl_diary_reback ...

  7. SQL SERVER 判断是否存在并删除某个数据库、表、视图、触发器、储存过程、函数

    -- SQL SERVER 判断是否存在某个触发器.储存过程 -- 判断储存过程,如果存在则删除IF (EXISTS(SELECT * FROM sysobjects WHERE name='proc ...

  8. SQL Server 2012 “阻止保存要求又一次创建表”的更改问题的设置方法

    我们在用SQL Server 2012 建完表后,插入或改动随意列时,提示:当用户在在SQL Server 2012企业管理器中更改表结构时.必需要先删除原来的表.然后又一次创建新表,才干完毕表的更改 ...

  9. sql server几种Join的区别测试方法与union表的合并

    /* sql server几种Join的区别测试方法 主要来介绍下Inner Join , Full Out Join , Cross Join , Left Join , Right Join的区别 ...

随机推荐

  1. Java-人民币转成大写

    /** * 人民币转成大写 hangeToBig * * @param value * @return String */ public static String 人民币转成大写(double va ...

  2. 【转】从Go、Swift语言出发

    Google于2009年第一次提出了Go的构思,Facebook在去年春天引入了Hack,随后不久Apple也发布了其Swift语言. 在战争中,胜利者写历史书:在科技中,赢的公司都在写编程语言.互联 ...

  3. rpm常用选项

    httpd-2.2.15-39.el6.centos.x86_64.rpmhttpd   -      2.2.15-    39.el6.centos.       x86_64 .rpm软件名称- ...

  4. opencv笔记4:模板运算和常见滤波操作

    time:2015年10月04日 星期日 00时00分27秒 # opencv笔记4:模板运算和常见滤波操作 这一篇主要是学习模板运算,了解各种模板运算的运算过程和分类,理论方面主要参考<图像工 ...

  5. BZOJ-1036 树的统计Count 链剖线段树(模板)=(树链剖分+线段树)

    潇爷昨天刚刚讲完...感觉得还可以...对着模板打了个模板...还是不喜欢用指针.... 1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec Memory Lim ...

  6. CRUD之delete操作

    在公司的项目中delete之后的操作有两种处理方式 1.后台删除成功之后前台页面刷新 2.后台删除成功之后页面不刷新,但是数据所在的那个div会刷新一次 3.后台删除,页面上做了一个删除,删除的td没 ...

  7. BZOJ1093 最大半连通子图

    Description 一个有向图G=(V,E)称为半连通的(Semi-Connected),如果满足:?u,v∈V,满足u→v或v→u,即对于图中任意 两点u,v,存在一条u到v的有向路径或者从v到 ...

  8. codevs1003 电话连线

    题目描述 Description 一个国家有n个城市.若干个城市之间有电话线连接,现在要增加m条电话线(电话线当然是双向的了),使得任意两个城市之间都直接或间接经过其他城市有电话线连接,你的程序应该能 ...

  9. getchar() 和 scanf("%c")的区别

    getchar()和scanf("%c")的功能都是从STDIN读一个字符,单论功能两者没有区别. 但两者的返回值是有区别的: -------------------------- ...

  10. android 事件处理概念簇

    1)事件传递函数 2)事件相关: 事件.事件源.事件监听器.MotionEvent 3)坐标系.定位.路由: 4)Window activity view viewGroup