解剖SQLSERVER 第十六篇 OrcaMDF RawDatabase --MDF文件的瑞士军刀(译)

本文涉及的产品
RDS SQL Server Serverless,2-4RCU 50GB 3个月
推荐场景:
云数据库 RDS SQL Server,基础系列 2核4GB
云解析 DNS,旗舰版 1个月
简介: 原文:解剖SQLSERVER 第十六篇 OrcaMDF RawDatabase --MDF文件的瑞士军刀(译)解剖SQLSERVER 第十六篇 OrcaMDF RawDatabase --MDF文件的瑞士军刀(译) http://improve.dk/orcamdf-rawdatabase-a-swiss-army-knife-for-mdf-files/ 当我最初开始开发OrcaMDF的时候我只有一个目标,比市面上大部分的书要获取MDF文件内部的更深层次的知识 随着时间的推移,OrcaMDF确实做到了。
原文: 解剖SQLSERVER 第十六篇 OrcaMDF RawDatabase --MDF文件的瑞士军刀(译)

解剖SQLSERVER 第十六篇 OrcaMDF RawDatabase --MDF文件的瑞士军刀(译)

http://improve.dk/orcamdf-rawdatabase-a-swiss-army-knife-for-mdf-files/

当我最初开始开发OrcaMDF的时候我只有一个目标,比市面上大部分的书要获取MDF文件内部的更深层次的知识

随着时间的推移,OrcaMDF确实做到了。在我当初没有计划的时候,OrcaMDF 已经可以解析系统表,元数据,甚至DMVs。我还做了一个简单UI,让OrcaMDF 更加容易使用。

这很好,但是带来的代价是软件非常复杂。为了自动解析元数据 例如schemas, partitions, allocation units 还有其他的东西,更不要提对于堆表和索引的细节的抽象层了,抽象层需要很多代码并且需要更多的数据库了解。鉴于不同SQLSERVER版本之间元数据的改变,OrcaMDF 目前仅支持SQL Server 2008 R2。然而,数据结构是相对稳定的,元数据的存储方式只有一点不同,使用DMVs暴露数据等等。要让OrcaMDF 正常运行,需要元数据是完好无损的,这就导致当SQLSERVER损坏的时候OrcaMDF 也是一样的。遇到损坏的boot page吗?无论SQLSERVER还是 OrcaMDF 都不能解析数据库

 

向RawDatabase问好
我在憧憬OrcaMDF 的未来 和如何使用他才是最有用的。我能够不断增加新的特性进去以使SQLSERVER支持什么功能他也支持,最终使得他能100%解析MDF文件。但是意义何在?当然,这是一个很好的学习机会,不过重点是,你使用软件读取数据,SQLSERVER能比你做得更好。所以,该如何选择?

RawDatabase, 参照Database 类,他不会尝试解析任何东西除非你让他去解析。
他不会自动解析schemas。他不知道系统表。他不知道DMVs。然而他知道SQLSERVER数据结构和给他一个接口他可以直接读取MDF文件。

让RawDatabase 只解析数据结构意味着他可以跳过损坏的系统表或者损坏的数据

 

 

例子
这个工具还在开发的早起,不过让我展示一下使用RawDatabase能够做什么东西。
当我运行LINQPad上的代码,他很容易的显示出结果,结果只是标准的.NET 对象。
所有的例子都在AdventureWorks 2008R2 LT (Light Weight)数据库上运行

 


获取单个页面
很多时候,我们只需要解析单个页面

// Get page 197 in file 1
var db = new RawDatabase(@"C:\AWLT2008R2.mdf");
db.GetPage(1, 197).Dump();

 

解析页头
现在我们获取到页面,我们如何把页头dump出来

// Get the header of page 197 in file 1
var db = new RawDatabase(@"C:\AWLT2008R2.mdf");
db.GetPage(1, 197).Header.Dump();

 

 

解析行偏移阵列
就像页头那样,我们也可以把页尾的行偏移阵列条目dump出来

// Get the slot array entries of page 197 in file 1
var db = new RawDatabase(@"C:\AWLT2008R2.mdf");
db.GetPage(1, 197).SlotArray.Dump();

 

解析数据记录
当获取到行偏移条目的原始数据,你通常想看一下数据行记录的内容。幸运的是,这也很容易做到

// Get all records on page 197 in file 1
var db = new RawDatabase(@"C:\AWLT2008R2.mdf");
db.GetPage(1, 197).Records.Dump();

 

 

从记录中检索数据
一旦你得到记录,你现在可以利用FixedLengthData 或者 VariableLengthOffsetValues 属性
去获取原始的定长数据内容和变长数据内容。然而,你肯定只想获取到实际的已解析的数据值。
对于解析,OrcaMDF会帮你解析,你只需要为他提供schema.

// Read the record contents of the first record on page 197 of file 1
var db = new RawDatabase(@"C:\AWLT2008R2.mdf");
RawPrimaryRecord firstRecord = (RawPrimaryRecord)db.GetPage(1, 197).Records.First();

