SQL SERVER 日志寫入原理淺析


   昨天看到網上有一個關於SQL SERVER 課件,便隨手下載了下來看看主要講了些什么內容,於是看到了下面兩個PPT頁面

    PPT1

PPT2

 

    由於第一張PPT上的內容不太准確(日志文件中沒有“日志頁”的概念,只有VLF的概念,可能是我們對“數據頁”的概念太深刻了,因此弄了以“日志頁”的概念出來,而PPT中說先更新高速緩沖區中的數據頁,然后將事務日志寫入到“日志頁”,很容易讓人理解成先更改高速緩沖區,然后將日志寫入到磁盤上的“日志頁”),再加上我看PPT時比較"囫圇"(只看到前一張PPT,沒有往后翻兩下看后面一張PPT).因此我覺得PPT的作者在日志的寫入順序上有問題.索性查了一下資料,然后比較深入的思考了日志的寫入順序問題,同時也糾正了一些自己以往的不正確理解.

    該文主要包含以下內容:
     1.SQL SERRVER 日志管理器的大致工作內容與原理.
     2.實例探究SQL SERVER 事務日志的產生與寫入磁盤磁盤. 
     3.一些其它的相關思考.

    第一部分:SQL SERVER 日志管理器的大致工作原理. 

    日志管理器承擔着事務日志的編排與寫入工作。它維護着一個或多個被稱之為“日志緩存”的連續的專用內存區域。由於SQL SERVER 事務日志必須按照一定的格式寫入到日志文件中,因此日志緩存中的功能之一就是用來編排日志的格式。而當一個日志緩存區域被占滿的時候,還有一個或多個日志緩存區域可以被用來保存新產生的日志記錄。 
    其次,日志管理器維護着兩個日志緩存隊列,一個flushQueue,另一個是freeQueue。其中flushQueue包含的是等待被刷新到日志文件(物理磁盤)的日志緩存;freeQueue包含的是已經被刷新並且可以被再次使用的日志緩存。 
    而日志的刷新工作主要一個被稱之為“日志編寫器”的線程來負責,它將依次遍歷 flushQueue,一次僅將一個當前的日志緩存中的內容寫入到磁盤上。 
    而日志編寫器的刷新工作由什么來觸發呢?當一個事務被提交時或者日志緩存被占滿時,當前的日志緩存就被放入flushQueue,日志編寫器就必須開始工作。日志編寫器的工作完成后, 日志管理器將會收到一個寫入成功的信號,進而激活所有正在等待日志緩存刷新的所有進程,以繼續完成工作。

    第二部分:實例探究SQL SERVER 事務日志的產生與寫入磁盤。

    當一個更新語句被發出並獲得相關鎖以后,SQL SERVER 將先更改高速緩沖區中的相關數據頁,在更改高速緩沖區中的頁時,將會產生一條日志記錄並放到日志緩存中,當這個更新語句被提交(COMMIT)時,這條存在於當前日志緩存中的日志記錄將首先被成功刷新到磁盤上的日志文件中以后,再返回“更新成功”的確認信息到客戶端。以上是事務比較“小”的時候日志寫入的相關情況。而當事務比較“大”時,盡管事務沒有被COMMIT,而日志也會被寫入到磁盤上。 
   下面我將以實例來證明以下幾種情況: 
    A. 當事務比較“小”時,只有事務被COMMIT時,日志才會被寫入到磁盤上的日志文件中。 
    B. 當事務比較“大”時,盡管事務沒有被COMMIT,日志也會被寫入磁盤上的日志文件中。

    實例一:要證明情況A比較麻煩,因為需要在事務被開始但沒有被COMMIT時,查看磁盤上的日志文件中是否有相關的日志記錄。而SQL SERVER 雖然提供了一個未公開的查看日志記錄的命令DBCC LOG(數據庫名),但是這個命令卻會將存在於日志緩沖區內沒有實際寫入磁盤的日志記錄一並列出來。因此我不得不借助一個大家熟知的第三方工具Log Explorer。

    試驗步驟: 
    1.建立一個測試數據庫northwind,恢復模式為完整。並且對其進行一次完整備份(這樣SQL SERVER 才能將日志保存,否則將會被定期截斷),運行CHECKPOINT命令,然后再進行一次事務日志備份以截斷所有不活動的日志。運行一下命令DBCC LOG(northwind)看看情況。  
        1 
   在上圖的結果中,你將只能看到Operation分別為LOP_BEGIN_CKPT和LOP_END_CKPT的兩條日志。這兩條日志是剛剛進行“事務日志備份”而產生的兩條日志,而其它的日志已經被截斷。 

   2.測試數據庫中建立一個表TEST(ORDER_ID INT,ZDESC   VARCHAR(100) ),然后插入一條測試數據。 

