加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 站长学院 > MsSql教程 > 正文

深入理解Sqlserver文件存储之页和应用

发布时间:2020-12-12 12:51:06 所属栏目:MsSql教程 来源:网络整理
导读:我们每天都在使用数据库,我们部门使用最多的关系数据库有 Sqlserver , Oracle ,有没有想过这些数据库是怎么存放到操作系统的文件中的? 有时候为了能够设计出最优的表结构,写出高性能的 Sqlserver 脚本,处理海量数据并发,我们必须解底层原理。 由于个

8KB

512B

12KB

512B

8KB

已分配空间

空闲

已分配空间

空闲

已分配空间


为了能够更好的利用磁盘空间,Sqlserver借鉴了操作系统的虚拟内存的概念,人为的将文件划分为了N8KB的存储空间。
这样每次分配时,都是按照
8KB空间申请,就解决了外部碎片的问题。
换句话说,
页是Sqlserver?中数据存储的基本单位,每一页的大小为8KB

实际数据

5kB

12KB

1KB

xxKB

分配的页单元

8KB

8KB

8KB

8KB

空闲


另外,每页的开头是96个字节的标头,存储了页的相关信息,包括:页码、页类型、页的可用空间以及拥有该页对象的分配单元ID等。
上述的例子分配就成为下表所示
:这样就解决了外部碎片问题。


什么是区

为什么会有区的概念,已经有了页的单位难道不够吗?
主要是为了更好的管理这些空间, Sqlserver将每8个页划分为一个区(如下表所示)。
就像百元大钞代表着
10010元人民币一样,出去买很多东西时,用百元大钞比用100张1元纸币要方面。

一个分区

1

2

3

4

5

6

7

8

?

行是怎么在页中存储的?

数据库中的数据到底是以什么样的形式存储的呢?
Sqlserver是以“物理行”为单位存储的数据,也就是说表(Table)中的每条记录(Record,逻辑行)都是顺序存放在页中的。

一个物理行的宽度是8064字节,一个逻辑行的宽度不能超过这个值。

因此,一个物理行可以包括多个逻辑行。

那么怎么找到逻辑行?即如何找到一条记录的开始地址和结束地址
Sqlserver在每页的末尾存放了每条记录的开始地址,这样我们就可以定位到某条记录的起始地址,通过下一条记录的开始位置能够知道该条记录的结束位置。

每一个这样的记录地址都占用2个字节。

如图所示,如果我想取第二条数据,那么先将一页数据都读到内存中,然后从最后读取偏移为第3开始读取2个字节,怎么可以找到行2的开始位置,同理可以读取出行2的结束位置。

假设现在有一个表,表的结构是: CREATE TABLE test( ? a INT, ? b INT )? ? 他在1:100这个页面上存储数据(1是数据文件ID--FileID,100 是页面ID--PageID)。 ? 这个页面结构大致如下图 ?

? 在页头部分,会记录页面属性,包括页面编号等,还会记录当前页面空闲部分的起始位置在哪里 ?www.2cto.com ? ? 这样SQLSERVER在要插入新数据的时候,就能够很快地找到开始插入的位置,而页尾的偏移量 ? 记录了每一条数据行的起始位置。这样SQLSERVER在找每一条记录的时候,就能很快找到不会 ? 把前一条记录和后一条搞混。在图例这一页里现在有两条记录:(1,100)和(2,200)第一条 ? 记录的开始位置是96,第二条记录的开始位置是111,从126开始,是空闲的空间 ? 当页面里的数据行发生变化的时候,SQLSERVER不但要去修改数据本身,还要修改这些偏移量的值, ? 以保证SQLSERVER能够继续准确地管理数据页面里的每一行。

列在页中是怎么存储的?

现在我们已经读取到行了并且已经在内存里了,接下来怎么解析出一行中的所有列?也就是这些列是怎么存放的?数据库表中的列无非就两种情况:定长列、变长列。

