SQL SERVER 事務日誌文件不停增長解決

 SQL SERVER 事務日誌文件不停增長解決

本文轉自http://blog.csdn.net/guchuanlong/archive/2010/11/03/5985258.aspx

1.3  日誌文件不停增長

事務日誌文件是一個SQL Server數據庫的另一個重要組成部分。每個數據庫都有事務日誌,用於記錄所有事務以及每個事務對數據庫所做的修改。爲了提高數據庫的整體性能,SQL Server檢索數據時,將數據頁讀入緩衝區高速緩存。數據修改不是直接在磁盤上進行,而是修改高速緩存中的頁副本。直到數據庫中出現檢查點,或者必須將修改寫入磁盤才能使用緩衝區來容納新頁時,纔將修改寫入磁盤。將修改後的數據頁從高速緩衝存儲器寫入磁盤的操作稱爲刷新頁。在高速緩存中修改但尚未寫入磁盤的頁稱爲"髒頁"。

對緩衝區中的頁進行修改時,會在日誌高速緩存中生成一條日誌記錄。SQL Server具有防止在寫入關聯的日誌記錄前刷新髒頁的邏輯。會確保日誌記錄在提交事務時,或者在此之前,一定已經被寫入磁盤。

換句話說,SQL Server對數據頁的插入、修改和刪除,都是隻在內存中完成後,就提交事務。這些修改並不立刻同步到硬盤的數據頁上。而SQL Server又必須保證事務的一致性。哪怕發生了SQL Server異常終止(例如SQL Server服務崩潰,機器掉電),內存中的修改沒有來得及寫入硬盤,下次SQL Server重啓的時候,要能夠恢復到一個事務一致的時間點。已經提交的修改要在硬盤中的頁面重新完成。爲了做到這一點,SQL Server必須依賴於事務日誌。

因此,任何一個數據庫,哪怕是tempdb,都有日誌文件。它和數據文件同等重要。如果日誌文件缺失或者損壞,將等同於數據庫損壞。

在SQL Server的使用過程中,會由於種種原因,出現日誌文件大小不停增長的現象。當文件達到最大限值,或者是把硬盤空間全部佔用後,數據庫就無法再進行任何插入、修改、刪除的工作。本節的主題,就是要探討出現這種現象的原因,以及相應的解決辦法。

當日志文件大小超過預期的時候,數據庫管理員自然會想去看看日誌文件中到底存放了些什麼信息。SQL Server有一條"DBCC LOG"命令可以幫助我們解釋日誌文件中的信息。它的語法是:


DBCC LOG(, )
:目標數據庫編號。可以用sp_helpdb得到。

:DBCC LOG命令翻譯和解釋日誌記錄的方式。

一般來講,使用"3"這個格式參數輸出比較詳細。

下面我們通過一個很簡單的表格操作來看看SQL Server是怎麼組織事務日誌記錄的。

首先,我們在範例數據庫AdventureWorks裏面創建一個只有一個int類型字段的表格。然後將數據庫日誌文件清空。接着運行DBCC LOG命令。找到這時日誌文件的最後一條記錄。


use adventureworks  go  create table a (a int)  go  checkpoint go  backup log adventureworks with truncate only go  dbcc log(5,3)  -- 5是adventureworks數據庫的編號,每個SQL Server可能都不同  -- 可以用sp helpdb來查到數據庫編號  go  接着,我們在表格裏插入一條記錄。  insert into a values (1)  go  dbcc log(5,3)  go
結果中(見圖1-26)可以看到三條關於這個Insert的記錄。

 
圖1-26  DBCC LOG結果中3條和INSERT動作相關的記錄

我們再插一條記錄。


insert into a values (100)  go  dbcc log(5,3)  go
可以看到新的3條記錄(見圖1-27)。新的記錄有不同的LSN編號。

 
圖1-27  新的3條和INSERT動作相關的記錄

從這些記錄裏,我們可以看到剛纔做的INSERT的事務,它的起始時間,剛纔連接的SPID,以及其他一些信息。SQL Server完全可以通過這些記錄把INSERT重做,或者撤銷。

