一:背景

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. C#在Xp系统执行.exe程序的报错怎么查看原因

    我的电脑---->管理---->事件查看器----->应用程序,查看错误来源

  2. .NET深入了解哈希表和Dictionary

    引子 问题:给定一串数字{1,2,5,7,15,24,33,52},如何在时间复杂度为O(1)下,对数据进行CURD? 数组:我创建一个Length为53的数组,将元素插入相同下标处,是不是就可以实现 ...

  3. KafkaOffsetMonitor:监控消费者和延迟的队列

    个人名片: 因为云计算成为了监控工程师‍ 个人博客:念舒_C.ying CSDN主页️:念舒_C.ying 目录 消费者组列表 消费组的topic列表 图中参数含义解释如下: topic的历史位置 O ...

  4. 关于urllib.request解析网站不能decode

    原因 不能decode,无论以gbk还utf8都无法正常解码,这个原因是因为 网页被gzip压缩了,需要解压缩 解决办法 import urllib.request import gzip url = ...

  5. services资源+pod详解

    services资源+pod详解 一.Service 虽然每个Pod都会分配一个单独的Pod IP,然而却存在如下两问题: Pod IP 会随着Pod的重建产生变化 Pod IP 仅仅是集群内可见的虚 ...

  6. css网页布局设置总结

    目录 1 网页样式 1.1 引入方法 1.1.1内联样式 1.1.2内部样式表 1.1.3链接外部样式 1.1.4导入外部样式 1.2 基础语法 1.3 选择器的分类 1.3.1标记选择器 1.3.2 ...

  7. cesium加载倾斜摄影,添加billboard并注册点击事件

    完整示例代码如下: <!DOCTYPE html> <html lang="en"> <head> <meta charset=" ...

  8. pycharm全局搜索

    方法有:1.使用[Ctrl+N]快捷键按文件名搜索py文件: 2.使用[Ctrl+shift+N]快捷键按文件名搜索所有类型的文件: 3.使用[ctrl+shift+f]快捷全局字符串搜索: 3.使用 ...

  9. 如何通过 C#/VB.NET 将 PDF 转为 Word

    众所周知,PDF 文档支持特长文件,集成度和安全可靠性都较高,可有效防止他人对 PDF 内容进行更改,所以在工作中深受大家喜爱.但是在工作中,我们不可避免的会对 PDF 文档进行修改或再编辑,这时我们 ...

  10. 简单体验一个高性能,简单,轻量的ORM库- Dapper (无依赖其它库,非常方便高效)

    步骤1)引入该ORM库. 使用Nuget搜索"Dapper"安装或者直接从github上下载源码  (https://github.com/StackExchange/Dapper ...