25 milyondan fazla satır için sorguları optimize etme


11

MS SQL kullanıyorum ve farklı ölçütlerde aynı tabloda birkaç sorgu çalıştırmak zorunda. İlk başta, tüm filtreleri (yani Tarih, durum) paylaşmasına rağmen orijinal tablodaki her bir sorguyu çalıştırdım. Bu çok zaman aldı (yaklaşık 2 dakika).

Veri satırlarında kopyalar var ve tüm dizinler CLUSTERED değil. Ölçütlerim için yalnızca 4 sütunla ilgileniyorum ve sonuç tüm sayılar için yalnızca sayım çıktısı almalıdır.

sütunlar gerekli: TABLE, FIELD, AFTER, DATE, ve her biri üzerinde bir indeks var DATEve TABLE.

Sadece ihtiyacım olan alanları içeren bir geçici tablo oluşturduktan sonra, 1:40 dakikaya indi, bu hala çok kötü.

CREATE TABLE #TEMP
(
    TABLE VARCHAR(30) NULL,
    FIELD VARCHAR(30) NULL,
    AFTER VARCHAR(1000) NULL,
    DATE DATETIME,
    SORT_ID INT IDENTITY(1,1)
)
CREATE CLUSTERED INDEX IX_ADT ON #TEMP(SORT_ID)

INSERT INTO #TEMP (TABLE, FIELD, AFTER, DATE)
    SELECT TABLE, FIELD, AFTER, DATE 
    FROM mytbl WITH (NOLOCK)
    WHERE TABLE = 'OTB' AND
    FIELD = 'STATUS'

Bunu çalıştır -> (etkilenen 216598 satır)

Tüm sorgular tarih aralığına bağlı olmadığından, sorguya dahil etmedim. Sorun, yalnızca yerleştirmenin 1 dakikadan fazla sürmesi . Yukarıdaki insert 1:19 dakika sürdü

Birkaç sorgu için böyle bir şey çalıştırmak istiyorum:

SELECT COUNT(*) AS COUNT
FROM #TEMP
WHERE AFTER = 'R' AND
DATE >= '2014-01-01' AND
DATE <= '2015-01-01'

Seçimden daha fazla ekleme ile ilgili bir sorun var, ancak temp, orijinal tablodan daha az satır içeriyor ve bu da tablodan birkaç kez daha iyi olabilir.

Bunu nasıl optimize edebilirim?

DÜZENLE

Sıralama kimliğini kaldırdım, sorunun esas olarak seçim ve ekleme ile olmadığını düşündüm. Bir tahmindi.

Benzersiz bir alan veya satır olmadığından herhangi bir dizin üzerinde benzersiz oluşturamıyorum.

SQL Server 2012 kullanıyorum.

Tablo Bilgisi : Bu bir yığın ve aşağıdaki alan kullanımına sahiptir:

name    rows        reserved    data        index_size  unused
mytbl   24869658    9204568 KB  3017952 KB  5816232 KB  370384 KB

@MikaelEriksson Üretim tablolarını değiştiremiyorum ..
Atieh

Optimize etmeye çalıştığınız sorgular formdaysa SELECT COUNT(*) AS COUNT FROM original_table WHERE AFTER = 'R' AND DATE >= '2014-01-01' AND DATE < '2015-01-01', neden her bir (sorguyu) ayrı ayrı optimize etmeye çalışmıyorsunuz? Tabloya dizin eklemenize izin verilmiyor mu?
ypercubeᵀᴹ

2
Neden yavaş olduğunu belirlemelisin. Engelleniyor mu? Tempdb'nin büyümesini bekliyor mu? Uygulama planı dipsiz mi? Kimse daha fazla ayrıntı olmadan "benim sorgu yavaş" düzeltemez ...
Aaron Bertrand

3
Eh, bana kayıp bir neden gibi görünüyor ( "Hiçbir şeyi optimize etmeme izin verilmiyor, bu yüzden her sorgu çalıştırmamız gerektiğinde geçici bir tabloya 200K satır girelim" ). Ancak TABLEve FIELDsütunlarını #temptablodan kaldırabilirsiniz (sonuçta tüm satırlar TABLE = 'OTB' AND FIELD = 'STATUS'belirli geçici tablo için geçerlidir.)
ypercubeᵀᴹ

