解剖SQLSERVER 第一篇 數據庫恢復軟件商的黑幕(有刪減版)

解剖SQLSERVER 第一篇  數據庫恢復軟件商的黑幕(有刪減版)

這一系列,咱們一塊兒來解剖SQLSERVERhtml

在系列的第一篇文章裏本人可能會得罪某些人,可是做爲一位SQLSERVER MVP,在個人MVP任期內但願能夠對你們做出一些貢獻mysql

在第一篇裏面涉及到某些內容可能不會以詳細的方式給出截圖而且和你們講解,畢竟第一篇的篇幅比較長,但願你們見諒。。git

 

在第一篇文章開始以前,先說三個題外話github

 

第一個題外話  更新丟失sql

首先要作的事情是,跟你們道歉數據庫

在以前《SQLSERVER走起》的微信公衆賬號裏推送了一篇文章,題目是《RDS-SQLSERVER的READ COMMITTED與READ_COMMITTED_SNAPSHOT的區別及各自優缺點緩存

因爲當時沒有仔細看,就給你們推送了,文章裏面的建議確實是誤導了你們服務器

文章裏面這樣說道微信

舉個例子描述這個場景:session

T1事務發起一個修改,讀取原庫存是10,需求修改庫存減1,原庫存應該變成9,由於是READ_COMMITTED_SNAPSHOT隔離級別,因此數據庫會在tempdb裏生成一個快照,可是事務未提交,在這時發起了第二事務T2,也來修改庫存,由於看到事務T1未提交,因此他不能獲取未提交事務修改的值9(若是獲取9就是髒讀了),而是他獲取的是最後提交版本的庫存爲10,而正巧T2未提交前,T1先提交了,實際庫存應該變9而不是10,但T2事務獲取庫存值是10,假設T2的需求是減庫2,那麼最後T2提交後,會覆蓋T1事務所作的修改,庫存變成了8(咱們實際指望的是10-1-2=7),這樣就形成了邏輯混亂

實際上,這種狀況不是邏輯混亂,這種狀況是屬於 「更新丟失」,你們隨便拿起一本SQLSERVER教科書,裏面都會有說到更新丟失這種現象

 

文章裏面說的解決方案基本上是錯誤的

錯誤一:提升事務隔離級別將會形成更加多的死鎖

錯誤二:沒有對「更新丟失」進行錯誤處理

個人好朋友高繼偉(博客園裏的shanks_gao)跟這個阿里雲SQLSERVER經理說過這個問題,可是最後他仍是沒有改過來

 

解決方案有兩種,都是在默認隔離級別 READ COMMITTED下,不須要修改默認隔離級別

第一種:使用try catch捕獲更新丟失

--示例
CREATE TABLE kucun(id INT PRIMARY KEY,qty INT,product NVARCHAR(20))
--插入一些測試數據 SELECT * FROM dbo.kucun ----------------------------------------------- --session 1 BEGIN TRAN DECLARE @qty INT SELECT @qty=qty FROM kucun WITH(UPDLOCK) WHERE [product]='牙膏' SELECT @qty UPDATE kucun SET qty=@qty-1 WHERE [product]='牙膏' COMMIT TRAN ------------------------------------------ --session 2 BEGIN TRAN DECLARE @qty INT SELECT @qty=qty FROM kucun WITH(UPDLOCK) WHERE [product]='牙膏' --阻塞 SELECT @qty --session 1提交以後才能夠讀,可是後面的update語句不會執行,這個時候更新丟失,使用try catch機制來捕獲更新丟失 UPDATE kucun SET qty=@qty-1 WHERE [product]='牙膏' COMMIT TRAN

第二種:  若是使用的是SQLSERVER2008 可使用merge語句來執行這個原子操做

--session 1

BEGIN TRAN
MERGE [dbo].[kucun] AS TGT
    USING [dbo].[kucun] AS SRC
    ON  TGT.product = SRC.product AND TGT.id = SRC.id AND  TGT.product = '牙膏' 
    WHEN MATCHED THEN
        UPDATE SET
               TGT.qty = TGT.qty - 1;