var values = RawColumnParser.Parse(firstRecord, new IRawType[] {
    RawType.Int("AddressID"),
    RawType.NVarchar("AddressLine1"),
    RawType.NVarchar("AddressLine2"),
    RawType.NVarchar("City"),
    RawType.NVarchar("StateProvince"),
    RawType.NVarchar("CountryRegion"),
    RawType.NVarchar("PostalCode"),
    RawType.UniqueIdentifier("rowguid"),
    RawType.DateTime("ModifiedDate")
});
    
values.Dump();

 

RawColumnParser.Parse方法做的事情是 跟他一个schema,他帮你自动将raw bytes转换为Dictionary<string, object>,key就是从schema 那里获取到的列名,

而value就是数据列的实际值,例如int,short,guid,string等等。让你的用户给定schema, OrcaMDF 可以跳过大量的依赖的元数据进行解析,因此可以忽略可能的元数据错误带来的数据读取失败。

由于页头已经给出了 NextPageID 和 PreviousPageID属性 ,这能够让软件简单的遍历链表中的所有页面,并解析这些页面里面的数据 --他基本上是根据给定的allocation unit来进行扫描

 

过滤页面
除非检索一个特定的页面,RawDatabase 也有一个页面属性能够枚举数据库中的所有页面。
使用这个属性,举个例子,获取数据库中所有的IAM页面的列表

// Get a list of all IAM pages in the database
var db = new RawDatabase(@"C:\AWLT2008R2.mdf");
db.Pages
    .Where(x => x.Header.Type == PageType.IAM)
    .Dump();


并且由于这是使用LINQ技术,这很容易去设计你想要的属性。
举个例子,你可以获取所有的 index pages 和他们的 slot counts 就像这样:

// Get all index pages and their slot counts
var db = new RawDatabase(@"C:\AWLT2008R2.mdf");
db.Pages
    .Where(x => x.Header.Type == PageType.Index)
    .Select(x => new {
        x.PageID,
        x.Header.SlotCnt
    }).Dump();

 

或者假设你想获得如下条件的页面
1、页面里面至少有一条记录
2、free space空间至少有7000 bytes

下面是page id, free count, record count 和 平均记录大小的输出

var db = new RawDatabase(@"C:\AWLT2008R2.mdf");
db.Pages
    .Where(x => x.Header.FreeCnt > 7000)
    .Where(x => x.Header.SlotCnt >= 1)
    .Where(x => x.Header.Type == PageType.Data)
    .Select(x => new {
        x.PageID,
        x.Header.FreeCnt,
        RecordCount = x.Records.Count(),
        RecordSize = (8096 - x.Header.FreeCnt) / x.Records.Count()
    }).Dump();

 

 

最后一个例子,,假设你只有一个MDF文件并且你已经忘记了有哪些对象存储在MDF文件里面。
不要紧,我们只需要查询系统表sysschobjs !sysschobjs 系统表包含了所有对象的数据
并且幸运的是,他的object ID 是 34。利用这些信息,我们可以把所有属于object ID 34的数据页面
过滤出来,并且从这些页面里读取记录并只需要解析这个表的前两列(你可以定义一个分部schema, 只要你在最后忽略列)

最后我们只需要把名称dump出来(当然我们可以把表里的所有列都查询出来,如果我们想的话)

SELECT * FROM sys.sysschobjs 

var db = new RawDatabase(@"C:\AWLT2008R2.mdf");

var records = db.Pages
    .Where(x => x.Header.ObjectID == 34 && x.Header.Type == PageType.Data)
    .SelectMany(x => x.Records);
    
var rows = records.Select(x => RawColumnParser.Parse((RawPrimaryRecord)x, new IRawType[] {
    RawType.Int("id"),
    RawType.NVarchar("name")
}));

rows.Select(x => x["name"]).Dump();

 


兼容性
可以看到 RawDatabase并不依赖于元数据,这很容易兼容多个版本的SQLSERVER。
因此,我很高兴的宣布:RawDatabase 完全兼容SQL Server 2005, 2008, 2008R2 , 2012.
这也有可能兼容2014,不过我还未进行测试。说到测试,所有的单元测试都是自动运行的
在测试期间使用AdventureWorksLT for 2005, 2008, 2008R2 and 2012 。
现在有一些测试demo来让OrcaMDF RawDatabase去解析AdventureWorks LT 数据库里面每个表的每条记录

 

 

数据损坏
其中一个有趣的使用RawDatabase 的方法是用来附加损坏的数据库。你可以检索特定object id的所有页面然后硬解析每个页面
无论他们是否是可读的。如果元数据损坏,你可以忽略他,你手工提供schema (输入表的每个列的列名)并且只需要沿着页面链表
或者解析IAM页面去读取堆表里面的数据。接下来的几个星期我将会 写一些关于OrcaMDF RawDatabase 的使用场景的博客,其中包括数据损坏

 

源代码和反馈
我非常兴奋因为最新的RawDatabase 已经添加到OrcaMDF 里面并且我希望不单只只有我一个见证他的威力。
如果你也想试一试,或者有任何想法,建议或者其他反馈,我都很乐意接受。

