一:背景

1. 讲故事

在 SQLSERVER 的众多阻塞场景中,有不小的一部分是由于 PFS 页上的 闩锁 等待造成的,毕竟写页操作一定是要串行化的,在面对 闩锁(PAGELATCH_X) 等待问题上,一定要搞明白 PFS 页到底是什么? 这篇就来好好聊一聊。

二:PFS 详解

1. 什么是 PFS 页

我们知道数据库是由海量的 数据页 组成,表记录会写入到 数据页 上,那海量的数据页如何管理呢? SQLSERVER 想到了一个办法,从海量的数据页中按一定规则择取一些作为 管理页 使用,比如:

  • GAM 跟踪区分配情况
  • SGAM 跟踪共享区分配情况
  • PFS 跟踪数据页的空间使用情况

这里我简述一下吧,GAM数据页中一个 bit 跟踪一个 64k 的空间(一个区),所以一个 8k 的GAM页 可以跟踪大约 (8 * 8192) * 64k = 4G 的空间,而 PFS 数据页用一个 byte 跟踪一个 8k 的数据页,理论上可以管理 8192 * 8k = 64M 的数据。

接下来的问题是这 1byte 是如何标记一个数据页的使用情况呢? 简单的画个图吧。

从图中看,这一个跟踪 byte 差不多都给塞满了,有了这些基础之后,接下来用一个案例来演示一下。

2. 案例演示

创建一个 MyTestDB 数据库,新建一个 post 表,参考sql如下:


CREATE DATABASE MyTestDB
GO
USE MyTestDB
GO
CREATE TABLE post (id INT IDENTITY, content CHAR(1000))

创建好之后来观察下 MyTestDB 的 mdf 文件中的 PFS 数据页,可以使用 DBCC PAGE 命令,但这个命令需要获取 fileid, pageid 这两个参数,那如何提取呢?

  • 如何提取 fileid

可以查询 sys.database_files 系统表提取。


SELECT file_id,name,physical_name FROM sys.database_files;

  • 如何提取 pageid

刚才说过了,这种管理页都是有规律的,比如 PFS 页是 64M 一个,在 file_id=1 的文件中,第一个区的第1号数据页就是 PFS 页,即 MyTestDB.mdf 文件偏移 8192byte 的位置,这里稍微补充一下,mdf 文件的第一个区中的 8 个 page 都是有特殊用途的,画个简图如下:

接下来观察下这个 PFS 数据页,它的 type=11,后续我们还会对它不断的观察。


DBCC TRACEON(3604)
DBCC PAGE(MyTestDB,1,1,3) ------ output -----
Page @0x000001DB52882000 m_pageId = (1:1) m_headerVersion = 1 m_type = 11
m_typeFlagBits = 0x0 m_level = 0 m_flagBits = 0x0
m_objId (AllocUnitId.idObj) = 99 m_indexId (AllocUnitId.idInd) = 0 Metadata: AllocUnitId = 6488064
Metadata: PartitionId = 0 Metadata: IndexId = 0 Metadata: ObjectId = 99
m_prevPage = (0:0) m_nextPage = (0:0) pminlen = 0
m_slotCnt = 1 m_freeCnt = 2 m_freeData = 8188
m_reservedCnt = 0 m_lsn = (37:192:1) m_xactReserved = 0
m_xdesId = (0:0) m_ghostRecCnt = 0 m_tornBits = 651443756
DB Frag ID = 1

好了,继续测试吧,插入一条数据,观察 PFS 页对应的位数是否有变化? 比如空间使用率,参考sql如下:


INSERT INTO post(content) VALUES ('aaaaa')
DBCC TRACEON(3604)
DBCC IND(MyTestDB,post,-1)

从图中可以看到,插入的 aaaaa 记录写到了 240 号数据页,跟踪这个数据页的byte 理论上是在 PFS 页偏移 0n240 byte 的位置,那具体是哪一个位置的 byte 跟踪的呢?我们用 windbg 观察下 PFS 数据页的内存页地址即可,首先用 DBCC PAGE(MyTestDB,1,1,2) 找到 Page 页在内存的首地址。


Memory Dump @0x000000C8177F8000 000000C8177F8000: 010b0000 00000000 00000000 00000000 00000000 ....................
000000C8177F8014: 00000100 63000000 0200fc1f 01000000 01000000 ....c...............
000000C8177F8028: 25000000 e0000000 1c000000 00000000 00000000 %...................
000000C8177F803C: 2c3ed426 01000000 00000000 00000000 00000000 ,>.&................
000000C8177F8050: 00000000 00000000 00000000 00000000 00009c1f ....................
000000C8177F8064: 44444444 00004444 60647060 74706070 60607060 DDDD..DD`dp`tp`p``p`
000000C8177F8078: 60707060 40404040 40404040 61706070 60606070 `pp`@@@@@@@@ap`p```p
000000C8177F808C: 60646060 60706060 60706060 60606070 40404040 `d```p```p`````p@@@@