2
Ayrıntılı (ve kibar) bir yorum ekleyerek bir düzenleme ve iyileştirmeler istedim. Yorumlar bunun için. Sorunuzu kullandığınız SQL Server sürümüyle de etiketlemelisiniz (örn. SQL Server 2014). Tablo için DDL de yararlı olabilir ( CREATE TABLEifade). Aşağı oy, sorunun net olmamasıydı.
Paul White 9

Yanıtlar:


12

Soru esas olarak select deyiminin nasıl optimize edileceğiyle ilgilidir:

SELECT [TABLE], [FIELD], [AFTER], [DATE]
FROM mytbl WITH (NOLOCK)
WHERE [TABLE] = 'OTB' AND
[FIELD] = 'STATUS'

Gereksiz projeksiyonları kaldırma ve varsayılan dboşemayı ekleme :

SELECT [AFTER], [DATE] 
FROM dbo.mytbl WITH (NOLOCK)
WHERE [TABLE] = 'OTB'
AND FIELD = 'STATUS';

([TABLE],[FIELD]) INCLUDE ([AFTER],[DATE])SQL Server gibi bir dizin olmadan iki ana seçenek vardır:

  1. Yığını tamamen tarayın (3GB +); veya
  2. Eşleşen satırları ( [TABLE] = 'OTB've [FIELD] = 'STATUS'kullanarak IDX6) bulun , ardından ve sütunlarını almak için satır başına yığın (RID) araması yapın .[AFTER][DATE]

İyileştirici bir yığın tarama veya dizin RID arama tahmini seçicilik bağlıdır ile aramak seçer olsun [TABLE] = 'OTB've [FIELD] = 'STATUS'yüklemler. Aramadaki tahmini satır sayısının gerçekle eşleşip eşleşmediğini kontrol edin. Değilse, istatistiklerinizi güncelleyin. Bu koşul makul derecede seçiliyse , sorguyu dizin kullanımını zorlayan bir tablo ipucuyla sınayın . Optimize edici şu anda dizin aramasını seçiyorsa, yığını taramak için performansı bir INDEX(0)veya FORCESCANipucu ile test edin.

Bunun ötesinde, kullanılmayan alanın bir kısmını (370MB) kaldırarak öbek taramasını biraz iyileştirmek isteyebilirsiniz. SQL Server 2008'de bu, yığının yeniden oluşturulmasıyla yapılabilir. Yığınlardaki kullanılmayan alan, genellikle bir masa kilidi alınmadan yapılan silme işlemlerinden kaynaklanır (bir masa kilidi olmadan, boş sayfalar bir öbekten çıkarılmaz). Sık sık silme işlemine maruz kalan tablolar genellikle bu nedenle kümelenmiş tablo olarak daha iyi saklanır.

Yığın taramasının performansı, tablonun ne kadarının bellekte saklandığına, diskten ne kadar okunması gerektiğine, sayfaların ne kadar dolu olduğuna, kalıcı depolama hızına, taramanın G / Ç veya CPU'ya bağlı olduğuna ( paralellik yardımcı olabilir).

Yukarıdakilerin tümünü araştırdıktan sonra performans hala kabul edilemez durumdaysa, yeni bir dizin için durum oluşturmaya çalışın. SQL Server sürümünüzde mevcutsa, verilen sorgu için filtrelenmiş olası bir dizin şöyle olur:

CREATE INDEX index_name
ON dbo.mytbl ([DATE],[AFTER])
WHERE [TABLE] = 'OTB'
AND [FIELD] = 'STATUS';

Ayrıca, eğer mevcutsa ve faydalıysa, indeks sıkıştırmasını dikkate alın. Bir tür yeni bir dizin olmadan, verilen sorgunun performansını artırmak için yapabileceğiniz nispeten az şey vardır.


Paul Maalesef vardır: IDX6 nonclustered located on PRIMARY TABLE, FIELD. Belki bu senin bahsettiğin şeyleri değiştirir?
Atieh

6

Burada dizinleri değiştirmek için bir durum olduğunu düşünüyorum çünkü:

  • yapacak bir göreviniz var (bu birden çok sorgu)
  • veri ambarı hacimleri (25+ milyon satır) ve
  • bir performans sorunu.

Bu, SQL Server 2012'de tanıtılan kümelenmemiş sütun deposu dizinleri için de iyi bir kullanım durumu olacaktır, yani çok sayıda sütunlu büyük bir tabloda birkaç sütunu özetlemek / toplamak.

