《Microsoft Sql server 2008 Internals》读书笔记--第十一章DBCC Internals(2)
《Microsoft Sql server 2008 Internals》读书笔记订阅地址:
http://www.cnblogs.com/downmoon/category/230397.html/rss
《Microsoft Sql server 2008 Internals》索引目录:
《Microsoft Sql server 2008 Internal》读书笔记--目录索引
上文简要介绍了获取一个持久视图、磁盘空间冲突和使用数据库快照的替代方案,本文将关注有效处理数据库的几个方法:事实生成、使用查询处理器、批处理、读取数据页到进程和并行机制。
一个数据库可以看作一个巨大的、用所有表指示后一节点到系统目录(catalogs),而所有的系统目录指示后一节点到存储在sys.sysallocunits中的最底层的分配元数据的互联结构。这些分配元数据依次在每一个数据库的第一个页(固定在页1:16)。再加上固定地址的分配位图,如Page Free Space(PFS),Global Allocation Map(GAM)页,完整的数据库可以被看作单个实体关系(entity-Relationship,即ER)图(diagram)。
基于此,你可以想像一个针对这些元数据的一致性检查算法,自页(1:16)和分配位图开始并逐渐地扩展到在对象和结构所处的数据库检查连接(linkage)。无论何时,某个页连接(linkage)被找到时,连接被跟踪以确信连接的是正确的页。无论何时,一个分配位图有一个被标记(作IAM页)的页,这个页被检查以确保它真的在IAM页中。这将是一个depth-first 算法。
考虑一个带有三条记录的数据页,每一个数据记录包含一个连接到两个存储在行外的8000字节的大对象(LOB),使用上面的算法,一致性检查页的序列操作如下:
1、从记录1提取包含第一个LOB列数据的页ID
2、读取页确信它上面有正确的LOB列数据
3、从记录1提取包含第二个LOB列数据的页ID
4、读取页确信它上面有正确的LOB列数据
5、如有必要,重复第1-4,直到整个结构被处理。
正如你看到的,这儿描述的算法是非常低效的。页需要时被随机读取。页可能被处理多次,页读取的自然随机性意味着I/O子系统不能被用作预读簇(read ahead)。从算法复杂性角度看,这个算法应该是O(n*n)。即n平方排序。这意味着算法将花费指数方式的时间来运行运算符增长的元素数量。在本例中,n是数据库的页数。
这不是SQL Server2008中DBCC CHECKED工作的方式,事实上,它是SQL Server2000的工作方式。一个O(n*n)算法在一个大数据库上运行是过度昂贵的。替代地,一个O(n*log(n))将提供一个近乎线性的缩放比例。
■事实生成
DBCC CHECKED从被以最可能的方式进行一致性检查的对象中读取所有页,按照分配顺序(即它们被存储在数据文件的顺序,而不是页连接和按基本的随机顺序读取它们。
当页被尽可能地按照严格的分配顺序读取时,没有方法验证所有的立刻要被处理的页之间的相关性,因此,DBCC CHECKED必须记住关于每个页以便在后续步骤中执行相关性检查,它通过生成一个页的信息来完成这个,也就是Fact(事实)。
接着前面的例子,作为处理数据页的部分,下列事实被生成:
◆第一条记录连接到一个LOB值的两个事实(每个事实对应一个LOB值),每个事实包含如下:
1、数据记录的页ID和Slot ID(即记录数)
2、LOB值(提取自存储在数据记录的文本根)应该被存储年处的页ID和Slot ID。
3、LOB值的文本时间戳(一个惟一的ID,被指派到LOB值)
4、组成页的对象ID,索引ID,分区ID,分配单元ID
◆第二条记录连接到一个LOB值的两个事实
◆第三条记录连接到一个LOB值的两个事实
这些事实被看作父文本事实。
当每一个包含实际LOB值的文本页被处理时,处理的部分是生成一个LOB值被遇到的事实。每个事实包含如下:
◆文本记录的页ID和Slot ID
◆LOB值的文本时间戳
◆TABLock选项被定义
◆组成页的对象ID,索引ID,分区ID,分配单元ID
这些事实被看作实际文本事实。
在后来的某个位置,事实被互相检查(即聚集Aggregation),只要有一个与每个LOB值匹配的父文本事实和实际文本事实,DBCC CHECKED辨别这个特殊的LOB值连接是否过期。
除了实际事实和父事实。还有一种类型的sibing(兄弟姐妹?这个词邀月真不知道什么意思。)事实。被用来检查索引B-Tree和描述(已经存在于索引B-Tree的每个级上的)连接列表。
对于数据库结构的不同部分,一致性检查算法采用多个事实类型和事实内容。但基本的算法是相同的。事实类型被用作:
◆采集关于对象、索引、分区、分配单元的分配统计
◆跟踪FileStream数据的事实
◆跟踪IAM表链的事实
◆跟踪针对一个特殊的GAM间隔的IAM页位图
◆跟踪数据库文件的事实
◆跟踪分区分配和所有者关系的事实
◆跟踪页分配和所有者关系的事实
◆跟踪B-Tree链接的事实
◆跟踪LOB值连接的事实
◆跟踪在Heaps中的正转发和的/已转发的记录
在生成和聚集之间,事实被存储在内存中的查询优化器用来人寿一个排序运算。有时排序折大小比查询处理器的可用内存要大得多,结果,排序"溢出"到磁盘中(其实是tempdb中),这样,在tempdb中生成物理读取写。因为每个事实本质上是一个表行,事实必定分为表列。每个Fact由5列组成:
1、ROWSET_COLUMN_FACT_KEY 事实描述的页的页ID,或FileStream文件的LSN
1、ROWSET_COLUMN_FACT_TYPE 事实类型
1、ROWSET_COLUMN_SLOT_ID 事实描述的记录SLOT ID
1、ROWSET_COLUMN_COMBINED_ID 组成页的对象,索引,分区,分配单元 IDs
1、ROWSET_COLUMN_FACT_BLOB 一个可存储任何额外数据的可变长度列
如果tempdb数据库没有足够的空间存储DBCC排序,DBCC CHECKED可能失败。此时会报8921错误!
■使用查询处理器
DBCC CHECKED充分利用发查询处理器,使得处理事实和并行一致性检查更加容易。
用于生成事实的算法如下:
1、发布一个查询(使用SQL Server可用的语法)到查询处理器,其中包含一个指向行集的指针和自定义聚集函数的名称
2、查询处理器查询行集得到一个行,本质上调用DBCC得到一个要处理的事实
3、DBCC交还单个事实作为一个行集的行(列结果如上所述)。 如果没有可用的事实,DBCC读取一个页,完整地处理它,生成所有必需的事实。这些事实被存储在thread-local内存(first-in-first-out,FIFO,即先进先出)等待队列。单个的事实被交还给查询处理器,一个事实被从thread-local序列(带查询处理器的子请求序列)的头部(Header)返回,直到没有可用的事实。然后另一个页被读取、处理并生成事实,再次填充事实序列。
4、查询处理器在排序内存中存储事实,和tempdb数据库中的一样。
一旦所有的事实被为将要被一致性检查的对象而生成,查询处理器在其上完成排序运算,然后调用DBCC提供的自定义的聚集函数。这些事实被按事实键存储,按所有类型以外的列分组,因此,聚集例程得到正确排序的事实,以允许连续的事实被容易地匹配。
接着上面的LOB连接例子,事实被按下列顺序传回到聚集例程:
1、针对某个LOB值的实际文本事实(在数据页的记录1中的真实LOB值1)
2、针对数据页的记录1中的LOB值1的父文本事实
3、针对某个LOB值的实际文本事实(在数据页的记录1中的真实LOB值2)
4、针对数据页的记录1中的LOB值2的父文本事实
如果事实不按这个顺序,匹配事实是不可能的。除非再次记住已经看到的顺序。
聚集算法运行如下:
1、查询处理器用一个简单事实调用DBCC自定义聚集函数。
2、事实被合并直到从查询处理器传来的某个事实不匹配已经被合并的事实。例如,前例中针对LOB值1的实际事实和父事实,下一个事实针对LOB值2,这是数据结构的不同部分。
3、一旦遇到不匹配的事实,事实的合并集被聚集以判定当前是否有错误。聚集意味着事实被检查正确的事实是否存在于它们所描述的某个数据结构片段中。例如LOB必须有一个实际事实(实际遇到的植)和一个父事实(连接到LOB值的某些索引和数据记录)。
4、如果当前发生错误,错误列表中会增加一个项,这个被生成的项使用包含在事实的聚集set中。示例错误是一个LOB值,没有一个数据或索引记录指向它。
5、事实被丢弃,而新的合并的事实集开始,自已触发的聚集的不匹配的事实。
6、DBCC代码发信号给查询处理器,将接受下一个将要合并的聚集的事实。
如下图所示:
这些算法在DBCC CHECKED内部执行一个查询时开始。DBCC运行的查询如下:
DECLARE @BlobEater Varbinary(8000)
Select @BlobEater =CheckIndex(ROWSET_COLUMN_FACT_BLOB)
FROM <memory address of fact rowset>
GROUP BY ROWSET_COLUMN_FACT_KEY
>>WITH ORDER BY
ROWSET_COLUMN_FACT_KEY,
ROWSET_COLUMN_SLOT_ID,
ROWSET_COLUMN_COMBINED_ID,
ROWSET_COLUMN_FACT_BLOB
OPTION(ORDER GROUP);
查询整合查询处理器和DBCC CHECKED代码,并执行事实生成,事实排序、事实存储、事实聚集算法。查询的这部分如下:
◆@BlobEater 这是一个仿制变量,惟一的作用是(仅仅出于语法需求)使用CHECKINDEX函数的输出。
◆CheckIndex(ROWSET_COLUMN_FACT_BLOB)
这是一个DBCC CHECKED内部的自定义聚集函数,查询处理器调用它存储和分组事实,作为整个事实聚集算法的一部分。
◆<memory address of fact rowset>
这是DBCC CHECKED提供给查询处理器的OLEDB行集的内存地址。查询处理器逐行查询该行集,(包含已生成的事实)作为整个事实生成算法的一部分。
◆ROWSET_COLUMN_FACT_KEY
this triggers the aggregation in the query processor(这句不知如何翻译?)
◆>>WITH ORDER BY <column list>
这仅仅是一个提供已经排序的聚集到聚集阶(Aggregation step)的内部的语法。正如前所解释,DBCC CHECKED聚集代码基于假定:来自查询处理器的事实的聚集流的排序是强制的(即它需要在每个组内的键的排序是查询中的四个键的顺序)。
◆OPTION(ORDER GROUP)
这是一个强制流聚集的查询优化器提示。它强制查询优化器在分组列上排序而避免哈希聚集。
这个机制被用于分配一致性检查和 DBCC CHECKED的per-table一致性检查阶段。该机制也用于DBCC CHECKALLOC,DBCC CHECKTABLE,DBCC CHECKGROUP命令。
如果内部查询因为内存存储而失败,会报一个8902错误。如果内部查询因为其他原因而失败,报通用的错误8975。无论哪种情况,DBCC CHECKED终止。
下文将关注批处理、读取数据页到进程和并行机制。