SQL大型事務(wù)日志備份與修復(fù)問題

字號:

問:我在備份時注意到一些異?,F(xiàn)象,希望您能解釋一下。我們需要經(jīng)常備份 62GB 的生產(chǎn)數(shù)據(jù)庫,以刷新應(yīng)用程序開發(fā)人員所使用的數(shù)據(jù)。我們總是先刪除舊副本然后再還原新副本。還原的副本與生產(chǎn)數(shù)據(jù)庫大小相同,而且數(shù)據(jù)看起來也一樣,但是與備份過程相比,還原過程所需的時間要長很多。這是怎么回事?為什么還原比備份需要更長的時間?
    答:其實(shí)這不屬于異?,F(xiàn)象。根據(jù)您所處的環(huán)境,這種現(xiàn)象通常是預(yù)料之中的。備份和還原所需的時間之所以不同,是由于每個流程必須執(zhí)行的步驟不同。
    備份數(shù)據(jù)庫包含兩個步驟。主要就是對數(shù)據(jù)庫執(zhí)行 IO 讀取操作以及對備份設(shè)備執(zhí)行 IO 寫入操作:
    備份步驟 1 讀取數(shù)據(jù)文件中所有分配的數(shù)據(jù),然后將其寫入備份設(shè)備。
    備份步驟 2 讀取某些事務(wù)日志,然后將其寫入備份設(shè)備。
    所需的事務(wù)日志數(shù)量可能會差異很大,但其數(shù)量一定能將還原的數(shù)據(jù)庫恢復(fù)到相同的時間點(diǎn)
    而還原數(shù)據(jù)庫最多可能包含四個步驟。涉及的工作要比讀寫 IO 復(fù)雜得多:
    還原步驟 1 如果數(shù)據(jù)庫文件不存在,則創(chuàng)建它們。
    還原步驟 2 從備份中讀取所有數(shù)據(jù)和事務(wù)日志,然后將其寫入相關(guān)的數(shù)據(jù)庫文件。
    還原步驟 3 對事務(wù)日志運(yùn)行恢復(fù)過程的“重做”階段。
    還原步驟 4 對事務(wù)日志運(yùn)行恢復(fù)過程的“撤消”階段。
    兩個備份步驟所需時間與還原步驟 2 所需時間大致相同(假定硬件配置類似并且服務(wù)器上沒有用戶活動)。如果數(shù)據(jù)文件較大并且需要進(jìn)行零初始化(這在 SQL Server 2000 中是需要執(zhí)行的操作,在 SQL Server 2005 中是默認(rèn)操作),則還原步驟 1 可能需要較長時間。
    為避免花費(fèi)較長時間,請不要在開始還原之前刪除現(xiàn)有文件。或者,也可以啟用即時初始化,以便快速創(chuàng)建這些文件。
    還原步驟 3 和 4 是對還原的數(shù)據(jù)庫進(jìn)行恢復(fù),以便確保事務(wù)一致性;此流程與崩潰恢復(fù)期間對數(shù)據(jù)庫執(zhí)行的操作流程相同。恢復(fù)操作所需時間取決于需要處理的事務(wù)日志量。例如,如果在進(jìn)行備份時恰好有一個長時間運(yùn)行的事務(wù)處于活動狀態(tài),則該事務(wù)的所有事務(wù)日志都會被備份進(jìn)來,因此屆時不得不進(jìn)行回滾。
    問:我打算在日志傳送和數(shù)據(jù)庫鏡像之間選擇一種提供生產(chǎn)數(shù)據(jù)庫冗余副本的方法。我很擔(dān)心需要在兩個服務(wù)器之間傳送的事務(wù)日志數(shù)量,尤其是對于我們每晚都需要執(zhí)行的索引重建操作。聽說鏡像功能發(fā)送的是實(shí)際的重建命令而非事務(wù)日志,而重建是在鏡像中完成的。真的是這樣嗎?如果是這樣,那么對于 BULK_LOGGED 恢復(fù)模式,鏡像解決方案要優(yōu)于日志傳送,對嗎?
    答:您聽說的觀點(diǎn)是錯誤的。數(shù)據(jù)庫鏡像過程是通過將實(shí)際的事務(wù)日志記錄從主體數(shù)據(jù)庫發(fā)送到鏡像服務(wù)器來完成的,這些記錄在鏡像數(shù)據(jù)庫中將被“重播”。對于鏡像的數(shù)據(jù)庫,既不存在任何類型的轉(zhuǎn)換或篩選,也不存在任何類型的 T-SQL 命令攔截。
    數(shù)據(jù)庫鏡像僅支持 FULL 恢復(fù)模式,這意味著始終會完全記錄索引重建操作。根據(jù)涉及的索引大小的不同,這可能意味著會生成大量事務(wù)日志,從而導(dǎo)致主體數(shù)據(jù)庫的日志文件很大,在將日志記錄發(fā)送到鏡像時需要占用大量的網(wǎng)絡(luò)帶寬。
    您可以將數(shù)據(jù)庫鏡像視為實(shí)時日志傳送(實(shí)際上,這正是早期在 SQL Server 2005 開發(fā)期間該功能所使用的名稱)。在日志傳送過程中,主數(shù)據(jù)庫的事務(wù)日志備份會定期傳送到輔助服務(wù)器上,并在輔助數(shù)據(jù)庫中進(jìn)行還原。
    日志傳送功能支持 FULL 和 BULK_LOGGED 恢復(fù)模式。對于使用 FULL 恢復(fù)模式在日志傳送數(shù)據(jù)庫中所執(zhí)行的索引重建操作,生成的事務(wù)日志量將與鏡像數(shù)據(jù)庫中生成的數(shù)量完全相同。但是,在日志傳送數(shù)據(jù)庫方案中,數(shù)據(jù)是以日志備份(或系列日志備份)而非連續(xù)流的形式發(fā)送到冗余數(shù)據(jù)庫的。
    如果在索引重建完畢后在日志傳送數(shù)據(jù)庫中使用 BULK_LOGGED 恢復(fù)模式,則只會生成少量的事務(wù)日志。但是在下次事務(wù)日志備份時,還將會包含被所記錄的最低限度索引重建操作改變的全部數(shù)據(jù)文件范圍。這意味著無論是納入在 BULK_LOGGED 恢復(fù)模式下重建的索引的日志備份還是納入在 FULL 恢復(fù)模式下重建的索引的日志備份,其大小都幾乎完全相同。
    因此,對于鏡像數(shù)據(jù)庫與日志傳送數(shù)據(jù)庫中的索引重建而言,需要發(fā)送到冗余數(shù)據(jù)庫的信息量幾乎完全相同。實(shí)際的差別僅在于發(fā)送信息的方式 — 是連續(xù)發(fā)送還是成批發(fā)送。
    在這兩種方法之間進(jìn)行選擇時需要考慮許多其他因素(因素太多,僅在一次 SQL 問題解答中無法全部討論)。您應(yīng)該先了解所有這些因素與您的需求的關(guān)聯(lián)程度(例如,可接受的數(shù)據(jù)丟失限制和允許的停機(jī)時間),然后再做決定。
    問:我正在運(yùn)行 SQL Server 2005,我發(fā)現(xiàn)其中一個數(shù)據(jù)庫的事務(wù)日志一直在不斷增大。該數(shù)據(jù)庫處于完全恢復(fù)模式,我正在進(jìn)行事務(wù)日志備份。我認(rèn)為這本應(yīng)該防止事務(wù)日志不斷增大。這其中究竟發(fā)生了什么問題?
    答:在完全恢復(fù)模式下進(jìn)行事務(wù)日志備份很重要,在這一點(diǎn)上您是對的。但是,還有其他一些因素可導(dǎo)致事務(wù)日志增大??荚?大提示這完全取決于究竟是什么在要求事務(wù)日志成為被使用的日志(或活動日志)。除了缺乏事務(wù)日志備份以外,可能會導(dǎo)致此現(xiàn)象發(fā)生的其他常見因素還包括復(fù)制、數(shù)據(jù)庫鏡像和活動事務(wù)等。
    復(fù)制過程是通過異步讀取事務(wù)日志記錄,然后加載這些事務(wù)并將其復(fù)制到單獨(dú)的分布數(shù)據(jù)庫來完成的。尚未被復(fù)制日志讀取器任務(wù)讀取的任何事務(wù)日志記錄都無法被釋放。如果您的工作負(fù)載生成了大量事務(wù)日志記錄,而您又為復(fù)制日志讀取器的運(yùn)行頻率設(shè)置了較長的時間間隔,則會累積大量記錄,導(dǎo)致事務(wù)日志增大。
    如果您運(yùn)行的是異步數(shù)據(jù)庫鏡像,則可能會存在尚未從主體數(shù)據(jù)庫發(fā)送到鏡像服務(wù)器的事務(wù)日志記錄儲備(稱為數(shù)據(jù)庫鏡像 SEND 隊(duì)列)。這些事務(wù)日志記錄在成功發(fā)出之前無法被釋放。如果生成了大量事務(wù)日志記錄,而網(wǎng)絡(luò)帶寬又受到限制(或出現(xiàn)其他硬件問題),則儲備可能會變得很大,導(dǎo)致事務(wù)日志不斷增大。
    最后,如果用戶啟動了一個顯式事務(wù)(如使用 BEGIN TRAN 語句),然后進(jìn)行了某些形式的修改(如 DDL 語句或插入/更新/刪除操作),則所生成的事務(wù)日志記錄在用戶提交或回滾該事務(wù)前都需要進(jìn)行保留。這意味著由其他事務(wù)生成的任何后繼事務(wù)日志記錄也無法被釋放,因?yàn)槭聞?wù)日志無法選擇性地進(jìn)行釋放。如果假設(shè)該用戶當(dāng)天沒有結(jié)束該事務(wù)就下班回家了,則隨著越來越多的事務(wù)日志記錄被不斷生成而又無法釋放,事務(wù)日志就會越來越大。
    要了解事務(wù)日志無法釋放的原因,可以查詢 sys.databases 系統(tǒng)目錄視圖并查看 log_reuse_wait_desc 列,類似于下面所示:
    SELECT name AS [Database],
    log_reuse_wait_desc AS [Reason]
    FROM master.sys.databases;
    如果證明是由于某個活動事務(wù)所導(dǎo)致的,可使用 DBCC OPENTRAN 語句獲取有關(guān)該事務(wù)的更多信息:
    DBCC OPENTRAN ('dbname')
    問:我聽說在從損壞進(jìn)行恢復(fù)時,不到萬不得已不要使用 REPAIR_ALLOW_DATA_LOSS;而應(yīng)先使用備份進(jìn)行還原。您是否能解釋一下為什么不應(yīng)使用 SQL Server 2005 的修復(fù)功能,以及為什么在使用時要考慮“危險”程度?
    答:首先,我確實(shí)編寫過有關(guān) SQL Server 2005 修復(fù)的文章。REPAIR_ALLOW_DATA_LOSS(以下簡稱為“修復(fù)”)的問題在于其工作方式不是很清晰。之所以這樣命名此“修復(fù)”正是為了說明運(yùn)行它可能會導(dǎo)致數(shù)據(jù)庫中的數(shù)據(jù)丟失。此功能修復(fù)損壞的數(shù)據(jù)庫結(jié)構(gòu)的方式通常是先刪除損壞的結(jié)構(gòu),然后修復(fù)數(shù)據(jù)庫中的其他所有內(nèi)容(引用已刪除結(jié)構(gòu)的或被已刪除結(jié)構(gòu)引用的)。在保持?jǐn)?shù)據(jù)庫結(jié)構(gòu)一致性方面,“修復(fù)”的確是不到萬不得已不推薦使用的方法,因?yàn)樗闹埸c(diǎn)不是挽救用戶數(shù)據(jù)?!靶迯?fù)”不會故意去刪除用戶數(shù)據(jù),但它也不會主動去挽救用戶數(shù)據(jù)。
    這樣一來您可能會認(rèn)為“修復(fù)”是一種不可靠的方式,但是當(dāng)不得不進(jìn)行修復(fù)時,它可以提供一種最快捷最可靠的方法來修復(fù)損壞數(shù)據(jù)。在進(jìn)行災(zāi)難恢復(fù)時,速度極為重要,并且要求絕對準(zhǔn)確。設(shè)計(jì)出經(jīng)過驗(yàn)證能在各種情況下迅速準(zhǔn)確地完成修復(fù)操作的復(fù)雜修復(fù)算法幾乎是不可能的。例如,在修復(fù)代碼中有一些復(fù)雜算法可解決為兩個索引分配同一頁面或范圍的問題,但通常此算法都是采用此“修復(fù)”功能再加上一些修補(bǔ)。
    此外,還有其他一些需要了解的有關(guān)“修復(fù)”的問題:
    在刪除損壞的結(jié)構(gòu)時,“修復(fù)”不會考慮外鍵約束,因此,可能會刪除與其他表格有外鍵關(guān)系的表格中的記錄。如果在運(yùn)行“修復(fù)”后不運(yùn)行 DBCC CHECK­CONSTRAINTS,則無法確定是否發(fā)生了這種情況。
    “修復(fù)”不會(也無法)考慮在應(yīng)用程序級定義的、可能會被要刪除的某些數(shù)據(jù)破壞的任何內(nèi)在業(yè)務(wù)邏輯或數(shù)據(jù)關(guān)系。同樣,如果不運(yùn)行應(yīng)用程序中構(gòu)建的任何一種自定義的一致性檢查,則無法確定是否有關(guān)系遭到破壞。
    某些修復(fù)操作無法被復(fù)制。在對等拓?fù)渲袑Πl(fā)布服務(wù)器或節(jié)點(diǎn)運(yùn)行“修復(fù)”可能會在拓?fù)渲幸氩灰恢聠栴},必須手動進(jìn)行糾正。
    鑒于以上原因,通過采用備份而非運(yùn)行“修復(fù)”來從損壞中進(jìn)行恢復(fù)始終是個不錯的辦法。但是產(chǎn)品中也提供了“修復(fù)”,因?yàn)橐坏┏霈F(xiàn)數(shù)據(jù)庫受損而又沒有備份的情況,最起碼要有一種方法能使數(shù)據(jù)庫迅速恢復(fù)聯(lián)機(jī)狀態(tài)。
    問:我剛以一名 DBA 的身份加入一家新公司,現(xiàn)在需要負(fù)責(zé)管理多種應(yīng)用程序及其后端數(shù)據(jù)庫。其中一種應(yīng)用程序的更新性能非常差。在經(jīng)過調(diào)查后,我發(fā)現(xiàn)該應(yīng)用程序使用的每個表都包含大量索引。經(jīng)過多方詢問后,才知道似乎是以前的 DBA 喜歡對各個表列及某些組合添加索引。我認(rèn)為并非所有索引都是必要的,但我該如何找出可以安全刪除的索引呢?我們運(yùn)行的是 SQL Server 2005。
    答:正如您所猜測的那樣,大量索引極有可能是造成性能不佳的主要因素。每次在表中插入、更新或刪除行時,都需要在每個非群集索引中執(zhí)行相應(yīng)的操作??荚?大提示這將在 I/O、CPU 利用率和事務(wù)日志生成等方面增加大量的管理開銷。
    在 SQL Server 2000 中,判斷正在使用哪些索引的途徑是使用配置文件和檢查查詢計(jì)劃。在 SQL Server 2005 中,則可使用新的動態(tài)管理視圖 (DMV) -sys.dm_db_index_usage_stats,它可以跟蹤索引使用情況。
    此 DMV 會跟蹤數(shù)據(jù)庫啟動以來的每一次索引使用及使用方式。SQL Server 關(guān)閉后所有數(shù)據(jù)庫的統(tǒng)計(jì)信息均會丟失,某個數(shù)據(jù)庫關(guān)閉或拆分后,該數(shù)據(jù)庫的統(tǒng)計(jì)信息會丟失。其想法是如果某個索引未出現(xiàn)在輸出中,則它肯定在數(shù)據(jù)庫啟動后就未被使用過。
    隨著時間的推移來跟蹤索引使用情況的簡單方法是定期拍攝 DMV 輸出的快照,然后對這些快照加以比較。許多人都忽略的一點(diǎn)是必須跟蹤索引在整個業(yè)務(wù)周期內(nèi)的使用情況。如果您只是拍攝一天的快照,則可能會發(fā)現(xiàn)多個未使用過的索引。但是,如果這些索引是具有其他用途,比如用于幫助月底報表更快速地運(yùn)行,則可能不應(yīng)該刪除這些索引。如果某索引確實(shí)在整個業(yè)務(wù)周期內(nèi)都未被使用過,則很可能能夠?qū)⑵鋭h除并回收空間以提高性能。