我們把這兩條記錄都改成2。這次出現了6條記錄(見圖1-28)。


update a set a = 2  go
 

  
 

 
圖1-28  和UPDATE動作相關的6條記錄

從這些記錄可以看出,雖然只是一條UPDATE語句,但是實際上SQL Server並沒有記錄語句本身。它記錄的是兩條被修改的數據原來的值和現在的值。

因此我們可以發現,SQL Server的日誌記錄有以下特點:

1. 日誌記錄的是數據的變化,而不是記錄用戶發過來的操作。

1.3.2  日誌文件增長的原因

前面已經談到,SQL Server會爲所有的修改記錄日誌,以便將來重新提交或者回滾時使用。那不停地記錄,日誌文件豈不會空間耗盡?爲此,SQL Server設計了相對應的機制,能夠定期清理日誌文件中不再需要的日誌記錄。

那哪些日誌記錄是"不再需要"的呢?我們反過來看,什麼是SQL Server"需要"的。SQL Server需要下面這幾類日誌記錄:

1. 所有沒有經過"檢查點"的日誌記錄。

SQL Server定期做檢查點(Checkpoint),保證所有的"髒頁"都被寫入硬盤。未做檢查點的修改,可能僅是內存中的修改。數據文件裏還沒有同步。SQL Server要硬盤上的日誌文件裏有一份記錄,以便在異常重啓後重新修改。

2. 所有沒有提交的事務所產生的日誌記錄,以及在它們之後的所有日誌記錄。

如果一個事務還沒有提交,那它可以在任何時候回滾。SQL Server必須做好這種準備,以便能夠從日誌記錄中找回修改前的數據內容,完成回滾。在SQL Server裏面,所有的日誌記錄都有嚴格順序,中間不可以有任何跳躍。所以如果某個數據庫有沒有提交的事務,SQL Server會標記所有從這個事務開始的日誌記錄(不管和這個事務有沒有關係)爲活動事務日誌。這些日誌記錄都有可能"需要"被用來做回滾。

3. 所有要做備份的日誌記錄。

如果數據庫設的恢復模式不是簡單模式,那SQL Server就假設用戶是要去備份日誌記錄的。所有未被備份的記錄,SQL Server都會爲用戶保留,哪怕這些記錄對數據庫本身已經沒有其他用途了。

4. 有其他需要讀取日誌的數據庫功能模塊。

除了數據庫引擎,還有一些功能,比如說,事務型複製(Transactional Replication)和數據庫鏡像(Database Mirroring)也需要讀取日誌文件中的內容,完成它們的同步工作。在這些功能組件沒有讀取日誌記錄之前,SQL Server也會保留。

對所有"不需要"的日誌記錄,SQL Server會在每個檢查點做一次截斷的動作,把這些記錄佔用的空間標誌成可重用。這樣這些空間就被釋放出來。因爲日誌文件是循環使用的,只要日誌文件裏有這樣的空間,SQL Server都會去重用,所以不會報告空間已滿,或者試圖去做自動增長。SQL Server做檢查點的頻率取決於服務器屬性"Recovery Interval"。默認大概一分鐘左右做一次檢查點。

如果日誌文件裏"需要"的記錄越來越多,那就會出現日誌文件不停增長的現象。通常的原因有下面幾個。

1. 數據庫恢復模式不是簡單模式,但是沒有安排日誌備份。

需要強調的是,對於非簡單模式的數據庫,只有做完日誌備份後記錄纔會被截斷。做完整備份和差異備份都不會起這個作用。

2. 數據庫上面有一個很長時間都沒有提交的事務。

由於應用程序設計的問題,有些連接可能會遺留一個事務在SQL Server裏面,而不是及時提交了它。SQL Server是不會干預用戶的這種行爲的。只要這個連接不退出,這個事務就會永遠存在,直到客戶端主動提交或者回滾它。而從這個事務開啓的那個時間點開始的所有日誌記錄,SQL Server都會保留。(做過日誌備份也沒有用。)

