解剖SQLSERVER 第十六篇 OrcaMDF RawDatabase
解剖 SQLSERVER 第十六篇 OrcaMDF RawDatabase --MDF文件的瑞士军刀(译) http://improve.dk/orcamdf-rawdatabase-a-swiss-army-knife-for-mdf-files/ 当我最初开始开发OrcaMDF的时候我只有一个目标,比市面上大部分的书要获取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)数据库上运行
获取单个页面
很多时候,我们只需要解析单个页面
<span>//</span><span> Get page 197 in file 1</span> <span>var</span> db = <span>new</span> RawDatabase(<span>@"</span><span>C:\AWLT2008R2.mdf</span><span>"</span><span>); db.GetPage(</span><span>1</span>, <span>197</span>).Dump();
解析页头
现在我们获取到页面,我们如何把页头dump出来
<span>//</span><span> Get the header of page 197 in file 1</span> <span>var</span> db = <span>new</span> RawDatabase(<span>@"</span><span>C:\AWLT2008R2.mdf</span><span>"</span><span>); db.GetPage(</span><span>1</span>, <span>197</span>).Header.Dump();
解析行偏移阵列
就像页头那样,我们也可以把页尾的行偏移阵列条目dump出来
<span>//</span><span> Get the slot array entries of page 197 in file 1</span> <span>var</span> db = <span>new</span> RawDatabase(<span>@"</span><span>C:\AWLT2008R2.mdf</span><span>"</span><span>); db.GetPage(</span><span>1</span>, <span>197</span>).SlotArray.Dump();
解析数据记录
当获取到行偏移条目的原始数据,你通常想看一下数据行记录的内容。幸运的是,这也很容易做到
<span>//</span><span> Get all records on page 197 in file 1</span> <span>var</span> db = <span>new</span> RawDatabase(<span>@"</span><span>C:\AWLT2008R2.mdf</span><span>"</span><span>); db.GetPage(</span><span>1</span>, <span>197</span>).Records.Dump();
从记录中检索数据
一旦你得到记录,你现在可以利用FixedLengthData 或者 VariableLengthOffsetValues 属性
去获取原始的定长数据内容和变长数据内容。然而,你肯定只想获取到实际的已解析的数据值。
对于解析,OrcaMDF会帮你解析,你只需要为他提供schema.
<span>//</span><span> Read the record contents of the first record on page 197 of file 1</span> <span>var</span> db = <span>new</span> RawDatabase(<span>@"</span><span>C:\AWLT2008R2.mdf</span><span>"</span><span>); RawPrimaryRecord firstRecord </span>= (RawPrimaryRecord)db.GetPage(<span>1</span>, <span>197</span><span>).Records.First(); </span><span>var</span> values = RawColumnParser.Parse(firstRecord, <span>new</span><span> IRawType[] { RawType.Int(</span><span>"</span><span>AddressID</span><span>"</span><span>), RawType.NVarchar(</span><span>"</span><span>AddressLine1</span><span>"</span><span>), RawType.NVarchar(</span><span>"</span><span>AddressLine2</span><span>"</span><span>), RawType.NVarchar(</span><span>"</span><span>City</span><span>"</span><span>), RawType.NVarchar(</span><span>"</span><span>StateProvince</span><span>"</span><span>), RawType.NVarchar(</span><span>"</span><span>CountryRegion</span><span>"</span><span>), RawType.NVarchar(</span><span>"</span><span>PostalCode</span><span>"</span><span>), RawType.UniqueIdentifier(</span><span>"</span><span>rowguid</span><span>"</span><span>), RawType.DateTime(</span><span>"</span><span>ModifiedDate</span><span>"</span><span>) }); values.Dump();</span>
RawColumnParser.Parse方法做的事情是 跟他一个schema,他帮你自动将raw bytes转换为Dictionary
而value就是数据列的实际值,例如int,short,guid,string等等。让你的用户给定schema, OrcaMDF 可以跳过大量的依赖的元数据进行解析,因此可以忽略可能的元数据错误带来的数据读取失败。
由于页头已经给出了 NextPageID 和 PreviousPageID属性 ,这能够让软件简单的遍历链表中的所有页面,并解析这些页面里面的数据 --他基本上是根据给定的allocation unit来进行扫描
过滤页面
除非检索一个特定的页面,RawDatabase 也有一个页面属性能够枚举数据库中的所有页面。
使用这个属性,举个例子,获取数据库中所有的IAM页面的列表
<span>//</span><span> Get a list of all IAM pages in the database</span> <span>var</span> db = <span>new</span> RawDatabase(<span>@"</span><span>C:\AWLT2008R2.mdf</span><span>"</span><span>); db.Pages .Where(x </span>=> x.Header.Type ==<span> PageType.IAM) .Dump();</span>
并且由于这是使用LINQ技术,这很容易去设计你想要的属性。
举个例子,你可以获取所有的 index pages 和他们的 slot counts 就像这样:
<span>//</span><span> Get all index pages and their slot counts</span> <span>var</span> db = <span>new</span> RawDatabase(<span>@"</span><span>C:\AWLT2008R2.mdf</span><span>"</span><span>); db.Pages .Where(x </span>=> x.Header.Type ==<span> PageType.Index) .Select(x </span>=> <span>new</span><span> { x.PageID, x.Header.SlotCnt }).Dump();</span>
或者假设你想获得如下条件的页面
1、页面里面至少有一条记录
2、free space空间至少有7000 bytes
下面是page id, free count, record count 和 平均记录大小的输出
<span>var</span> db = <span>new</span> RawDatabase(<span>@"</span><span>C:\AWLT2008R2.mdf</span><span>"</span><span>); db.Pages .Where(x </span>=> x.Header.FreeCnt > <span>7000</span><span>) .Where(x </span>=> x.Header.SlotCnt >= <span>1</span><span>) .Where(x </span>=> x.Header.Type ==<span> PageType.Data) .Select(x </span>=> <span>new</span><span> { x.PageID, x.Header.FreeCnt, RecordCount </span>=<span> x.Records.Count(), RecordSize </span>= (<span>8096</span> - x.Header.FreeCnt) /<span> x.Records.Count() }).Dump();</span>
最后一个例子,,假设你只有一个MDF文件并且你已经忘记了有哪些对象存储在MDF文件里面。
不要紧,我们只需要查询系统表sysschobjs !sysschobjs 系统表包含了所有对象的数据
并且幸运的是,他的object ID 是 34。利用这些信息,我们可以把所有属于object ID 34的数据页面
过滤出来,并且从这些页面里读取记录并只需要解析这个表的前两列(你可以定义一个分部schema, 只要你在最后忽略列)
最后我们只需要把名称dump出来(当然我们可以把表里的所有列都查询出来,如果我们想的话)
<span>SELECT</span> <span>*</span> <span>FROM</span> sys.sysschobjs
<span>var</span> db = <span>new</span> RawDatabase(<span>@"</span><span>C:\AWLT2008R2.mdf</span><span>"</span><span>); </span><span>var</span> records =<span> db.Pages .Where(x </span>=> x.Header.ObjectID == <span>34</span> && x.Header.Type ==<span> PageType.Data) .SelectMany(x </span>=><span> x.Records); </span><span>var</span> rows = records.Select(x => RawColumnParser.Parse((RawPrimaryRecord)x, <span>new</span><span> IRawType[] { RawType.Int(</span><span>"</span><span>id</span><span>"</span><span>), RawType.NVarchar(</span><span>"</span><span>name</span><span>"</span><span>) })); rows.Select(x </span>=> x[<span>"</span><span>name</span><span>"</span>]).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 下发布
第十六篇完

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