接下来用 WinDbg 附加 SqlServer,由于前 96byte 是数据页头,所以理论位置应该是 000000C8177F8000+0x60 + 0n240 的位置,截图如下:

哈哈,终于给找到了,那 0x41 是什么意思呢?可以用 windbg 的 .formats 命令观察一下。


0:118> .formats 41
Evaluate expression:
Hex: 00000000`00000041
Decimal: 65
Decimal (unsigned) : 65
Octal: 0000000000000000000101
Binary: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 01000001

对应 byte 中位解读,可以知道当前 240 号数据页的空间使用率为 1 ~50%,同时 数据页已被分配

这里我们顺带观察下 PageID=80 号这个 IAM 页,看看 PFS 中如何表示它的,在内存中计算位置应该是 000000C8177F8000+0x60 + 0n80,输出如下:


0:118> dp 000000C8177F8000+0x60 + 0n80 L2
000000c8`177f80b0 70607070`60606070 60302070`60606070
0:118> .formats 70
Evaluate expression:
Hex: 00000000`00000070
Decimal: 112
Decimal (unsigned) : 112
Octal: 0000000000000000000160
Binary: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 01110000

再对应 byte 位解读,可以知道当前的 80号数据页 的四点信息:

  • 当前数据页已分配
  • 当前数据页在混合区
  • 当前数据页为 IAM 页
  • 当前为特殊管理页

接下来再插入 4条 数据,观察下 空间使用率 是否有变化 ?


INSERT INTO post(content) VALUES ('bbbbb')
INSERT INTO post(content) VALUES ('ccccc')
INSERT INTO post(content) VALUES ('ddddd')
INSERT INTO post(content) VALUES ('eeeee')

DBCC PAGE 观察内存页首地址之后,再次用 WinDBG 附加观察,输出如下:

0:126> .formats 42
Evaluate expression:
Hex: 00000000`00000042
Decimal: 66
Decimal (unsigned) : 66
Octal: 0000000000000000000102
Binary: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 01000010

从输出可以看到已经由原来的 41 变成了 42,其中的 010 刚好表示当前数据页为 51~80% 满,作为校验的话,可以直接观察 240号 页头上的 PFS (1:1) = 0x42 ALLOCATED 80_PCT_FULL 描述。


DBCC TRACEON(3604)
DBCC IND(MyTestDB,post,-1)
DBCC PAGE(MyTestDB,1,240,2) ----- output ----- PAGE: (1:240)
... PAGE HEADER: Page @0x000001DB58F92000
...
Allocation Status GAM (1:2) = ALLOCATED SGAM (1:3) = NOT ALLOCATED PFS (1:1) = 0x42 ALLOCATED 80_PCT_FULL
DIFF (1:6) = CHANGED ML (1:7) = NOT MIN_LOGGED
...

三:总结

总的来说 PFS页 瓶颈主要来自于用户创建的 临时表表变量,常常在高并发的场景下由于高频的创建和删除临时表以及对临时表的插入,SQLSERVER 不得不高频的修改 PFS页 造成这一块的瓶颈,如果大家在阻塞中看到大量的PageLatch_x 等待资源,记得一定要将 Tempdb 中的 ndf 划分为多个来分摊写入压力。