3. 數據庫上有一個很大的事務正在運行。

例如,某個用戶正在建立/重建索引,或者用DELETE/INSERT語句刪除或插入大量數據等。或者用戶端開了一個服務器端遊標,但是沒有把數據及時取走等。

4. 數據庫複製或者鏡像出了異常。

要避免日誌文件不停增長,其實就是要避免上面這些情況的發生。對於一個最近不會去做日誌備份的數據庫,設成簡單恢復模式即可。如果數據庫設成了完整恢復模式,那就一定要安排定期做日誌備份。如複製或鏡像任務出問題,要及時解決。如果沒辦法解決,就必須暫時拆除複製或鏡像,以防止日誌記錄越積越多,最終造成數據庫不可使用。在設計程序的時候,也要避免事務時間過長,一個事務做太多的操作。數據庫晚上或週末會做一些維護工作,例如歷史數據清洗整理,數據導入導出,索引重建等。這些操作都可能寫許多日誌,所以要爲它們預留出足夠的空間,並且在做完之後及時備份。

1.3.3  案例:日誌增長原因定位

當日志文件增長到很大時,可以採取一些臨時手段,比如把有未關閉事務的連接強制取消,或者截斷數據庫的事務。但是,管理者必須找到日誌增長的原因,從而從根本上解決問題。

讓我們來練習一下如何定位日誌增長的原因。

步驟1:檢查日誌現在使用情況和數據庫狀態

首先要檢查當前日誌的使用百分比、數據庫恢復模式和日誌重用等待狀態。和SQL Server 2000不同的是,SQL Server 2005在管理視圖sys.databases裏面加入了一列log_reuse_wait(log_reuse_wait_desc)以反映SQL Server認爲的,不能截斷日誌的原因。可能的狀態如表1-6所示。

表1-6  log_reuse_wait(log_reuse_wait_desc)的可能狀態

檢查腳本很簡單:

DBCC SQLPERF(LOGSPACE)  GO  SELECT name, recovery model desc, log reuse wait,log reuse wait desc       FROM sys.databases  GO 檢查結果如圖1-29所示。

如果當前日誌的絕大部分都在使用中(Log Space Used (%)很高),那就要馬上定位是什麼原因導致了日誌記錄不能被SQL Server清除掉。如果當前日誌的大部分都已經處於空閒狀態了,那就說明觸發日誌增長的因素已經暫時消失。數據庫現在的狀態是正常的。如果問題反覆發生,可能就要想辦法跟蹤SQL Server內部運行的操作,直到抓住問題再次發生。

如果數據庫的日誌重用等待狀態是LOG_BACKUP,那就意味着SQL Server在等待着日誌備份。這時需要檢查備份計劃,是否需要做日誌備份。如果用戶並不期望做日誌備份,那就可以直接把恢復模式改成簡單。這樣SQL Server會在下一個檢查點的時候做日誌記錄截斷的工作。等到以後要安排日誌備份任務的時候,再把恢復模式改回來。

 
圖1-29  檢查數據庫日誌不能TRUNCATE的直接原因

步驟2:檢查最老的活動事務

如果日誌的大部分都在使用中,而且日誌重用等待狀態是ACTIVE_TRANSACTION,那麼就要看這個數據庫最久未提交的事務到底是由誰申請的。這就要用如下的命令。

DBCC OPENTRAN  GO  SELECT st.text,t2.*                                                               FROM sys.dm exec sessions AS t2, sys.dm exec connections AS t1          CROSS APPLY sys.dm exec sql text          (t1.most recent sql handle) AS st       WHERE t1.session id = t2.session id          AND t1.session id >50     DBCC OPENTRAN返回的是當前數據庫最久未被提交的事務。  Transaction information for database 'AdventureWorks'.   Oldest active transaction:      SPID (server process ID): 52      UID (user ID) : -1      Name          : user transaction     LSN           : (128:2474:11)      Start time    : Dec  7 2008  9:49:11:607PM      SID           :       0x010500000000000515000000bf093097412261f57d0f2a7ded030000  DBCC execution completed. If DBCC printed error messages,   contact your system administrator. 從上面的結果可以知道,這個事務是在Dec  7 2008  9:49:11:607PM開始的,是被SPID 52這個連接申請的。

