SQL Server數據庫的存儲過程當中定義的臨時表,真的有必要顯式刪除(drop table #tableName)嗎?

 

本文出處:http://www.cnblogs.com/wy123/p/6704619.html html

 

 

問題背景程序員

在寫SQL Server存儲過程當中,若是存儲過程當中定義了臨時表,
有些人習慣在存儲過程結束的時候一個一個顯式地刪除過程當中定義的臨時表(drop table #tName),有些人又沒有這個習慣,
對於不明真相的羣衆或者喜歡思考的人會問,存儲過程當中定義的臨時表,最後要不要主動刪除,爲何?
或者說是否是存儲過程結束的時候刪除臨時表更加規範?
不止一我的問過這個問題了,說實在話,本人以前確實不清楚,只是認爲,顯式刪掉或者不刪都行,臨時表在當前Session斷開以後會自動釋放
那麼存儲過程當中定義的臨時表,在使用完以後,到底刪仍是不刪?顯式刪除與不作刪除有無區別?
本文將對此問題進行一個粗淺的分析,若有不對的地方,還望指出,謝謝。sql

 

存儲過程當中臨時表的表結構也有緩而且會被重用數據庫

那麼到底需不須要顯式刪除,顯式刪除或者是不刪除有什麼區別?
這中間涉及到一個臨時表緩存的知識點,首先看什麼是臨時表的緩存。
緩存臨時表是SQL SERVER 2005以來的一個新特性,
臨時表的建立時須要往臨時庫系統表中寫入數據(元數據,臨時表的表結構信息),跟普通的增刪改操做同樣,這個過程須要必定的資源消耗
在知足必定條件的狀況下(後面說須要知足的條件是什麼),
每當用戶請求完成以後(固然這個用戶請求的SQL中包含了臨時表),臨時表的元數據將會保存在臨時庫(tempdb)的系統表中
雖然在用戶看來,當前Session建立的臨時表,對其餘Session事不可見的,在Session斷開或者臨時表被刪除(drop)以後,將不可訪問。
可是當新的Session調用一樣的包含了建立臨時表的代碼,SQL Server內部會重用以前Session執行時建立過的臨時表,而無需再次定義臨時表。
這樣的話能夠節約一些建立表的步驟所消耗的資源。緩存

 

上面是理論,下面來作個小實驗演示上面的理論,首先來看不一樣Session之間臨時表「重用」的現象。
首先這裏要藉助系統視圖sys.dm_os_performance_counters 來判斷臨時表的建立次數,該系統表中計數器的名稱爲:Temp Tables Creation Rate。

建立以下存儲過程,存儲過程當中定義了一個臨時表,服務器

create procedure Proc_TestTempTable
as
begin
    
    create table #t20170413
    (
        col_1 varchar(100) ,
        col_2 varchar(100)
    )

    insert into #t20170413 values ('aaa','bbb');

    select * from #t20170413
    --select * from tempdb.sys.tables where name like '#t20170413%'
end

在存儲過程建立以後,第一次執行的時候,來觀察一個現象,以下截圖併發

很明顯,sys.dm_os_performance_counters系統表中的Temp Tables Creation Rate計數器加了1,也就是說在執行存儲過程當中過程當中發生了一次臨時表的建立動做
而後繼續再次執行上面的代碼sqlserver

一樣的代碼,這一次sys.dm_os_performance_counters系統表中的Temp Tables Creation Rate計數器沒有加1,
爲何明明是存儲過程當中定義了臨時表,上面執行一次,Temp Tables Creation Rate加1,而後再次執行就不加1了?
這個就是臨時表重用的現象(嚴格說是臨時表的表結構或者表定義,而不包含數據),
由於第一次執行存儲過程的時候建立了臨時表,而後再次執行存儲過程的時候就重用了第一次的臨時表。  性能

  那怎麼證實該存儲過程第二次執行的時候重用了第一次建立的臨時表?
  對存儲過程稍做修改,存儲過程當中加一句代碼,查詢臨時庫中該臨時表信息測試

  而後執行兩次以下代碼,下面截圖是第二次執行的結果(下面會作解釋爲何是第二次的執行的結果),
  在臨時表被重用的時候查詢出來當前臨時表的信息,發現臨時表建立次數並無增長,也就是說臨時表被重用了

  既然說臨時表重用了,那麼臨時表必定存在於臨時庫的系統表中,那麼如何證實這個存儲過程的臨時表在臨時庫中呢?
  上面顯示的臨時表的Id是-1297292959,那麼這裏就臨時庫中查詢Id = -1297292959的表信息,發現果真存在這個一張表。
  臨時庫中的這個表信息除了名字和modify_date不同,modify_date據觀察是臨時表被重用的時間,也就是臨時表被重用一次就修改一次modify_date
  其餘信息徹底一致,這就是說明,存儲過程第一次執行完成以後,它所建立的臨時表被緩存了起來(至於名字不一樣,後面再解釋),
  當再次執行該存儲過程的時候能夠重用第一次執行存儲過程時候建立的臨時表的表結構。

 

存儲過程當中顯式刪除臨時表,到底有沒有用處?

對上面的存儲過程作以下修改,在存儲過程結束以前顯式刪除定義的臨時表

  而後再次執行以下的測試代碼,注意截圖是第二次執行的結果(下面會作解釋爲何是第二次的執行的結果)

  而後繼續在臨時庫的系統表中查詢上述Id的系統,發現臨時表依舊存在於系統表中,即使是存儲過程當中顯式刪除(drop table #t20170413)

  這裏說明,即使在存儲過程當中顯式調用了刪除臨時表的操做,臨時表依舊會存在得臨時庫的系統表中,也就是說臨時表依舊會被緩存。
  並不會由於在存儲過程當中顯式刪除而真正的刪除,臨時表對象會緩存在臨時庫的系統表中。
  之因此Session中查詢到的臨時表的名字與系統表中查詢到的臨時表的名字不一樣,
  緣由是臨時表從建立到緩存(當前Session斷開以後),在內部只是發生了一個對當前Session臨時表重命名的過程。
  被緩存的臨時表的重用的過程與上面的相似,也是將緩存的換反向重命名。

事實證實:
對於存儲過程的臨時表,在知足可緩存的前提下(只是表結構,固然不包括臨時表的數據),
你刪,或者不刪,他都會緩存在臨時庫中,並不由於顯式Drop臨時表,臨時表就會被真正的刪除,這是SQL Server專門爲此作的優化,你真的不用爲刪除臨時表而操心或者糾結
這裏回到一開始的問題,存儲過程當中有沒有必要顯式刪除臨時表就有答案了:對於存儲過程的建立的臨時表,不必刪除,對於知足可緩存的臨時表對象,想刪也刪不掉!

 

存儲過程當中定義的臨時表,只有知足必定的條件,纔會被緩存重用

  上面說了,臨時表的重用是要知足必定條件的,以下條件將會致使臨時表沒法重用

1,建立臨時表的時候存在命名約束(這一點很是操蛋,不只僅是緩存問題,曾經遇到過坑,有機會演示)
2,在臨時表建立以後執行DDL操做,好比建立索引等,可是這個DDL不包括drop 臨時表和truncate臨時表
3,動態SQL方式建立的臨時表
4,在不一樣的範圍以內建立的臨時表,應該是存儲過程調用另一個存儲過程,另一個存儲過程定義的臨時表,這一點尚未具體研究
5,存儲過程以WITH RECOMPILE重編譯的方式運行


  好比在上面的存儲過程,在臨時表定義以後,建立一個索引,
  此舉將會形成臨時表沒法重用,這種狀況下,無論你刪或者不刪,存儲過程執行完成Session斷開以後,臨時表都不會緩存(在臨時庫中)
  這一點就不截圖演示了,有興趣的本身測試

 

  解釋另一個問題:
  既然認爲沒法刪除緩存的臨時表,正常狀況下,緩存的臨時表什麼狀況下會被刪除?
  上面說截圖都是第二次運行的截圖,由於在存儲過程重建以後(create或者alter),這個存儲過程當中定義的臨時表都會被清理掉
  只有重建了存儲過程,第一次執行以後,緩存的臨時表在第二次執行的時候才能被重用
  固然這一點也和容易驗證,緩存臨時表以後,而後alter 存儲過程,
  而後根據緩存臨時表的Id去查詢臨時庫中sys .tables的信息,這個緩存的表會在1~2秒以後被刪除(我的測試驗證過)
  另外顯式執行DBCC FREEPROCCACHE,也能刪除緩存的臨時表。
  其實也不難理解,緩存的對象是跟執行計劃緩存綁定的,若是執行計劃自己就不存在了,那麼緩存的臨時表對象也將會被請處理。

 

併發執行的狀況下,臨時表可否重用?

  併發線程之間固然不會重用同一個臨時表,若是不是這樣的話,SQL Server也不用混江湖了,併發的每一個線程會建立本身的臨時表。
  參考以下截圖是在併發狀況下,tempdb產生的臨時表的狀況,每一個線程調用存儲過程產生的臨時表後綴都是不同的。
  併發調用存儲過程的時候,每一個線程會產生屬於本身的臨時表,重用臨時表是發生在當前線程執行完成以後,其餘Session從新調用存儲過程時候才能重用已緩存的臨時表。
  鑑於本文不是專門說明臨時表的,這裏就很少說了。

  

 

顯式刪除臨時表與否的性能測試

既然上面說了,若是存儲過程當中定義的臨時表知足臨時表被緩存的條件的狀況下,存儲過程當中是否刪除臨時表,臨時表都同樣會被緩存
那麼,若是真的指定了顯式刪除臨時表操做,與沒有顯式指定刪除臨時表,性能上有沒有差異呢?
抱着以數聽說話的態度,分別在存儲過程當中不刪除與顯式刪除臨時表,用SQLQueryStress作了一系列的性能測試
結果以下

不顯式刪除臨時表 顯式刪除臨時表

測試結果以下, 

  

  測試過程部分截圖(不浪費博客園的圖片服務器資源了,隨便截了兩張)

  

  從測試結果看,確實有一些差別,不過這個差異是很是小的,
  第一組測試結果5000次調用產生了0.07秒的差距
  第二組測試結果20000次調用產生了0.35秒的差距,平均到一次差距也就在微妙級,即使是顯式調用刪除,對性能來講是有一點點影響,不過這個影響也是無傷大雅。
  不過這個內部的原始必定要弄清楚,有沒有必要刪除,以及緣由,這個纔是原則性的問題!

    至於臨時表數據佔用的空間,也不是說顯式刪除就釋放,不刪除就不釋放,應該是有後臺進程來作這個工做的,我的建議不用爲這個問題瞎操心。
  寫存儲過程的時候,多寫一點好一點的SQL語句,比糾結這個強多了。

多囉嗦一句:
有些人的觀念是根深蒂固的,對於習慣刪除臨時表的人,以爲這麼作「規範」,「專業」,雖然他沒有確切的理由說明顯式刪除臨時表的必要性。
可是你要跟他說不必刪除臨時表,必定會激怒他,好多程序員都是這樣的,你否定他根深蒂固的一個觀點的時候,他是很惱火的。
從生物學上說,這個是屬於「印隨行爲」,如宗教般,在本身處於懵懂期的時候,受到一些說法的影響
或許是當初的師傅說的,或者膜拜的對象這麼作了,或者聽高人說過這麼作比較好,而後本身就一直這麼作了而且堅信不疑。
固然,包括我本身在某些時候也有此種行爲,思惟被曾經的某一些經歷固化,而後一直束縛本身的認知。
不過對於無傷大雅的問題,就隨他去了,不必說服他,弄很差他反過來以爲你業餘,但願小夥伴們明辨,好彷佛跑題了……

 

顯式刪除臨時表與否與臨時庫空間釋放問題

  有人擔憂說,若是不顯式刪除臨時表,是否是臨時表佔用的空間沒法快速釋放?
  其實也不用顧慮,仍是以數聽說話,這裏對比兩個同樣的存儲過程,一個不顯式刪除臨時表,一個顯示刪除臨時表,看看臨時數據庫中用戶對象佔用page的狀況
  不顯式刪除臨時表的存儲過程
  作以下對比測試,藉助SQLQueryStress,作一個20線程,每一個線程500次循環的測試

      

  測試的過程當中,在臨時數據庫中,利用以下SQL,間隔一秒的頻率抓取臨時庫中user objects對象的數據

  

  把上述腳本記錄到的數據,利用Excel的透視圖功能,呈現出來上述腳本記錄到的user objects數量,能夠很清楚地發現,不顯式刪除臨時表,與顯式刪除臨時表相比,UserObjecs數量並無明顯的差別
  也就數說,不顯式刪除臨時表的狀況下,並無出現臨時表空間對象釋放不及時的狀況

  

   所以大可沒必要擔憂,不顯式刪除臨時表,臨時表申請的空間沒法及時釋放。

  

總結:   

  本文從存儲過程當中的臨時表是否須要顯式刪除入手,簡單介紹了臨時表重用的現象和前提條件,以及有無必要顯式刪除臨時表,
  同時測試了臨時表在知足重用的狀況下,臨時表顯式刪除與否的性能問題,對於存儲過程當中定義的臨時表,無論是否可否緩存重用,都不建議顯式刪除。

 

參考連接:https://www.mssqltips.com/sqlservertip/4406/sql-server-temporary-table-caching/
     http://sqlblog.com/blogs/paul_white/archive/2012/08/17/temporary-object-caching-explained.aspx

      http://www.davewentzel.com/content/do-you-explicitly-drop-your-temp-tables

相關文章
相關標籤/搜索