Hangi sorgunun tempdb işlem günlüğünü doldurduğunu nasıl belirleyebilirim?


65

TEMPDB veritabanının işlem günlüğünü dolduran tam sorguyu veya saklı proc'ları nasıl tanımlayacağımı bilmek istiyorum.



Bu sitede yeniyim ve gönderinin nasıl düzenleneceğinden emin değilim. Daha fazla bilgi vermek için PROD'a erişimim yok. Tüm PROD DBA’dan duyduğum, kodunuz tempdb’yi dolduruyor! Kodumuzun tempdb günlüğünü doldurmadığından emin olmak için izlenecek en iyi kodlama uygulamaları var mı?

@prasanth Buradaki sorunuzu değiştirebilmek için aynı açıklığa sahip olarak bu siteye kaydolmanız gerekecek. Kodunuzun neden tempdb kullandığı konusunda ne yaptığına bağlı. Yürütme planı ne yaptığını göstermelidir ve gerçek kodu kaydederseniz geliştirmeye yardımcı olabiliriz.
Cade Roux,

@CadeRoux Sorguyu (veya sorguları) tanımlamaya çalıştığını, belirli bir bilinen sorgunun soruna neden neden olduğunu anlamaya çalışmadığını düşünüyorum.
Aaron Bertrand

@AaronBertrand evet, ancak yorum kodlama için en iyi uygulamaları istediğini gösteriyor gibi görünüyor.
Cade Roux,

Yanıtlar:


73

Gönderen http://www.sqlservercentral.com/scripts/tempdb/72007/

;WITH task_space_usage AS (
    -- SUM alloc/delloc pages
    SELECT session_id,
           request_id,
           SUM(internal_objects_alloc_page_count) AS alloc_pages,
           SUM(internal_objects_dealloc_page_count) AS dealloc_pages
    FROM sys.dm_db_task_space_usage WITH (NOLOCK)
    WHERE session_id <> @@SPID
    GROUP BY session_id, request_id
)
SELECT TSU.session_id,
       TSU.alloc_pages * 1.0 / 128 AS [internal object MB space],
       TSU.dealloc_pages * 1.0 / 128 AS [internal object dealloc MB space],
       EST.text,
       -- Extract statement from sql text
       ISNULL(
           NULLIF(
               SUBSTRING(
                 EST.text, 
                 ERQ.statement_start_offset / 2, 
                 CASE WHEN ERQ.statement_end_offset < ERQ.statement_start_offset 
                  THEN 0 
                 ELSE( ERQ.statement_end_offset - ERQ.statement_start_offset ) / 2 END
               ), ''
           ), EST.text
       ) AS [statement text],
       EQP.query_plan
FROM task_space_usage AS TSU
INNER JOIN sys.dm_exec_requests ERQ WITH (NOLOCK)
    ON  TSU.session_id = ERQ.session_id
    AND TSU.request_id = ERQ.request_id
OUTER APPLY sys.dm_exec_sql_text(ERQ.sql_handle) AS EST
OUTER APPLY sys.dm_exec_query_plan(ERQ.plan_handle) AS EQP
WHERE EST.text IS NOT NULL OR EQP.query_plan IS NOT NULL
ORDER BY 3 DESC;

DÜZENLE

Martin, bir yorumda belirtildiği gibi, bu , tempdb'de yer kaplayan aktif işlemleri bulamayacak , sadece şu anda orada yer alan aktif sorguları bulacaktır (ve mevcut günlük kullanımı için büyük olasılıkla). Bu nedenle açık bir işlem olabilir, ancak soruna neden olan gerçek sorgu artık çalışmıyor.

Sen değişebilir inner joinüzerinde sys.dm_exec_requestsbir karşı left outer joino zaman şu anda aktif olarak sorgular yayınlanmıyor oturumları için satırları döndürür.

Martin sorgusu yayınlandı ...

SELECT database_transaction_log_bytes_reserved,session_id 
  FROM sys.dm_tran_database_transactions AS tdt 
  INNER JOIN sys.dm_tran_session_transactions AS tst 
  ON tdt.transaction_id = tst.transaction_id 
  WHERE database_id = 2;

... session_idlog alanını işgal eden aktif işlemlere sahip olan s'leri tanımlar , ancak soruna neden olan gerçek sorguyu belirleyemezsiniz, çünkü eğer şimdi çalışmıyorsa yukarıdaki sorguda yakalanmayacaktır. aktif istekler En son sorguyu kullanarak reaktif olarak kontrol edebilirsiniz, DBCC INPUTBUFFERancak ne duymak istediğinizi size söylemeyebilir. Aktif olarak çalışanları yakalamak için benzer şekilde dış birleştirebilirsiniz, örneğin:

SELECT tdt.database_transaction_log_bytes_reserved,tst.session_id,
       t.[text], [statement] = COALESCE(NULLIF(
         SUBSTRING(
           t.[text],
           r.statement_start_offset / 2,
           CASE WHEN r.statement_end_offset < r.statement_start_offset
             THEN 0
             ELSE( r.statement_end_offset - r.statement_start_offset ) / 2 END
         ), ''
       ), t.[text])
     FROM sys.dm_tran_database_transactions AS tdt
     INNER JOIN sys.dm_tran_session_transactions AS tst
     ON tdt.transaction_id = tst.transaction_id
         LEFT OUTER JOIN sys.dm_exec_requests AS r
         ON tst.session_id = r.session_id
         OUTER APPLY sys.dm_exec_sql_text(r.plan_handle) AS t
     WHERE tdt.database_id = 2;

Ayrıca, sys.dm_db_session_space_usagegenel alan kullanımını oturumdan görmek için DMV'yi de kullanabilirsiniz (ancak sorgu için geçerli sonuçları geri alamayabilirsiniz; sorgu etkin değilse, geri aldığınız şey asıl suçlu olmayabilir).

;WITH s AS
(
    SELECT 
        s.session_id,
        [pages] = SUM(s.user_objects_alloc_page_count 
          + s.internal_objects_alloc_page_count) 
    FROM sys.dm_db_session_space_usage AS s
    GROUP BY s.session_id
    HAVING SUM(s.user_objects_alloc_page_count 
      + s.internal_objects_alloc_page_count) > 0
)
SELECT s.session_id, s.[pages], t.[text], 
  [statement] = COALESCE(NULLIF(
    SUBSTRING(
        t.[text], 
        r.statement_start_offset / 2, 
        CASE WHEN r.statement_end_offset < r.statement_start_offset 
        THEN 0 
        ELSE( r.statement_end_offset - r.statement_start_offset ) / 2 END
      ), ''
    ), t.[text])
FROM s
LEFT OUTER JOIN 
sys.dm_exec_requests AS r
ON s.session_id = r.session_id
OUTER APPLY sys.dm_exec_sql_text(r.plan_handle) AS t
ORDER BY s.[pages] DESC;

Tüm bu soruları emrinizde, kimin tempdb kullandığını ve özellikle eylemde onları yakalarsanız, nasıl daraltabilirsiniz.

tempdb kullanımını en aza indirmek için bazı ipuçları

  1. #temp tablolarını ve @table değişkenlerini kullanın
  2. eşzamanlı indeks bakımını en aza indirin ve SORT_IN_TEMPDBgerekmediği takdirde seçeneği önleyin
  3. gereksiz imleçlerden kaçının; Bunun bir tıkanıklık olabileceğini düşünüyorsanız statik imleçlerden kaçının, çünkü statik imleçler tempdb'de çalışma masaları kullanır - bu imleç türü olsa da, tempdb bir darboğaz değilse her zaman tavsiye ederim
  4. makaralardan kaçınmaya çalışın (örneğin, sorguda birden çok kez referans verilen büyük CTE'ler)
  5. MARS kullanma
  6. Anlık görüntü / RCSI izolasyon seviyelerinin kullanımını iyice test edin - NOLOCK'tan daha iyi olduğu söylendiğinden yalnızca tüm veritabanları için açmayın (bu ücretsizdir)
  7. Bazı durumlarda, sezgisel görünmeyebilir, ancak daha fazla geçici tablo kullanın . Örneğin, çok sayıda soruyu parçalara ayırmak biraz daha az etkili olabilir, ancak tempdb'ye çok büyük bir bellek dökülmesini engelleyebilirse, daha büyük olan tek bir sorgu çok büyük bir bellek ...
  8. toplu işlemler için tetikleyicileri etkinleştirmekten kaçının
  9. LOB türlerinin (max türleri, XML, vb.) yerel değişkenler olarak kullanılmasını önlemek
  10. işlemleri kısa ve tatlı tutmak
  11. tempdb'yi herkesin varsayılan veritabanı olacak şekilde ayarlama -

Ayrıca, tempdb günlük kullanımınızın, üzerinde çok az kontrol sahibi olduğunuz veya hiç kontrolünüz olmayan dahili işlemlerden kaynaklanabileceğini de düşünebilirsiniz - örneğin, veritabanı postası, olay bildirimleri, sorgu bildirimleri ve hizmet komisyoncusu hepsi bir şekilde tempdb kullanır. Bu özellikleri kullanmayı bırakabilirsiniz, ancak bunları kullanıyorsanız tempdb'yi nasıl ve ne zaman kullandıklarını belirleyemezsiniz.


Bağlantı Aaron için teşekkürler. Genel olarak, TEMPDB Transaction loglarının doldurulmasını önlemek için takip edilmesi gereken en iyi kodlama uygulamaları var mı?

2
Hmm, Sadece bunu test ettim ve session_idaşağıdaki sorgu ile ortaya çıksa da, rahatsız edici oturumumu bulamadım SELECT database_transaction_log_bytes_reserved,session_id FROM sys.dm_tran_database_transactions tdt JOIN sys.dm_tran_session_transactions tst ON tdt.transaction_id = tst.transaction_id WHERE database_id = 2. Bulmayı beklediğim sorgu aşağıdakileri çalıştırdıktan sonraydıBEGIN TRAN CREATE TABLE #T(X CHAR(8000)) INSERT INTO #T SELECT name FROM sys.objects
Martin Smith

@Martin: Cte'de sonuçları @@ mevcut SPID ile sınırlayacak bir @@ SPID olduğunu fark ettim. Tüm oturumlarda yayılmasını istiyorsanız, onu kaldırın.
Ben Thul

@BenThul - Sorguyu başka bir bağlantıda çalıştırdım. @@SPIDOlduğu <>değil =. spid için benim için tüm sütunlar için açık işlem dm_db_task_space_usageraporları 0. Açık bir işlemle boşta değil, istek aslında yürütülürken sorgulamanız gerekip gerekmediğini merak edin.
Martin Smith

@MartinSmith, sorgu aktif işlemleri değil, yalnızca aktif istekleri bulur. Bu nedenle, sorgu artık çalışmıyorsa, haklısınız, işlem DMV'lerini kullanarak takip edebilirsiniz. Ancak, artık çalışmıyorsa, soruna neden olan sorguyu çözemezsiniz - aynı spid mevcut işlemde başka bazı ifadeler yayınlamış olabilir.
Aaron Bertrand

5

https://social.msdn.microsoft.com/Forums/sqlserver/en-US/17d9f862-b9ae-42de-ada0-4229f56712dc/tempdb-log-filling-cannot-find-how-or-what?forum=sqldatabaseengine

 SELECT tst.[session_id],
            s.[login_name] AS [Login Name],
            DB_NAME (tdt.database_id) AS [Database],
            tdt.[database_transaction_begin_time] AS [Begin Time],
            tdt.[database_transaction_log_record_count] AS [Log Records],
            tdt.[database_transaction_log_bytes_used] AS [Log Bytes Used],
            tdt.[database_transaction_log_bytes_reserved] AS [Log Bytes Rsvd],
            SUBSTRING(st.text, (r.statement_start_offset/2)+1,
            ((CASE r.statement_end_offset
                    WHEN -1 THEN DATALENGTH(st.text)
                    ELSE r.statement_end_offset
            END - r.statement_start_offset)/2) + 1) AS statement_text,
            st.[text] AS [Last T-SQL Text],
            qp.[query_plan] AS [Last Plan]
    FROM    sys.dm_tran_database_transactions tdt
            JOIN sys.dm_tran_session_transactions tst
                ON tst.[transaction_id] = tdt.[transaction_id]
            JOIN sys.[dm_exec_sessions] s
                ON s.[session_id] = tst.[session_id]
            JOIN sys.dm_exec_connections c
                ON c.[session_id] = tst.[session_id]
            LEFT OUTER JOIN sys.dm_exec_requests r
                ON r.[session_id] = tst.[session_id]
            CROSS APPLY sys.dm_exec_sql_text (c.[most_recent_sql_handle]) AS st
            OUTER APPLY sys.dm_exec_query_plan (r.[plan_handle]) AS qp
    WHERE   DB_NAME (tdt.database_id) = 'tempdb'
    ORDER BY [Log Bytes Used] DESC
GO

4

Bu yazı için teşekkür ederim, muhtemelen türünün tek örneği. Testim basitti, geçici bir tablo oluşturun ve bu yazıdaki soruların herhangi birini çalıştırdığımda göründüğünden emin olun ... yalnızca bir ya da iki kişi gerçekten başarılı oldu. T-SQL'e katılması için düzelttim, daha uzun süreler için optimize ettim ve oldukça faydalı oldu. Bir şeyleri kaçırdıysam bana bildirin, ancak şimdiye kadar otomatikleştirilmiş / kodlanmış bir komut dosyası aldınız. Aşağıdaki standart sapma (STDEV) sorgusu kullanılarak, hangi sorgunun / SPID'nin belirli bir süre boyunca suçlu olduğunu belirlemek için bir yol sunar.

Bu işlem 3 dakikada bir 40 kez, yani 2 saat boyunca çalışır. Uygun gördüğünüz gibi parametreleri değiştirin.

Aşağıda, çok sayıda küçük tablonuz olması durumunda, kişilerin temizlemek isteyebileceği bir NEREDE> 50 sayfa filtresi vardır. Aksi takdirde, bu nüansı aşağıda olduğu gibi yakalayamazsınız ...

Keyfini çıkarın!

DECLARE @minutes_apart INT; SET @minutes_apart = 3
DECLARE @how_many_times INT; SET @how_many_times = 40
--DROP TABLE tempdb..TempDBUsage
--SELECT * FROM tempdb..TempDBUsage
--SELECT session_id, STDEV(pages) stdev_pages FROM tempdb..TempDBUsage GROUP BY session_id HAVING STDEV(pages) > 0 ORDER BY stdev_pages DESC

DECLARE @delay_string NVARCHAR(8); SET @delay_string = '00:' + RIGHT('0'+ISNULL(CAST(@minutes_apart AS NVARCHAR(2)), ''),2) + ':00'
DECLARE @counter INT; SET @counter = 1

SET NOCOUNT ON
if object_id('tempdb..TempDBUsage') is null
    begin
    CREATE TABLE tempdb..TempDBUsage (
        session_id INT, pages INT, num_reads INT, num_writes INT, login_time DATETIME, last_batch DATETIME,
        cpu INT, physical_io INT, hostname NVARCHAR(64), program_name NVARCHAR(128), text NVARCHAR (MAX)
    )
    end
else
    begin
        PRINT 'To view the results run this:'
        PRINT 'SELECT * FROM tempdb..TempDBUsage'
        PRINT 'OR'
        PRINT 'SELECT session_id, STDEV(pages) stdev_pages FROM tempdb..TempDBUsage GROUP BY session_id HAVING STDEV(pages) > 0 ORDER BY stdev_pages DESC'
        PRINT ''
        PRINT ''
        PRINT 'Otherwise manually drop the table by running the following, then re-run the script:'
        PRINT 'DROP TABLE tempdb..TempDBUsage'
        RETURN
    end
--GO
TRUNCATE TABLE tempdb..TempDBUsage
PRINT 'To view the results run this:'; PRINT 'SELECT * FROM tempdb..TempDBUsage'
PRINT 'OR'; PRINT 'SELECT session_id, STDEV(pages) stdev_pages FROM tempdb..TempDBUsage GROUP BY session_id HAVING STDEV(pages) > 0 ORDER BY stdev_pages DESC'
PRINT ''; PRINT ''

while @counter <= @how_many_times
begin
INSERT INTO tempdb..TempDBUsage (session_id,pages,num_reads,num_writes,login_time,last_batch,cpu,physical_io,hostname,program_name,text)
    SELECT PAGES.session_id, PAGES.pages, r.num_reads, r.num_writes, sp.login_time, sp.last_batch, sp.cpu, sp.physical_io, sp.hostname, sp.program_name, t.text
    FROM sys.dm_exec_connections AS r
    LEFT OUTER JOIN master.sys.sysprocesses AS sp on sp.spid=r.session_id
    OUTER APPLY sys.dm_exec_sql_text(r.most_recent_sql_handle) AS t
    LEFT OUTER JOIN (
        SELECT s.session_id, [pages] = SUM(s.user_objects_alloc_page_count + s.internal_objects_alloc_page_count) 
        FROM sys.dm_db_session_space_usage AS s
        GROUP BY s.session_id
        HAVING SUM(s.user_objects_alloc_page_count + s.internal_objects_alloc_page_count) > 0
    ) PAGES ON PAGES.session_id = r.session_id
    WHERE PAGES.session_id IS NOT NULL AND PAGES.pages > 50
    ORDER BY PAGES.pages DESC;
PRINT CONVERT(char(10), @counter) + ': Ran at: ' + CONVERT(char(30), GETDATE())
SET @counter = @counter + 1
waitfor delay @delay_string
end

Bunu kabul edilen cevap ile birleştirmek, kaçmak tempdb aktivitesini izlemek için uygun bir yoldur. Bunu bir SQL Agent zamanlanmış görevi ile çalıştırmak, SSMS kapalı olsa bile çalışmayı sürdürecektir. Paylaşım için teşekkürler!
Lockszmith 11:18

1

Ne yazık ki tempDB günlüğü, çalışan işlemleri görüntüleyerek doğrudan sessionID'lere geri izlenemez.

TempDB günlük dosyasını, tekrar önemli ölçüde büyüyecek bir noktaya kadar küçültün. Ardından, günlük büyümesini yakalamak için genişletilmiş bir etkinlik oluşturun. Tekrar büyüyünce, genişletilmiş olayı genişletebilir ve paket olay dosyasını görüntüleyebilirsiniz. Dosyayı açın, bir zaman filtresi, dosya türü filtresi (veri sonuçlarının dahil edilmesini istemiyorsunuz) ekleyin ve ardından SSMS'de oturum kimliğine göre gruplandırın. Bu, oturum kimliklerini en çok gruba ait olanları ararken aradığınız kişiyi bulmanıza yardımcı olacaktır. Elbette oturum kimliğinde çalışanları başka bir işlem veya araç aracılığıyla toplamalısınız. Belki birileri query_hash sütundan sorguyu nasıl elde edeceğini bilir ve çözümü gönderecek kadar naziktir.

Genişletilmiş etkinliğin sonuçları:

görüntü tanımını buraya girin

Genişletilmiş etkinliği oluşturmak için komut dosyası:

CREATE EVENT SESSION [tempdb_file_size_changed] ON SERVER ADD EVENT 
sqlserver.database_file_size_change(SET collect_database_name=(1)ACTION(sqlserver.client_app_name,sqlserver.client_hostname,sqlserver.is_system,sqlserver.query_hash,sqlserver.session_id,sqlserver.session_nt_username,sqlserver.sql_text,sqlserver.username) WHERE ([database_id]=(2))) ADD TARGETpackage0.event_file(SET filename=N'C:\ExtendedEvents\TempDBGrowth.xel',max_file_size=(100),max_rollover_files=(25)) WITH (MAX_MEMORY=4096 KB,EVENT_RETENTION_MODE=ALLOW_SINGLE_EVENT_LOSS,MAX_DISPATCH_LATENCY=1 SECONDS,MAX_EVENT_SIZE=0 KB,MEMORY_PARTITION_MODE=NONE,TRACK_CAUSALITY=OFF,STARTUP_STATE=ON)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.