從第二個查詢的結果(如圖1-30所示)可以知道這個連接是由什麼程序建立的,以及這個連接最後發過來的一句命令的內容。

 
圖1-30  第二句查詢的結果

知道了連接是誰建立的,也知道了它正在跑什麼,管理員就可以聯繫連接所屬應用程序的擁有者,瞭解爲何事務沒能及時提交,以及是否現在可以暫時終止這個事務。比較安全的方法是找到那個程序,從客戶端提交或取消這個事務(比如,暫時終止這個用戶正在進行的操作)。如果來不及或者一時找不到,可以在SQL Server端用KILL命令嘗試關閉這個連接。例如:

KILL 52

如果52這個連接還想運行任何命令,它會收到類似於下面的這個報錯。告知連接已經被斷掉了。

Msg 233, Level 20, State 0, Line 0

A transport-level error has occurred when sending the request to the server. provider: Shared Memory
Provider, error: 0 - No process is on the other end of the pipe.)

這時如果再運行DBCC OPENTRAN,命令會返回下一個最久未提交的事務,直到所有的事務都被提交或回滾完畢爲止。

需要說明的是,KILL命令並不是百試不爽的。如果一個連接正處於提交或者回滾的過程中,SQL Server會尊重它的執行而不去強行終止它。而如果需要終止的這個連接所開啓的事務非常龐大,比如,它正在做一個幾百萬條數據的修改或刪除動作,那麼取消掉這個動作而產生的回滾時間,可能不會比它的運行時間要短。有時候可能會等很久這個連接也終止不掉。所以這個方法只能在應急的時候嘗試使用。要從根本上解決問題,還是要改變客戶端的行爲,避免這種事件的發生。

在這一點上,日誌記錄的定位很清楚。它只是爲了保證數據庫一致性。所以它記錄的信息對SQL Server來講很有意義。但是如果想要通過它來倒推出用戶剛纔發過來的語句,可以說是不可能的。

2. 每條記錄都有它唯一的編號(LSN),並且記錄了它屬於的事務號。

這種設計便於事務的重新提交和回滾。

3. 日誌記錄的行數和實際修改的數據量有關。

SQL Server會爲每一條記錄的修改保存日誌記錄。如果單個語句修改的行數非常多,那它所帶來的日誌行數也就會非常多。所以日誌增長的速度不僅和事務的多少有關,還和事務所帶來的數據的修改量有關。

4. 日誌記錄了事務發生的時間,但是不保證記錄下了發起這個事務的用戶名,更不記錄發起者的程序名稱。

5. SQL Server能夠從日誌記錄裏面讀到數據修改前的值和修改後的值。但是對管理者來講,直接從日誌記錄裏面是很難了解其修改過程的。

討論這些的原因,是因爲很多用戶希望能從日誌文件裏倒推出數據庫曾經發生的異常操作。比如,是誰惡意或不小心刪掉了一些重要數據,或者是誰在某個時間段發起了一個龐大的事務。由於SQL Server日誌定位不是做用戶行爲監視和記錄,而是在對性能影響最小的前提下保證事務一致性,所以它記錄的內容是面向數據庫服務,而不是面向用戶的。換句話說,它記錄的東西只要SQL Server自己能讀懂就可以了,而沒有考慮要給用戶去訪問和理解。所以使用者很難用事務日誌來達到倒推的目的。

市場上有一些第三方的工具宣稱能從SQL Server的日誌文件中完成一些推斷工作。他們能夠更進一步地解釋日誌記錄,並且做一些自動化的工作(例如,幫助用戶回滾一個誤操作)。但是如果有什麼內容DBCC LOG看不到,這些工具應該也很難看到。所以如果要監視用戶的行爲,還是要開啓SQL Server自己的監視工具,比如SQL Trace或XEvents等。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章