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

什么是页,区?

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

8KB

512B

12KB

512B

8KB

已分配空间

空闲

已分配空间

空闲

已分配空间

为了能够更好的利用磁盘空间,Sqlserver借鉴了操作系统的虚拟内存的概念,人为的将文件划分N个8KB的存储空间,这样每次分配时,都是按照8KB空间申请,就解决了外部碎片的问题,也就是说Sqlserver 中数据存储的基本单位,页的大小为8KB,每页的开头是96字节的标头用于存储有关页的存储信息,其中有页码、页类型、页的可用空间以及拥有该页对象的分配单元ID。上述的例子分配就成为下表所示:这样就解决了外部碎片问题。

业内分配

8kB

12KB

8KB

xxKB

页单元

8KB

8KB

8KB

8KB

空闲

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

一个分区

页1

页2

页3

页4

页5

页6

页7

页8

表3

为了有个页有更具体的认识,下表为页头的结构:

图-1

行是怎么在页中存储的?

那么数据库中的数据到底是以什么样的形式存储在数据中的呢?Sqlserver是以行为单位存储的数据,也就是说表中的每条数据(每行数据为一个块)顺序存放在页中的,那么怎么找到行?也就是一行的开始地址和结束地址? Sqlserver在每页的末尾以2个字节为单位存放了每行的开始地址,这样我们就可以定位到行的开始,通过下一条的开始位置能够知道本条记录的结束位置,这样我们就可以取出这行数据了。

图-2

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

列是怎么在页中的存储?

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

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

2字节

3字节

6字节

10字节

3字节

2字节

1

23

55

A

C

D

表-4

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

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

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

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

1

2

3

4

5

6

长度(字节)

2

3

6

10

3

2

表-5

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

图-3

假如设计的表结构为 :

Col1

Col2

Col3

Col4

Char(5)(not null)

Int (null)

Char(3)(null)

Char(6)(not null)

表-6

在数据库中存放数据为:

Col1

Col2

Col3

Col4

‘ABCDE’

‘123’

‘null’

‘ccc ‘

表-7

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

图-4

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

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

图-5

结论:

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

深入理解Sqlserver文件存储之页和应用 (转)的更多相关文章

  1. Android 文件存储浅析

    最近做的一个需求和文件存储有关系.由于之前没有系统梳理过,对文件存储方面的知识一直很懵懂.趁着周末有时间,赶紧梳理一波. 这首从网上找到的一张图,很好的概括了外部存储和内部存储. 下面我们再来具体介绍 ...

  2. Linux文件系统十问---深入理解文件存储方式(rhel6.5,EXT4)【转】

    本文转载自:https://blog.csdn.net/tongyijia/article/details/52832236 前几天在红黑联盟上看了一篇博客<Linux文件系统十问—深入理解文件 ...

  3. Kafka深入理解-1:Kafka高效的文件存储设计

    文章摘自:美团点评技术团队  Kafka文件存储机制那些事 Kafka是什么 Kafka是最初由Linkedin公司开发,是一个分布式.分区的.多副本的.多订阅者,基于zookeeper协调的分布式日 ...

  4. (4.19)深入理解SQLSERVER的日志链

    您真的理解了SQLSERVER的日志链了吗? 转自:https://www.cnblogs.com/lyhabc/p/3460272.html 先感谢宋沄剑给本人指点迷津,还有郭忠辉童鞋今天在QQ群里 ...

  5. SQL Server :理解Page Free Space (PFS) 页

    原文:SQL Server :理解Page Free Space (PFS) 页 我们已经讨论了GAM与SGAM页,数据页(Data Page) ,现在我们来看下页面自由空间页(Page Free S ...

  6. Kafka与RocketMq文件存储机制对比

    一个商业化消息队列的性能好坏,其文件存储机制设计是衡量一个消息队列服务技术水平和最关键指标之一. 开头问题 kafka文件结构和rocketMQ文件结构是什么样子?特点是什么? 一.目录结构 Kafk ...

  7. Android_文件存储

    因为项目的需求,想实现一个指导使用的欢迎页效果,通过在网上的询问,给的一种解决办法是通过SharedPreferences文件存储方式来实现,具体的实现类似于通过第一次取得SharedPreferen ...

  8. Android 数据存储之 文件存储

    -------------------------------------------文件存储----------------------------------------------- 文件存储是 ...

  9. Sqlserver数据库存储路径的修改

    Sqlserver数据库存储路径的修改 Sqlserver数据库存储路径问题:本系统sqlserver路径默认是存储在C盘目录下的,由于数据会慢慢变大和避免重装系统数据丢失等问题,最好手动将路径设置在 ...

随机推荐

  1. Angular2入门系列教程7-HTTP(一)-使用Angular2自带的http进行网络请求

    上一篇:Angular2入门系列教程6-路由(二)-使用多层级路由并在在路由中传递复杂参数 感觉这篇不是很好写,因为涉及到网络请求,如果采用真实的网络请求,这个例子大家拿到手估计还要自己写一个web ...

  2. Linux下Nodejs安装(完整详细)

    之前安装过windows下以及Mac下的node,感觉还是很方便的,不成想今天安装linux下的坑了老半天,特此记录. 首先去官网下载代码,这里一定要注意安装分两种,一种是Source Code源码, ...

  3. Sublime配置python开发环境

    Package Control Package Control 是Sublime 里直接安装附加插件的包管理器.可以通过以下步骤手动安装: 1.点击Preferences > Browse Pa ...

  4. 前端学HTTP之安全HTTP

    前面的话 HTTP的主要不足包括通信使用明文(不加密),内容可能会被窃听:不验证通信方的身份,有可能遭遇伪装:无法证明报文的完整性,有可能被篡改 基本认证和摘要认证能够使得用户识别后较安全的访问服务器 ...

  5. .NET平台开源项目速览(16)C#写PDF文件类库PDF File Writer介绍

    1年前,我在文章:这些.NET开源项目你知道吗?.NET平台开源文档与报表处理组件集合(三)中(第9个项目),给大家推荐了一个开源免费的PDF读写组件 PDFSharp,PDFSharp我2年前就看过 ...

  6. “此网页上的某个 Web 部件或 Web 表单控件无法显示或导入。找不到该类型,或该类型未注册为安全类型。”

    自从vs装了Resharper,看见提示总是手贱的想去改掉它.于是乎手一抖,把一个 可视web部件的命名空间给改了. 喏,从LibrarySharePoint.WebPart.LibraryAddEd ...

  7. Android Studio —— 创建Menu菜单项

    大多数android程序的右上角都会设置一个菜单按钮比如微信的界面右上角的加号. 这个需要在layout同级目录下新建文件夹命名为menu,再右击新建的menu新建xml文件:

  8. CentOS7之按时间段截取指定的Tomcat日志到指定文件的方法

    CentOS7之按时间段截取指定的Tomcat日志到指定文件的方法 sed -n '/2016-11-02 15:00:/,/2016-11-02 15:05:/p' catalina.out > ...

  9. Mysql - 数据库操作

    之前介绍了数据库的增删改查, 发现忘记了数据库的一些基本操作, 比如建库, 建表, 改表等等. 那这里就来小结一下数据库sql形式的基本操作. 一.库操作 1. 建库 在建库之前, 可能需要看一下, ...

  10. linux yum命令详解

    yum(全称为 Yellow dog Updater, Modified)是一个在Fedora和RedHat以及SUSE中的Shell前端软件包管理器.基於RPM包管理,能够从指定的服务器自动下载RP ...