復制代碼
 CREATE TABLE TEST 
    ( 
       ORDER_ID    INT, 
       ZDESC         VARCHAR(100) 
     ) 

 GO 

   INSERT INTO TEST(ORDER_ID,ZDESC)  
                  VALUES( 1,’a’) 
  GO  
復制代碼


    成功執行后,我們再運行DBCC LOG(northwind)看看日志的情況: 
    2       

  

   3.然后我們運行以下命令:

         BEGIN TRAN 
              UPDATE TEST SET ZDESC=’B’ 
              WHERE ORDER_ID=1

      該命令開始了一個事務,將ZDESC列的值更改為‘B’,但是該事務沒有被COMMIT.  
      運行DBCC LOG(northwind)查看日志的情況。 
   3 
   大家注意看第54條日志,從第54條日志開始,就是我們運行上面的UPDATE事務所產生的所有日志,由於該事務並沒有被COMMIT,我們必須想辦法查看自54號日志開始的所有日志是否已經保存到了磁盤的日志文件中。這時,我們先將SQL SERVER服務改為手動啟動,然后強行重新啟動電腦,啟動電腦后,在SQL SERVER未啟動以前,拷貝northwind的日志文件到其它目錄(雖然我們可以在電腦啟動后,SQL SERVER 服務啟動以后再次運行DBCC LOG(northwind)來查看日志的情況,但是我擔心SQLSERRVER在啟動的時候會進行恢復工作而對沒有提交的日志進行什么處理),我們還是利用Log Explorer來查看拷貝出來的日志文件中的日志記錄。 
   我們還是先DBCC LOG(northwind)看看情況: 
   4 
  大家可以看到,先前的序號64,65號日志已經看不到了,而這兩條日志,就是UPDATE語句產生的真正的日志,而54-63是進行修改工作系統內部的一些事務。然后我們再用Log Explorer來看看之前拷貝出來的日志文件中的日志。 
5

    上圖是Log Explorer顯示的備份出來northwind的日志文件的詳細情況,我們可以在上面的表格欄選中某一條日志,然后注意紅色框框出來的LSN.LSN:36:87:1這條日志即是我們用DBCC LOG(northiwind)命令所顯示的排序為64的即CurrentLSN為:00000036:00000087:0001這條日志,很明顯,Log Explorer顯示的這條日志和先前DBCC LOG(northiwind)顯示出的日志並不相同,因此我們可以斷定,一個“小”的事務在未被COMMIT以前,日志已經產生,並且存儲在日志緩沖區,但沒有寫入到磁盤的日志文件中。而一旦該事物被COMMIT,日志將一定會被寫入磁盤,這種情況各位園友可以自己去實際驗證。 


實例二:證明情況B比較簡單,我們只需要讓SQL SERVER運行一個較“大”的事務,然后觀察磁盤上日志文件有沒有被自動增長,如果增長了,那么日志肯定被寫入到磁盤上了。 

實驗步驟: 
1.觀察northiwind當前日志文件的大小。因為我的northwind是剛剛新建的數據庫,日志文件的物理大小為1M. 
2.運行以下腳本,然后在觀察日志文件的物理大小。 

復制代碼
   BEGIN TRAN 
       DECLARE @I  INT 
       SET @I=1 
       WHILE(@I<99999) 
            BEGIN 
                 UPDATE TEST SET ZDESC=LEFT(NEWID(),10) 
                 SET @I=@I+1 
            END 
復制代碼


  該腳本被封裝成一個事務,並且沒有被提交(COMMIT),運行完成后,我觀察到的日志文件的物理大小為38.3M,如下圖: 
6 
很明顯,盡管該事務沒有被提交(COMMIT),但是,只要日志緩沖區被填滿,日志緩存中的日志就會被寫入到物理磁盤上。及時我們回滾了該事務,我們依然可以用DBCC LOG(northwind)看到這些被回滾的日志。 


第三部分:其它一些相關問題的思考 
    1.當SQL SERVER修改高速緩沖區中的數據頁時,日志便會產生,並放入到日志緩存。那么日志究竟是由緩沖區管理器產生后交給日志管理器,還是由日志管理器探測到緩沖區的修改,然后自己產生日志。 
    2.日志緩沖中的日志,究竟要“編排”成什么格式?是否就是我們通過DBCC LOG(northwind)所看到的格式。 
    3.日志編寫器在講日志寫入磁盤時,如何知道該寫到日志文件的哪一個VLF,也就是說它是如何知曉某個VLF是邏輯上的最后一個VLF。

 

后文

關於Virtual Log Files(虛擬日志文件)參考這里

關於Write-Ahead Logging (預寫日志)參考這里

轉自:https://www.cnblogs.com/zhouqiang52154/archive/2009/06/20/1507488.html

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM