前言
本篇主要是上一篇文章的補充篇,上一篇我們介紹了SQL Server服務啟動過程所遇到的一些問題和解決方法,可點擊查看,我們此篇主要介紹的是SQL Server啟動過程中關于用戶數據庫加載的流程,并且根據加載過程中所遇到的一系列問題提供解決方案。
其實SQL Server作為微軟的一款優秀RDBMS,它啟動的過程中,本身所帶的那些系統庫發生問題的情況相對還是很少的,我們在平常使用中,出問題的大部分集中于我們自己建立的用戶數據庫。
而且,相對于側重面而言,其實我們更關注的是我們自己建立的用戶數據庫,假如系統數據庫出現問題,甚至實例出現問題,最壞的情況我們重搭環境,但是如果我們應用的用戶數據庫壞掉了,那可不是重搭環境就能解決的。這牽扯到公司利益問題,問題嚴重性不言而喻!
閑言少敘,我們速度進入本篇的正題。
上一篇我們介紹了SQL Server實例啟動的過程,并且分析了其詳細的過程,而在這一流程中,有一個步驟非常關鍵,就是加載恢復用戶數據庫的過程,我們來截取這段日志信息:
上面是一個正常啟動各個用戶庫的流程,SQL Server會采用多線程的進行數據庫啟動,并且在這個過程中進行一致性校驗,確保啟動的數據庫能夠正常使用。
而這過程中會發生很多問題,在分析問題之前,我先要介紹SQL Server數據庫的幾個常見狀態:
RECOVERING(恢復中):
這個狀態表示數據在啟動完成后,正在發生恢復,也就是上面日志中的 Recovery過程,和其它的關系型數據庫一樣,SQL Server對所有的數據庫行為都是先寫事務日志,然后在修改內存中的數據,然后通過后臺的一個進程在適當的時候進行寫入硬盤(Lazy write),所以在數據庫運行過程中,磁盤中的數據并不是最新的,如果這個時候關閉了,在下一次啟動過程中SQL Server就要根據事務日志中的記錄,將磁盤中的舊的數據改寫,改寫過程為:
1、重做redo
2、回滾和撤銷 undo/rollback
上面的目的就是為了保證數據庫一致性。
如果上面的流程發生了問題,就會進去到下面這個狀態:
RECOVERY PENDING(掛起還原):
這個過程就是將恢復數據的過程掛起,掛起的原因基本就是不能正常打開所用的數據庫文件。這里先記住這個狀態就行,我在后面的內容會再現這個問題,以及給出解決方案。
如果能找到文件或者能打開文件,但是文件有問題,機會出現下面這個狀態:
SUSPECT(質疑):
這個狀態,我相信很多用戶如果在玩數據庫久了的時候,會偶爾遇到,相對于其它狀態,這個狀態是出現最高的。
原因很簡單:數據庫文件壞掉了。
當經歷了上面的這個幾個狀態都不出現問題,上面的這幾個狀態下,數據庫都是不能使用的,會進入到下面這個狀態:
ONLINE(在線):
這個狀態應該是最期待的了,數據庫在線,正常使用,默認都是正常的在線狀態。
當然,除了上面幾個數據庫自己形成的數據庫狀態,在我們管理員處理數據庫的時候也會更改狀態,這里我們順便提一下:
OFFLINE(離線):有在線狀態就有離線狀態,很簡單,讓數據庫離線,用戶不能使用
RESTORING(還原中):這個狀態很簡單,管理員正在還原該數據庫,不解釋
EMERGENCY(緊急):這個狀態也是管理員用的,就是說明數據庫有問題了,它正在盡量解決
以上幾個狀態中,發生在啟動過程中,并且會發生問題就是上面的RECOVERY PENDING(掛起還原)、SUSPECT(質疑)、RECOVERING(恢復中):
我們依次來看:
RECOVERY PENDING(掛起還原):
出現這個狀態通常的原因是數據庫文件找不到,或者文件找到權限訪問不到,我們來看該問題報錯信息:
在數據庫中存儲方式中,分為主文件組和輔助文件組和日志文件,為了展示方便我們特意建立了個測試庫,來重現該部分問題:
<1>主文件組問題
當不能訪問主文件組文件的時候,也就是上面的CnblogsTestDB.mdf文件,會報如下錯誤:
我們先來看數據庫:
在實例啟動的過程,恰巧有一個庫顯示了上面我們提到的一個狀態:RECOVERING(恢復中),我順便把圖給截圖了,當然出現這個情況很正常,有時候刷新一下就正常,其它用戶庫沒有顯示是因為庫太小,恢復時間太短,我們捕捉不到。
我們來看,上面我們建立的測試庫CnblogsTestDB已經不能訪問了,我們來看一下Error中的錯誤信息:
錯誤信息很明顯,說這個該文件不能訪問,并且確切的說出了這個為操作系統錯誤,那我們看操作系統的錯誤記錄:
可以看到在Windows系統日志中也能看到該部分錯誤信息。
解決方案:
此問題的解決方法還是很簡單的,一般主要是因為權限問題,只需要將數據庫管理員賬戶組,提權到可讀寫權限就可以,然后重啟服務:
上面的情況是找到數據庫文件,但是不能打開數據庫文件,當然還有可能是直接找不到數據庫文件,系統會報出如下錯誤:
會給出17204錯誤,報找不到文件錯誤
解決方案:
a、如果能找到數據文件最好了,拷貝到錯誤制定的路徑下既可以,然后重啟實例
b、不能找到文件了,那就得只能刪除該庫,重新新建同名庫,從備份文件中還原
一般上述問題發生在物理存儲出現了故障,當然不排除某些軟件操作,比如殺毒軟件、還有人為誤刪等原因。如果沒有備份,這可能是一個很大的遭難,基本可以確定的完全還原的可能性不高!所以記?。簜浞輸祿斓闹匾裕?/p>
<2>輔助文件組問題
上面的出現問題的文件為數據庫的主文件組,當我們數據庫在承載到一定數據量的情況下,我么采取多個輔助文件組來容納數據,下面我們來看一下輔助文件組的問題:
同樣的提示的輔助文件組不能正常打開,或者找不到相關的輔助文件組,遇到這樣的問題我們怎么解決呢?
其實SQL Server數據庫輔助文件存儲的主要為數據庫的數據內容信息,關于本庫的一些架構信息是放在主(PRimary)文件組中,所以我們可以先這樣
解決方案:
a、我們將打不開或者不能訪問的數據庫文件(輔助文件)設置成離線,然后先將能夠正常的數據文件上線,確保除了損壞的那部分文件的其它庫信息能正常訪問,我們通過以下代碼更改:
ALTER DATABASE CnblogsTestDB MODIFY FILE(NAME=CnblogsTestDB2,OFFLINE)GOALTER DATABASE CnblogsTestDB set ONLINEGO
這樣,我們刷新下數據庫,既可以正常訪問正確的數據信息:
當我們處于生產環境中,生產庫不能正常啟動的時候,此刻的火燒眉毛的時刻,采取上面的方法先確保一部分數據能正常訪問也不失為一種緩議之計。
下面的步驟就是找到該輔助文件,并且確保有正常的權限訪問,更重要的是找到的輔助文件不能是損壞的,然后拷貝至錯誤文件中給出的路徑,然后重啟實例,上線該庫。
b、當然大部分情況下,我們找不到該文件,或者這個文件已經損壞,那就得采取第二種方案,通過備份還原,根據以往的經驗,建議采取的措施是:
先將能訪問的數據庫做一次備份,然后通過文件組恢復的方式,恢復上面出問題的文件組。
<3>日志文件組
其實從市面上的所有數據庫而言,其本身所有的機制都是通過先寫日志,然后通過一個進程后寫入(lazy write)方式寫入到磁盤,這種方式是為了避免IO的阻塞,因為我們都知道磁盤IO這個問題一直是所有文件讀寫的最大瓶頸。
所以,日志文件是數據庫不可分割的一部分。當數據庫在啟動的過程,會通過日志中的記錄做一次數據的一致性校驗,文章的開端有介紹。
所以說,如果日志文件不能訪問,或者說出問題,那我們的SQL Server數據庫會出現什么問題呢?
我們先來看數據庫模式為簡單(SIMPLE)模式的,我將咱們的測試庫設置成簡單模式:
USE CnblogsTestDBGOALTER DATABASE [CnblogsTestDB] SET RECOVERY SIMPLE WITH NO_WAITGO
然后我們停掉實例,然后刪除掉該庫的日志文件,然后重新啟動
可以看到處于簡單模式下,如果日志文件出現錯誤,在啟動的過程是不會發生任何問題的,這里的原因我們在啟動Error日志文件中能找到答案:
經過上面的日志分析,我們可以看到,當數據庫處于簡單模式下,數據庫在啟動的過程中,如果發現任何與日志相關的信息,則會重新創建一份日志文件,保證數據庫的正常訪問。
如果這樣那我們數據庫的完整性怎么保證呢,是這樣,如果數據庫處于簡單模式,在我們數據庫關閉的時候,系統會先將該提交的所有事務都寫入到磁盤中去,所有該回滾的就撤銷。
上面能正常創建數據庫日志文件的前提條件有兩條:1、數據庫為簡單模式;2、數據庫正常關閉,保證事務都已正常寫入磁盤
下面我們在看看如果恢復模式為“完整”模式下的,數據庫上次沒有正常的情況,SQL Server數據庫是如何處理的,
我們先將數據庫改成完整恢復模式,停掉實例,然后刪除日志,然后啟動
然后我們啟動,可以看到這個時候,數據庫不能正常訪問的,該錯誤的Error的日志信息為:
windows平臺下也為我們記錄了該錯誤的日志信息:
其實出現上面的錯誤,很正常,因為有些數據庫的事務性操作已經記錄到事務日志中,還未寫入磁盤數據頁中,這時候發生了宕機,或者非正常關閉,這個對SQL Server數據庫是能應付的,但是,而在啟動的過程找不到相關的事務日志盡心回滾和寫入操作,所以該庫的數據時非一致性的,所以SQL Server是不讓我們使用該庫,出現此種錯誤,我們的解決方式有如下幾種:
解決方案:
a、如果有備份,最好最快的方式就是恢復數據庫備份或者找到了該日志文件拷貝到錯誤路徑下(推薦)
b、如果沒有備份,我們只能通過使用CHECKDB命令修復數據庫(不推薦)
上述解決方案中CHECKDB命令,是一種萬不得已的方式,而且,我可以明確的告訴你這命令使用的時候會可能造成數據丟失,并且在大數據庫中,運行周期很長!
當然在萬不得已的情況下,我們還的采取,過程如下:
我們先將數據庫設置成EMERGENCY(緊急)模式,并且為單用戶(SINGLE_USER)模式
USE CnblogsTestDBGOALTER DATABASE CnblogsTestDB SET EMERGENCYGOALTER DATABASE CnblogsTestDB SET SINGLE_USERGO
經過我們上面的設置,將庫設置成了“緊急”模式,并且只為單用戶方式訪問,便于我們進行數據修復
然后我們執行CHECKDB命令,進行數據庫的修復
DBCC CHECKDB(CnblogsTestDB,REPAIR_ALLOW_DATA_LOSS)GO
經過該命令的修復,數據庫會為系統新建一個日志,但是不能保證事務的一致性,也就是說會因此而丟失數據,所以非常不推薦的一種方式!
并且,在這過程中,如果是大數據庫的話,該修復過程會很漫長,當然我不能給出一個漫長參考值,因為這過程還有會出現其它的錯誤需要修復。
所以酌情考量。
當然,在恢復完成之后,不要忘記將數據庫改回多用戶模式
USE [master]GOALTER DATABASE [CnblogsTestDB] SET MULTI_USER WITH ROLLBACK IMMEDIATEGO
至此,這個有問題的庫就能夠正常訪問了。
----------------------------------------------------------霸氣的分割線-----------------------------------------------------------------------
在經歷了上面的文件級別錯誤后,在數據庫啟動的過程,還經常出現的是數據頁級別的錯誤,相對于上面的文件錯誤級別,在數據頁中造成的錯誤粒度更小,并且基本不會反映到數據庫級別,也就是說在出現數據頁級別的錯誤時候,該數據時可以正常訪問的,只是在訪問有錯誤的數據頁的時候才會報錯,在我們遇到這種錯誤的時候該如何解決呢?
下面我們依次來分析,首先我們來制作一個經典的824錯誤,以下部分內容牽扯到數據庫部分基礎,限于篇幅,我們不做詳細介紹:
<1>首先我們在我們的測試庫中新建一個表,我們將該表新建成一行為一個數據頁的方式,也就是說一行數據庫在數據庫中就能承載一個數據頁
USE CnblogsTestDBGOCREATE TABLE [dbo].[TestPage]( [a] [int] NULL, [b] [nvarchar](3900) NULL) ON [PRIMARY]
腳本很簡單,一張表,兩列,一列int類型,一列nvarchar(3900),一行數據的存儲空間為:3900*2(nvarchar(3900))字節+4(int)+96字節(頁頭)+36字節(行偏移)=7932字節,我們知道一個數據頁存儲的信息為8K=8192字節,包括其它消耗所以該表一行數據如果填充完,一行數據將近乎占據一個數據頁。
我們
新聞熱點
疑難解答