首先假设只有定长列,那么很容易想到一样中的每列的之顺序存放就行了,因为是定长的,完全可以将每列的偏移放到另外一个地方单独存储,如果要取某个特定的列,每个列的位置很容易定位:如下表所示:

2字节

3字节

6字节

10字节

3字节

2字节

1

23

55

A

C

D


如果要取红色的数据,那么它的

?开始位置=(行开始位置)+?2字节+3字节+6字节+10字节。

?结束位置=?开始位置?+?3字节。

其中每个列的长度完全可以用另一张表存放

1

2

3

4

5

6

长度(字节)

2

3

6

10

3

2


具体行结构的详细信息如下:

?

假如设计的表结构为?:

Col1

Col2

Col3

Col4

Char(5)(not?null)

Int?(null)

Char(3)(null)

Char(6)(not?null)


在数据库中存放数据为:

Col1

Col2

Col3

Col4

‘ABCDE’

‘123’

‘null’

‘ccc?‘


则数据在数据库文件中数据以如下形式存在:

?


?

如果其中有变长列呢,这个结构又是怎么存储的?有变长列最大的不同就是每个列的长度是不定的(同一列,每行长度都不一样),也就是不能用另外一张表存放。那么我们只能把列的长度放在行内了。这样就解决了实际长度定位的问题,上面已经说过,sqlserver有一个行偏移矩阵。

如果我们定义的表结构如下:

Col1

Col2

Col3

Col4

Col5

Char(2)(not?null)

Varchar(250)(not?null)

Varchar(5)(null)

Varchar(20)(not?null)

Small?int?(null)

-8

假如这行数据为:

Col1

Col2

Col3

Col4

Col5

AAA

RELICATE(‘X’,250)

null

‘ABC’

123

-9

则数据在数据库中实际的存放形式为:

?



结论:

1.数据库列中尽量不用可空类型,当值为空时,实际不占用位置,并且也不能作为索引的键值。导致where语句中含有?is?null?或者?is?not?null?时只能进行全表扫描,并且可空类型也容易导致空引用异常。

2.在设计列时,只有列长度确定的才用定长,比如身份证。其他情况基本上应该用varchar边长类型,不但节省空间的同时,一个页存放的数据会变多。导致同样的数据量读取页的次数变少,减少I/O,提高性能。

3.如图-1所示,聚簇索引不是按物理顺序存放的,是按逻辑物理顺序存放的(大多数人在这里会有误解。)

4.正常情况下不要使用varchar(max),因为这个列的数据肯定放不在一个页里,为了解决这个问题,sqlserver在列里只存放了一个指针。真正的数据放在了其他多个页里。每读取一行中的列都会至少多一次I/O,影响性能。

附注,参考资料:

(1)?Microsoft?SQL?Server?2005技术内幕:存储引擎(中文)

(2)微软MSDN:?http://msdn.microsoft.com/zh-cn/library/ms190969(v=sql.105).aspx

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

我们每天都在使用数据库,我们部门使用最多的关系数据库有SqlserverOracle,有没有想过这些数据库是怎么存放到操作系统的文件中的?
有时候为了能够设计出最优的表结构,写出高性能的
Sqlserver脚本,处理海量数据并发,我们必须解底层原理。
由于个人兴趣最近研究了下
Sqlserver的文件存储,下面只讲解Sqlserver的最小存储单元——页。

什么是页

为什么会有一个页的概念。我们知道对于操作系统来说,文件可以认为是一个很大的线性空间,如果按地址空间顺序分配容量(也就是按段式存储),则有可能造成很多的外部碎片,导致很多的容量很难再次使用,只有移动合并空间才能腾出更多的空间。
例如:如下表所示,如果我现在要申请1024B字节的空间,显然下面的两个空间空间单个计算不够,合起来却是够用的的,只能移动合并空间。

    推荐文章
      热点阅读