匯入步驟如下:將 MDF 檔案複製到 SQL Server 的資料目錄(通常為 C:\Program Files\Microsoft SQL Server\MSSQL\DATA)。在 SQL Server Management Studio(SSMS)中,開啟資料庫並選擇「附加」。點選“新增”按鈕,選擇 MDF 檔案。確認資料庫名稱,點選確定按鈕即可。

對於 SQL Server 資料庫中已存在同名對象,需要採取下列步驟:確認物件類型(表格、檢視、預存程序)。如果物件為空,可使用 IF NOT EXISTS 跳過建立。如果物件有數據,使用不同名稱或修改結構。使用 DROP 刪除現有物件(謹慎操作,建議備份)。檢查架構更改,確保沒有引用刪除或重新命名的物件。

若要查看 SQL Server 連接埠號碼:開啟 SSMS,連線到伺服器。在物件資源管理器中找到伺服器名稱,右鍵單擊它,然後選擇“屬性”。在「連線」標籤中,查看「TCP 連接埠」欄位。

若誤刪 SQL Server 資料庫,可採取下列步驟還原:停止資料庫活動;備份日誌檔案;檢查資料庫日誌;復原選項:從備份還原;從交易日誌還原;使用 DBCC CHECKDB;使用第三方工具。請定期備份資料庫並啟用交易日誌以防止資料遺失。

SQL Server 資料庫檔案通常儲存在下列預設位置:Windows: C:\Program Files\Microsoft SQL Server\MSSQL\DATALinux: /var/opt/mssql/data可透過修改資料庫檔案路徑設定來自訂資料庫檔案位置。

當 SQL Server 服務無法啟動時,可採取下列步驟解決:檢查錯誤日誌以確定根本原因。確保服務帳戶具有啟動服務的權限。檢查依賴項服務是否正在執行。禁用防毒軟體。修復 SQL Server 安裝。如果修復不起作用,重新安裝 SQL Server。

問題發現這次使用的是SqlServer資料庫,之前並沒有使用過,但是問題不大,我按照需求文檔的步驟連接好SqlServer之後,啟動SpringBoot項目,發現了一個報錯,如下:剛開始我以為是SqlServer連接問題呢,於是便去查看資料庫,發現資料庫一切正常,我首先第一時間問了我的同事,他們是否有這樣的問題,發現他們並沒有,於是我便開始了我最拿手的環節,面向百度程式設計.開始解決具體報錯資訊是這樣,於是我便開始了百度報錯:ERRORc.a.d.p.DruidDataSource$CreateCo

如果 SQL Server 安裝失敗,可透過下列步驟清理:解除安裝 SQL Server刪除註冊表項刪除檔案和資料夾重新啟動計算機
