視圖后面加with(nolock)后


      一些小的企業項目,往往存在很多局限性,這里講的局限性是指這些項目在面對某些需求時,所選用的解決方案不多。就拿我目前處理這個來講吧,說的通俗點就是為業務部門生成相關報表。我目前理解的報表生成方式,總結如下:
     
      第一:在業務庫上執行SQL語句或者存儲過程,實時生成數據。這其中又分為三種:
     
       1:在主庫上操作,及業務系統操作的數據庫與生成報表的SQL邏輯同處一個數據庫。
       2: 在主庫的只讀庫即從庫上操作,一般可以通過訂閱復制來設置主從庫,產生報表的邏輯只操作從庫。
       3: 主庫定時生成一個備份庫,報表從生成的備份庫中操作。
      
      第二:由數據倉庫生成相應報表。
          這部分在這里就不多說了。
         
      我目前的項目就屬於第一種的第一小分類,即直接在主庫上以執行SQL方式產生數據,其中的原因也不用多說,低成本。但由於生成報表的需要的時間一般都較長,容易對業務表進行長時間的鎖定,影響業務系統的正常使用,反過來,如果用戶正在操作業務庫,此時如果同時執行報表生成,就會形成鎖等待。為此決定對報表相關查詢表進行無鎖操作,即在表后面加 with(nolock),但我們為客戶端是以視圖形式提供,為此產生一個問題:在視圖后面加with(nolock),是否意味着視圖內的所有表都是nolock?
     
      我在網上沒找到相關內容,於是還是眼見為實,決定做個小實驗。重點需要搞清楚如下幾個問題:
     
      問題一:如何人為造成一個鎖?即讓某個表被某個線程長時間鎖定。


      我們可以BEGIN TRAN mytran,然后操作一個表,比如update,但不commit tran,此時被操作的表就會被當前線程長時間鎖定。我們可以用sp_who2來查詢鎖定情況,如下圖所示,第一個框代表狀態,第二個框是CPUTime。還可以利用sp_lock查看鎖的類型。


      
      問題二:如何制造臟讀的情況,我們都說加了nolock,容易形成臟讀,但臟讀到底怎么樣呢?
     
      1:創建一個測試用的表:
      

-- 創建表
CREATE  TABLE mytest
    (
      id  INT  IDENTITY
              NOT  NULL ,
      data  UNIQUEIDENTIFIER  DEFAULT (  NEWID() )
                             NOT  NULL
    )

 

    這里需要創建一個聚焦索引,如果不創建此聚焦索引,后續的結果會發生變化。
    

-- 創建索引
CREATE  UNIQUE  CLUSTERED  INDEX cidx  ON mytest( data )

 

     2:為表添加一些測試數據。
   

-- 添加數據
DECLARE  @i  INT ;
SET  @i  =  1 ;
WHILE  @i  <=  50000 
     BEGIN
         INSERT  mytest
                 DEFAULT  VALUES  
         SET  @i  =  @i  +  1
     END

    

     3:創建測試視圖:
    

CREATE  VIEW view_mytest
AS
   SELECT  *  FROM mytest
go

 

    4:創建一個嵌套視圖,嵌套視圖的目的就是想看看在最上層視圖上加nolock,是否會傳遞到最底層的基表。
   

CREATE  VIEW view_mytest2
AS
   SELECT a. *  FROM view_mytest a
   LEFT  JOIN dbo.Report b
   ON a.id =b.id
go

 

     5:在MSMS中打開兩個窗口:
      窗口一:執行如下腳本,提交一個事務,更新測試表,但不提交,形成長時間鎖定。
      

BEGIN  TRAN mytran
UPDATE  mytest
SET     data  =  NEWID()

 

      窗口二:對測試表進行行數統計,當發現數據出現錯誤時,打印出提示信息。
      

View Code
DECLARE  @totalrows  INT ,
     @currentnow  INT ,
     @errorcount  TINYINT
SET  @errorcount  =  0
SELECT   @totalrows  =  COUNT( 1)
FROM    mytest 
WHILE  1  =  1 
     BEGIN
         SELECT   @currentnow  =  COUNT( 1)
         FROM    view_mytest2  WITH ( NOLOCK )  
         IF  @totalrows  <>  @currentnow 
             BEGIN
                 PRINT  ' 查詢總數=  '  +  CAST( @currentnow  AS  VARCHAR( 10))  +  '  差異數量=  '
                     +  CAST( @currentnow  -  @totalrows  AS  VARCHAR( 10))
                 SET  @errorcount  =  @errorcount  +  1
                 IF  @errorcount  >=  8 
                     BREAK
             END
     END

 

      6:查看窗口二,如果nolock,理論上應該會出現錯誤提示信息,出現此錯誤提示信息就是因為在無鎖情況下有可能出現多讀或者是少讀的現象。


    

    

      上圖表示,在視圖上添加nolock跟在基表后加nolock效果一樣,只不過在視圖上加就相當於視圖內所有表都無鎖,這樣不太靈活,盡管有臟讀現象,但我們的業務邏輯一般都是多表相匹配后的情況,如果一個事務中會操作多個表,生成報表時,某些表形成了臟讀,也不會影響最終數據,如果所有表都操作完畢,還未提交事務,此時雖然查詢出的數據可能是錯誤數據,但屬於可接受范圍之內。

  

      文中若不錯誤地方望批評指正。

   

      參考資料:http://www.dotblogs.com.tw/ricochen/archive/2011/04/15/22758.aspx

      

   
      


免責聲明!

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



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