COMMIT TRAN

SELECT * FROM [dbo].[kucun]  WHERE product = '牙膏' 
--session 2

--當session 1沒有提交的時候就會阻塞,當session 1提交的時候 session 2 也能成功update記錄 ,不會形成更新丟失

BEGIN TRAN
MERGE [dbo].[kucun] AS TGT
    USING [dbo].[kucun] AS SRC
    ON  TGT.product = SRC.product AND TGT.id = SRC.id AND  TGT.product = '牙膏' 
    WHEN MATCHED THEN
        UPDATE SET
               TGT.qty = TGT.qty - 1;

COMMIT TRAN

 

但願你們升級一下SQLSERVER,使用SQLSERVER2008提供的最新的merge語句,由於merge語句確實可以減小不少沒必要要的麻煩,並且性能也會有提高

 

看到這裏,可能你們對這種最基礎最基礎的知識不覺得然,可是你們試想一下,剛好這種最基礎的東西就有可能帶來致命的後果

例子:

好比你的銀行帳戶裏有100萬,你取出來了20萬,還剩下80萬

可是恰好遇到更新丟失,你的帳戶裏面可能已經取出了錢可是系統裏面沒有扣取你的錢又或者扣除多了 、扣除少了

後果可大可小

還有庫存系統,這裏就不說了

 

你們可能以爲「樺仔想借用這個例子,趁機貶低他人來擡高本身」  。實際上,我對於這個經理也是很理解,

當你管理成千成萬臺服務器的時候,你的腦子裏就會想到數據庫架構、集羣搭建、容災、業務連續性。。。 

這是數據庫架構師要作的事,很難會顧及到這些基礎的東西,我本身也是管理着公司不少的數據庫

可是做爲數據庫專家,你給客戶的建議應該要足夠專業吧???


第二個題外話  估計行數

 
某一天,羣裏面某位童鞋給我看了一個執行計劃,他說:「掃描運算符裏面的估計行數爲什麼偏差這麼大?」
 

腳本

USE [sss]
SELECT @@VERSION
--Microsoft SQL Server 2005 - 9.00.4035.00 (X64) 
--Nov 24 2008 16:17:31 
--Copyright (c) 1988-2005 Microsoft Corporation
--Developer Edition (64-bit) on Windows NT 6.1 (Build 7601: Service Pack 1)


CREATE TABLE teststat(id INT,name NVARCHAR(20))

--首先插入5條記錄
INSERT INTO teststat
SELECT 1 ,'nihao' UNION ALL
SELECT 2 ,'dajiahao' UNION ALL
SELECT 3 ,'nihao' UNION ALL
SELECT 4 ,'dajiahao' UNION ALL
SELECT 5 ,'nihao' 

--顯示實際執行計劃
SELECT * FROM teststat
--預估行數5條

--查看緩存的執行計劃
SELECT  [cacheobjtype] ,
        [objtype] ,
        [usecounts] ,
        [sql]
FROM    sys.[syscacheobjects]
WHERE   [sql] NOT LIKE '%cache%'
        AND [sql] LIKE '%INSERT INTO%'


--再插入100條記錄
INSERT INTO teststat(id,name)
SELECT 6,'dajiahao'
GO 100


--查詢緩存的執行計劃
SELECT  [cacheobjtype] ,
        [objtype] ,
        [usecounts] ,
        [sql]
FROM    sys.[syscacheobjects]
WHERE   [sql] NOT LIKE '%cache%'
        AND [sql] LIKE '%INSERT INTO%'

--顯示實際執行計劃
SELECT * FROM teststat
--預估行數仍是5條




--清空編譯計劃
DBCC FREEPROCCACHE
GO

--顯示實際執行計劃
SELECT * FROM teststat
--預估行數變成105條

第一次查看緩存的執行計劃

第二次查看緩存的執行計劃

 

清空plan cache以後

 

實際上,清空一下執行計劃緩存就能夠了,那位童鞋在第一次插入1條記錄,第二次插入4條記錄,
那麼估計行數是1,實際行數是5,他就認爲SQLSERVER估計得太不許確了,並且他認爲統計信息有問題,
實際上,這個時候根本沒有統計信息,又怎麼會跟統計信息扯上關係呢?
 

注意:DBCC FREEPROCCACHE是清空實例級別的計劃緩存,請不要隨意在生產環境下執行


第三個題外話  性能太差的SQLSERVER

某一天,開發又抱怨了:「SQLSERVER很慢,查詢要差很少9秒,這個問題怎麼完全解決!」

這個例子不是證實SQLSERVER多牛逼,只是爲了說明數據量大的時候,SQLSERVER也能夠應付

 

開發查詢的是一張xxclassifyxx表,表數據1.8億+

 

查詢語句以下

SELECT TOP 500  * FROM  DBO.xxCLASSIFYxx with (nolock) WHERE ID>5102332830 ORDER BY ID

 

表狀況:

彙集索引創建在ID列上

 

上面的查詢大概須要8秒

 

你們看到這個SQL語句可能一開始並無什麼頭緒,但你們會發現,彙集索引既然創建在ID這一列上,那麼ORDER BY ID是否是有點多餘呢???

 

語句修改以前

執行時間 8秒
(500 行受影響)
表 'xxClassifyxx'。掃描計數 113,邏輯讀取 619665 次,物理讀取 0 次,預讀 0 次,lob 邏輯讀取 0 次,lob 物理讀取 0 次,lob 預讀 0 次。

 

 

語句修改以後,只是去掉了ORDER BY ID

SELECT TOP 500  * FROM  DBO.xxCLASSIFYxx with (nolock) WHERE ID>5102332830 
(500 行受影響)
表 'xxClassifyxx'。掃描計數 1,邏輯讀取 32 次,物理讀取 0 次,預讀 0 次,lob 邏輯讀取 0 次,lob 物理讀取 0 次,lob 預讀 0 次。

 

修改以後所用時間

 

在這裏,加ORDER BY ID和不加ORDER BY ID對於SQLSERVER來看是不一樣的

 

注意:不要亂用(forceseek表提示),原本應該使用索引掃描的讓SQLSERVER使用索引查找,使用(forceseek表提示)以前必定要先測試一下

正題

 

使用Visual Studio查看數據庫文件數據
 
對於須要常常和數據打交道的那些數據恢復人可能會喜歡使用winhex這款hex編輯器軟件來查看文件數據
我最喜歡的功能是數據着色,根據SQLSERVER數據頁面的大小 8192字節,你能夠輸入8192字節爲
頁面大小,而後 winhex會以8192的塊來交替着色
指定8192字節爲一個頁面
winhex就會8192字節的塊大小來交替着色,好比單數頁面以白色爲底色,雙數頁面以灰色爲底色
 
輸入偏移值來定位某行數據
 
我相信對於winhex這個軟件比我還要精通的人有不少,小弟就不獻醜了,對這個軟件不做介紹
 
 
實際上也可使用Visual Studio來查看mdf文件的數據,把Visual Studio當作是winhex的精簡版
其實編輯文件用其餘的hex編輯器也是能夠的,而不侷限於winhex和Visual Studio ,例如Editplus 、NotePad++
在開始以前,你們能夠看一下這篇文章,張充計算頁面偏移位置的方式《 SQL Server 簡單模式下,誤刪除堆表記錄如何恢復(繞過頁眉校驗)
 
張充給出的頁面偏移計算公式是:邏輯頁號89 *8192 就是頁面89 的偏移位置

按照公式 邏輯頁面號  * 8192 來算出偏移位置 

這個計算公式沒有錯,只是,若是你真的要靠這個公式來找數據頁面,不少時候會找不到

例如沒有考慮到邏輯碎片的問題,並且咱們平時搜索數據的時候通常都不會使用這個公式

 

還有你們在研究的時候不要將DBCC PAGE的輸出中左邊的偏移值和winhex中左邊的偏移值對應起來

DBCC PAGE

WINHEX

 

 

測試腳本

View Code

 

咱們看下怎麼用VS查看數據文件內容

咱們使數據庫脫機,而後把mdf文件拖入VS

 
而後使數據庫聯機,這時候能夠繼續操做數據庫,可是VS跟winhex都不會自動刷新你對數據庫所作的修改
 
 
在這裏VS固然沒有winhex強大,winhex能夠搜索文本字符不管是unicode仍是ASCII
 
 
另外一個也是至關強大的hex編輯器

 

 
只是使用VisualStudio可能會方便你們一邊看代碼一邊研究文件結構
 
 
 
還有說一下,不要隨意用VS和winhex修改數據,不然你的數據庫有90%機率起不來喔

用winhex修改數據,選中要修改的數據的字節區域,而後右鍵—》edit-》你能夠選擇剪切、添加字節、填充零等等

用Visual Studio修改數據更簡單,選中要修改的字節區域,而後直接輸入16進制數就能夠了

 

在修改數據以前,把頁面校驗設置爲NONE

ALTER DATABASE [sss] SET PAGE_VERIFY NONE

若是你修改數據頁頭的話,在修改完畢以後數據庫還能夠聯機,可是你修改數據行的話,問題就嚴重了

下面是本人玩壞了的數據庫的報錯信息彙總
824錯誤
消息 824,級別 24,狀態 2,第 1 行
SQL Server 檢測到基於一致性的邏輯 I/O 錯誤 校驗和不正確(應爲: 0xb70c8233,但實際爲: 0xb7438233)。在文件 'E:\DataBase\sss.mdf' 中、偏移量爲 0x0000000009a000 的位置對數據庫 ID 8 中的頁 (1:77) 執行 讀取 期間,發生了該錯誤。SQL Server 錯誤日誌或系統事件日誌中的其餘消息可能提供了更詳細信息。這是一個威脅數據庫完整性的嚴重錯誤條件,必須當即糾正。請執行完整的數據庫一致性檢查(DBCC CHECKDB)。此錯誤能夠由許多因素致使;有關詳細信息,請參閱 SQL Server 聯機叢書。
消息 5028,級別 16,狀態 4,第 1 行 系統沒法激活足夠的數據庫來重建日誌。 sss的 DBCC 結果。 CHECKDB 在數據庫 'sss' 中發現 0 個分配錯誤和 0 個一致性錯誤。 消息 7909,級別 20,狀態 1,第 1 行 緊急模式修復失敗。您必須從備份中還原。

消息 601,級別 12,狀態 3,第 1 行 因爲數據移動,沒法繼續以 NOLOCK 方式掃描。

消息 926,級別 14,狀態 1,第 1 行 沒法打開數據庫 'sss'。恢復操做已將該數據庫標記爲 SUSPECT。有關詳細信息,請參閱 SQL Server 錯誤日誌。 消息 5069,級別 16,狀態 1,第 1 行 ALTER DATABASE 語句失敗。 消息 5125,級別 24,狀態 2,第 1 行 文件 'E:\DataBase\sss.mdf' 彷佛已被操做系統截斷。其大小應爲 3072 KB,但實際大小爲 3064 KB。 消息 3414,級別 21,狀態 1,第 1 行 恢復期間出
錯,致使數據庫 'sss' (數據庫 ID 8)沒法從新啓動。請診斷並糾正這些恢復錯誤,或者從已知的正確備份中還原。若是沒法更正錯誤,或者爲意外錯誤,請與技術支持人員聯繫。

 


分享某位牛人的代碼

國外某位牛人開發了一個軟件,這個軟件可以讀取SQLSERVER的mdf文件,並且這個軟件支持大部分的SQLSERVER數據存儲新格式
最重要的是,這個軟件是開放源代碼的
 
這個軟件是一個winform程序
 
他裏面帶了四個測試數據庫 ,支持SQL2005,SQL2008,SQL2008 R2,SQL2012
 
咱們運行一下這個軟件,OrcaMDF.OMS纔是winform程序,其餘項目只是程序集
 
咱們打開AWLT2012.mdf
 

不管系統表、DMV、用戶表、系統存儲過程、系統視圖均可以讀取出來

 

 

附加到SQLSERVER以後,查看數據庫屬性,做者使用的是微軟的標準示例數據庫adventureworkoltp來作的測試

這幾個庫的版本號是611,655,661,706

 

咱們能夠本身新建一個數據庫,而後測試一下

 

牛人的博客地址:http://improve.dk/

項目代碼已經放上去GITHUB:https://github.com/improvedk/OrcaMDF


分享SQLSERVER技術內幕系列圖書筆記

本人把一些SQLSERVER技術內幕讀書筆記分享出來,其實也不算是分享,由於這些筆記一直躺在個人博客裏

你們能夠對我作的筆記進行搜索,技術內幕系列圖書最大的一個特徵是 :Microsoft Press  權威性不可忽視

筆記地址

Microsoft SQL Server 6.5 技術內幕 筆記

Microsoft SQL Server 2005技術內幕:T-SQL查詢筆記

Microsoft SQL Server 2005技術內幕:存儲引擎筆記

Microsoft SQL Server 2005技術內幕 查詢、調整和優化筆記

Microsoft SQL Server 2005技術內幕: T-SQ程序設計 筆記

深刻解析SQL Server 2008 筆記

Microsoft SQL Server 2008技術內幕:T-SQL查詢 筆記

MICROSOFT SQL SERVER 2008技術內幕:T-SQL語言基礎 筆記

精通SQL Server2008程序設計 筆記

 

 

你們平時遇到的不少問題,這些書本里面基本都有解決方案,除非是特別新的技術
對於一些教條之類的方法、技巧、規定,書本里面都給出了爲什麼這麼作,裏面箇中的原理
 
 
 
先說一下《SQLSERVER6.5 技術內幕》
SQL6.5技術內幕裏面介紹了:SQLSERVER開發團隊剛開始開發SQLSERVER的時候採用單進程多線程的方式
而沒有使用ORACLE的多進程的方式,書本里面解釋了緣由
執行計劃修剪,buffer pool污染(mysql也有污染現象好像從5.5版本解決了),原行更新,原頁更新
數據頁面在那個年代仍是 2KB而不是如今的 8KB
某些存儲過程還保留着SQL6.5的代碼
EXEC sys.[sp_helpconstraint] @objname = N'[dbo].[nums]', -- nvarchar(776)
    @nomsg = '' -- varchar(5)


select object_id, type, name,[parent_object_id] from sys.objects where parent_object_id = OBJECT_ID('customer')
and type in ('C ','PK','UQ','F ', 'D ') -- ONLY 6.5 sysconstraints objects

 

若是你們有興趣的話能夠下載SQL6.5下來玩一下,若是你們能下載下來並能安裝和運行的話 ,安裝包只有100MB不到
 

 

《SQLSERVER2005存儲引擎》

SQLSERVER2005存儲引擎裏面把cachestore翻譯爲 存儲倉庫

還有SQLOS

SQLSERVER的工做線程是映射到Windows的線程池,SQLSERVER每條工做線程內存的分配都是由Windows來分配

SQL2005引入SQLOS,開始由SQLSERVER本身來調度線程,而先前是由Windows來調度

 

《SQLSERVER2005: T-SQ程序設計》

人們老是說遊標性能很差,這本書裏面解釋了遊標實際上也有他的優點的地方

 

《SQLSERVER2008 TSQL查詢》

髒讀、幻讀、重複讀解釋得特別清楚,對髒讀、幻讀、重複讀搞不清楚的童鞋能夠看一下
某些人會把重複讀理解成幻讀《 Transaction And Lock--READ COMMITTED隔離級別下的"髒讀"
分區表查詢的內部原理
內部碎片和外部碎片

 

《深刻解析SQLSERVER2008 》

DBCC的工做原理解釋得很清楚,大部份內容跟《SQLSERVER2005存儲引擎》有重疊

在最後一章DBCC 揭祕,譯者把鬼影記錄翻譯爲備份記錄,搞到一頭霧水

 

一邊看書,一邊思考

例如這篇文章《大表分批刪除腳本》 做者爲何要寫 DELETE TOP (5000) 呢? 有多是5000行鎖升級到表鎖的緣由

 

還有這一篇文章《恢復SQLSERVER被誤刪除的數據

你們看完書本以後,看一下存儲過程的代碼,本身是否理解裏面代碼的意思


結尾

SQLSERVER的水真的很深,我本身也沒有辦法精通,就例如 :一個開窗函數就能夠寫一本219頁的書
《T-SQL性能調優祕笈——基於SQL Server 2012窗口函數》這本書的做者是《SQL2005技術內幕 TSQL查詢》的做者之一:Itzik Ben-Gan   
 
 
我把這些筆記放上博客園目的只有一個「爲你們提供儘量多的筆記,讓你們儘快學會SQLSERVER」
做爲一位SQLSERVER MVP,我以爲本身已經盡了SQL Server MVP的義務,幫助你們迅速成長,成爲數據庫大牛
我高興的是,還有不少使用SQL Server的大牛活躍在博客園裏
這些SQLSERVER高手們還在不斷的寫文章,研究SQLSERVER,並且dudu園主也是竭盡全力的將SQLSERVER方面的文章推上編輯推薦
 

 

解剖SQL Server系列目錄

解剖SQLSERVER 第二篇  對數據頁面頭進行逆向(譯)

解剖SQLSERVER 第三篇  數據類型的實現(譯)

解剖SQLSERVER 第四篇  OrcaMDF裏對dates類型數據的解析(譯)

解剖SQLSERVER 第五篇  OrcaMDF裏讀取Bits類型數據(譯)

解剖SQLSERVER 第六篇  對OrcaMDF的系統測試裏避免regressions (譯)

解剖SQLSERVER 第七篇  OrcaMDF 特性概述(譯)

解剖SQLSERVER 第八篇  OrcaMDF 如今支持多數據文件的數據庫(譯)

解剖SQLSERVER 第九篇  OrcaMDF如今能經過系統DMVs顯示元數據(譯)

解剖SQLSERVER 第十篇     OrcaMDF Studio 發佈+ 特性重溫(譯)

解剖SQLSERVER 第十一篇    對SQLSERVER的多個版本進行自動化測試(譯)

解剖SQLSERVER 第十二篇   OrcaMDF 行壓縮支持(譯)

解剖SQLSERVER 第十三篇    Integers在行壓縮和頁壓縮裏的存儲格式揭祕(譯)

解剖SQLSERVER 第十四篇    Vardecimals 存儲格式揭祕(譯)

解剖SQLSERVER 第十五篇  SQLSERVER存儲過程的源文本存放在哪裏?(譯)

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

解剖SQLSERVER 第十七篇 使用 OrcaMDF Corruptor 故意損壞數據庫(譯)

解剖SQLSERVER 完結篇 關於Internals Viewer源代碼

 

 

因爲本人精力有限並且E文水平不太好,翻譯過程可能有錯漏,望你們見諒

建議先看一下技術內幕的書,不然一頭栽進代碼你會理解不了

 

經過閱讀這些譯文你們可能會以爲SQLSERVER的新存儲格式比較複雜,要遇上SQLSERVER的步伐不太容易,

改天微軟推出一個SQLSERVER補丁包,並在補丁包裏面添加新的存儲格式你的軟件可能又要歇菜了~

而我寫這系列文章並非要與這些數據庫恢復軟件商做對,而是讓你們知道他們可以恢復哪些數據,有哪些數據超出了他們的數據恢復能力

 

Mark S. Rasmussen的PPT:

http://files.cnblogs.com/lyhabc/StoringCharacterDataOptimally%E6%95%B0%E6%8D%AE%E5%AD%98%E5%82%A8%E4%BC%98%E5%8C%96byorcaMDF%E5%A4%A7%E7%89%9BMarkS.Rasmussen.rar

相關文章
相關標籤/搜索