Bu dizinler, tabloyu salt okunur yapma yan etkisine sahip olsa da (bölüm değiştirme hariç), birleştirilmiş sorguların performansını doğru koşullar altında dönüştürebilirler. Salt okunur yönü, dizin veya basit bölüm anahtarı verilerinin tabloya bırakılması ve yeniden oluşturulmasıyla yönetilebilir.

Kurulumunuzu taklit etmek için basit bir test ekipmanı kurdum ve performansta iyi bir gelişme gördüm:

USE tempdb
GO

SET NOCOUNT ON
GO

-- Create a large table
IF OBJECT_ID('dbo.largeTable') IS NOT NULL
DROP TABLE dbo.largeTable
GO
CREATE TABLE dbo.largeTable ( 

    [TABLE] VARCHAR(30) NULL,
    FIELD VARCHAR(30) NULL,
    [AFTER] VARCHAR(1000) NULL,
    [DATE] DATETIME,
    SORT_ID INT IDENTITY(1,1),

    pad VARCHAR(100) DEFAULT REPLICATE( '$', 100 )
)
GO

-- Populate table
;WITH cte AS (
SELECT TOP 100000 ROW_NUMBER() OVER ( ORDER BY ( SELECT 1 ) ) rn
FROM master.sys.columns c1
    CROSS JOIN master.sys.columns c2
    CROSS JOIN master.sys.columns c3
)
INSERT INTO dbo.largeTable ( [TABLE], FIELD, [AFTER], [DATE] )
SELECT 
    x.tableName, 
    y.field,
    z.[after],
    DATEADD( day, rn % 1111, '1 Jan 2012' )
FROM cte c
    CROSS JOIN ( VALUES ( 'OTB' ), ( 'AAA' ), ( 'BBB' ), ( 'CCCC' ) ) x ( tableName )
    CROSS JOIN ( VALUES ( 'STATUS' ), ( 'TIME' ), ( 'POWER' ) ) y ( field )
    CROSS JOIN ( VALUES ( 'R' ), ( 'X' ), ( 'Z' ), ( 'A' ) ) z ( [after] )

CHECKPOINT

GO 5

EXEC sp_spaceused 'dbo.largeTable'
GO

SELECT MIN([DATE]) xmin, MAX([DATE]) xmax, FORMAT( COUNT(*), '#,#' ) records
FROM dbo.largeTable
GO

-- Optionally clear cache for more comparable results; DO NOT RUN ON PRODUCTION SYSTEM!!
--DBCC DROPCLEANBUFFERS
--DBCC FREEPROCCACHE
--GO

DECLARE @startDate DATETIME2 = SYSUTCDATETIME()

SELECT COUNT(*) AS COUNT
FROM dbo.largeTable
WHERE [AFTER] = 'R' 
  AND [DATE] >= '2014-01-01' 
  AND [DATE] <= '2015-01-01'

SELECT DATEDIFF( millisecond, @startDate, SYSUTCDATETIME() ) diff1
GO

-- Add the non-clustered columnstore
CREATE NONCLUSTERED COLUMNSTORE INDEX _cs ON dbo.largeTable ( [TABLE], FIELD, [AFTER], [DATE] )
GO

-- Optionally clear cache for more comparable results; DO NOT RUN ON PRODUCTION SYSTEM!!
--DBCC DROPCLEANBUFFERS
--DBCC FREEPROCCACHE
--GO

-- Check query again
DECLARE @startDate DATETIME2 = SYSUTCDATETIME()

SELECT COUNT(*) AS COUNT
FROM dbo.largeTable
WHERE [AFTER] = 'R' 
  AND [DATE] >= '2014-01-01' 
  AND [DATE] <= '2015-01-01'

SELECT DATEDIFF( millisecond, @startDate, SYSUTCDATETIME() ) diff2
GO

Sonuçlarım, 6 saniye v 0.08 saniye:

resim açıklamasını buraya girin

Özetle, dizinlerin değişmesi için patronunuzla bir vaka oluşturmaya çalışın veya en azından bu kayıtların çalışmanızı yapabileceğiniz salt okunur bir raporlama tablosuna / veritabanına oyulduğu bir tür geceleme işlemi oluşturun ve dizin oluşturma ekleyin iş yüküne uygun.

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.