SQLSERVER 阻塞之 PFS 页到底是什么?的更多相关文章

  1. SQLServer基础之数据页类型:GAM,SGAM,PFS

    简介 我们已经知道SQL Server IO最小的单位是页,连续的8个页是一个区.SQL Server需要一种方式来知道其所管辖的数据库中的空间使用情况,这就是GAM页和SGAM页. GAM页 GAM ...

  2. 深入理解Sqlserver文件存储之页和应用 (转)

    我们每天都在使用数据库,我们部门使用最多的关系数据库有Sqlserver,Oracle,有没有想过这些数据库是怎么存放到操作系统的文件中的?有时候为了能够设计出最优的表结构,写出高性能的Sqlserv ...

  3. SQL Server 存储(4/8):理解Page Free Space (PFS) 页

    我们已经讨论了GAM与SGAM页,数据页(Data Page) ,现在我们来看下页面自由空间页(Page Free Space (PFS) ). PFS在数据文件里是第2页(页号1,页号从0开始),接 ...

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

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

  5. sqlserver 存储过程中使用临时表到底会不会导致重编译

    曾经在网络上看到过一种说法,SqlServer的存储过程中使用临时表,会导致重编译,以至于执行计划无法重用, 运行时候会导致重编译的这么一个说法,自己私底下去做测试的时候,根据profile的跟踪结果 ...

  6. SQLServer中的页如何影响数据库性能 (转)

    无论是哪一个数据库,如果要对数据库的性能进行优化,那么必须要了解数据库内部的存储结构.否则的话,很多数据库的优化工作无法展开.对于对于数据库管理员来说,虽然学习数据库的内存存储结构比较单调,但是却是我 ...

  7. SQLSERVER数据库死锁与优化杂谈

    死锁杂谈 当数据库死锁时,SqlServer会释放一个优先级较低的锁,让另一个事务运行:所以,即时去捕捉数据库死锁,是挺不容易的. 如果,数据库死锁比较长时间,那么死锁是可以被捕捉的. 可以用SqlS ...

  8. SQLSERVER聚集索引与非聚集索引的再次研究(上)

    SQLSERVER聚集索引与非聚集索引的再次研究(上) 上篇主要说聚集索引 下篇的地址:SQLSERVER聚集索引与非聚集索引的再次研究(下) 由于本人还是SQLSERVER菜鸟一枚,加上一些实验的逻 ...

  9. 【转】怎样查出SQLServer的性能瓶颈

    怎样查出SQLServer的性能瓶颈 --王成辉翻译整理,转贴请注明出自微软BI开拓者[url]www.windbi.com[/url]--原帖地址 如果你曾经做了很长时间的DBA,那么你会了解到SQ ...

  10. (转)解释一下SQLSERVER事务日志记录

    本文转载自桦仔的博客http://www.cnblogs.com/lyhabc/archive/2013/07/16/3194220.html 解释一下SQLSERVER事务日志记录 大家知道在完整恢 ...

随机推荐

  1. 开源项目在线化 中文繁简体转换/敏感词/拼音/分词/汉字相似度/markdown 目录

    前言 以前在 github 上自己开源了一些项目.碍于技术与精力,大部分项目都是 java 实现的. 这对于非 java 开发者而言很不友好,对于不会编程的用户更加不友好. 为了让更多的人可以使用到这 ...

  2. SQLSever视图和存储过程

    一.视图(View) 1. 为什么要学习视图? 在没有视图之前,我们都是写各种各样的SQL语句,有点,非常灵活.后面我们学习应用程序开发的时候,通过C#发送过来的SQL语句 到达数据库的时候,会执行什 ...

  3. mysql删库报错

    3.开发人员测试环境删库报错 #解决:在数据库的物理目录中(mysql的data目录),进入要删除的数据库目录,查看是否有文件存在,若存在,使用rm -rf 命令清除:再次执行删除数据库命令即可 [r ...

  4. 视频服务HDR Vivid 还原色彩,让所见成“真”

    如今,视频正在以一种前所未有的方式渗入日常生活,是当下人们记录生活最热门的方式.所以,用户对视频的画质要求越来越高,App想要吸引更多的用户,拥有视频画质新技术的强力支撑很关键. HDR(High-D ...

  5. Ian Lance Taylor

    https://img.mukewang.com/5a9dfda50001933e23006728.png 在GCC的世界中,没有人比Ian更火.在GCC maillist中,Ian的身影呈现在前端中 ...

  6. C++初阶(stack+queue)

    stack stack介绍 stack是一种先进后出的数据结构,只有一个出口,类似于栈.stack容器哦允许新增元素,移除元素,取得栈顶元素,但是除了最顶端之后,没有任何其他办法可以存取stack的其 ...

  7. js 金钱3位格式化

    function formatCash(str) { return str.split('').reverse().reduce((prev, next, index) => { return ...

  8. js-day05-对象

    为什么要学习对象 没有对象时,保存网站用户信息时不方便,很难区别 对象是什么 1.对象是一种数据类型 2.无序的数据集合 对象有什么特点 1.无序的数据的集合 2.可以详细的描述某个事物' 对象使用 ...

  9. 【面试真题】ThoughtWorks-编程结对技术面试(一面)-2022年2月11日

    一.技术问题 1.Hbase (1)介绍 (2)项目中是否有用到 于:存大量数据(千万),考虑性能,方便进行数据处理,对其进行分析 自己:ADS层和Flink的数据,DWD计算出的的中间层数据存入DW ...

  10. Spring面试点汇总

    Spring面试点汇总 我们会在这里介绍我所涉及到的Spring相关的面试点内容,本篇内容持续更新 我们会介绍下述Spring的相关面试点: Spring refresh Spring bean Sp ...