如果你想试用,在GitHub上签出OrcaMDF项目。一旦这个工具做得比较完美了,我会把他放上去NuGet 。
就好像OrcaMDF一样,在GPL v3 licensed 下发布

 

第十六篇完

相关实践学习
使用SQL语句管理索引
本次实验主要介绍如何在RDS-SQLServer数据库中,使用SQL语句管理索引。
SQL Server on Linux入门教程
SQL Server数据库一直只提供Windows下的版本。2016年微软宣布推出可运行在Linux系统下的SQL Server数据库,该版本目前还是早期预览版本。本课程主要介绍SQLServer On Linux的基本知识。 相关的阿里云产品:云数据库RDS&nbsp;SQL Server版 RDS SQL Server不仅拥有高可用架构和任意时间点的数据恢复功能,强力支撑各种企业应用,同时也包含了微软的License费用,减少额外支出。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/sqlserver
目录
相关文章
|
3月前
|
SQL 数据库
SQL Server 附加数据库,报只读文件,无权修改其中某些文件
SQL Server 附加数据库,报只读文件,无权修改其中某些文件
106 0
|
6月前
|
SQL 存储 小程序
数据库数据恢复—Sql Server数据库文件丢失的数据恢复案例
数据库数据恢复环境: 5块硬盘组建一组RAID5阵列,划分LUN供windows系统服务器使用。windows系统服务器内运行了Sql Server数据库,存储空间在操作系统层面划分了三个逻辑分区。 数据库故障: 数据库文件丢失,主要涉及3个数据库,数千张表。数据库文件丢失原因未知,不能确定丢失的数据库文件的存放位置。数据库文件丢失后,服务器仍处于开机状态,所幸未写入大量数据。
数据库数据恢复—Sql Server数据库文件丢失的数据恢复案例
|
SQL 数据挖掘 数据库
数据库数据恢复-SQL SERVER数据库文件误还原备份的数据恢复方案
SQL SERVER数据库故障类型: 1、SQL SERVER数据库文件被删除。 2、SQL SERVER数据库所在分区格式化。 3、SQL SERVER数据库文件大小变为“0”。 4、使用备份还原数据库时覆盖原数据库。
|
数据库 C#
C#,.net,winform导入Excel功能以及下载Excel文件到本地,并使用SqlBulkCopy把DataTable类型的数据写入到sqlserver数据库中
C#,.net,winform导入Excel功能以及下载Excel文件到本地,并使用SqlBulkCopy把DataTable类型的数据写入到sqlserver数据库中
418 0
|
机器学习/深度学习 SQL
SQL Server提示:安装程序无法与下载服务器联系。请提供 Microsoft机器学习服务器安装文件的位置。。。。
今天在安装SQL Server的过程中,出现问题:安装程序无法与下载服务器联系。请提供 Microsoft机器学习服务器安装文件的位,然后单击“下一步”,可从以下位置下载安装文件。
SQL Server提示:安装程序无法与下载服务器联系。请提供 Microsoft机器学习服务器安装文件的位置。。。。
|
数据库 数据库管理
sqlite数据库文件导入到sqlserver
sqlite数据库文件导入到sqlserver
384 0
|
存储 SQL 数据库
SQLServer数据库文件相关知识笔记
数据库文件是SQLServer数据库的物理体现,和计算机的普通文件一样存储在计算机的磁盘空间当中。作为数据库记录和日志等其他信息的存储载体。
SQLServer数据库文件相关知识笔记
|
2月前
|
SQL 数据库
数据库数据恢复—SQL Server数据库报错“错误823”的数据恢复案例
SQL Server附加数据库出现错误823,附加数据库失败。数据库没有备份,无法通过备份恢复数据库。 SQL Server数据库出现823错误的可能原因有:数据库物理页面损坏、数据库物理页面校验值损坏导致无法识别该页面、断电或者文件系统问题导致页面丢失。
100 12
数据库数据恢复—SQL Server数据库报错“错误823”的数据恢复案例
|
8天前
|
存储 数据挖掘 数据库
数据库数据恢复—SQLserver数据库ndf文件大小变为0KB的数据恢复案例
一个运行在存储上的SQLServer数据库,有1000多个文件,大小几十TB。数据库每10天生成一个NDF文件,每个NDF几百GB大小。数据库包含两个LDF文件。 存储损坏,数据库不可用。管理员试图恢复数据库,发现有数个ndf文件大小变为0KB。 虽然NDF文件大小变为0KB,但是NDF文件在磁盘上还可能存在。可以尝试通过扫描&拼接数据库碎片来恢复NDF文件,然后修复数据库。
|
2月前
|
SQL 关系型数据库 MySQL
创建包含MySQL和SQLServer数据库所有字段类型的表的方法
创建一个既包含MySQL又包含SQL Server所有字段类型的表是一个复杂的任务,需要仔细地比较和转换数据类型。通过上述方法,可以在两个数据库系统之间建立起相互兼容的数据结构,为数据迁移和同步提供便利。这一过程不仅要考虑数据类型的直接对应,还要注意特定数据类型在不同系统中的表现差异,确保数据的一致性和完整性。
30 4

热门文章

最新文章