Tek satır INSERT… SELECT ayrı ayrı SELECT'den çok daha yavaş


18

1'den 400'e kadar 400 satır içeren aşağıdaki yığın tablosu göz önüne alındığında:

DROP TABLE IF EXISTS dbo.N;
GO
SELECT 
    SV.number
INTO dbo.N 
FROM master.dbo.spt_values AS SV
WHERE 
    SV.[type] = N'P'
    AND SV.number BETWEEN 1 AND 400;

ve aşağıdaki ayarlar:

SET NOCOUNT ON;
SET STATISTICS IO, TIME OFF;
SET STATISTICS XML OFF;
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;

Aşağıdaki SELECTifade yaklaşık 6 saniye içinde tamamlanır ( demo , plan ):

DECLARE @n integer = 400;

SELECT
    c = COUNT_BIG(*) 
FROM dbo.N AS N
CROSS JOIN dbo.N AS N2
CROSS JOIN dbo.N AS N3
WHERE 
    N.number <= @n
    AND N2.number <= @n
    AND N3.number <= @n
OPTION
    (OPTIMIZE FOR (@n = 1));

Not: @ OPTIMIZE FORMadde sadece çeşitli nedenlerle ortaya çıkabilecek bir kardinalite yanlışlığı da dahil olmak üzere gerçek sorunun temel ayrıntılarını yakalayan makul büyüklükte bir repro üretmek içindir.

Tek satırlık çıktı bir tabloya yazıldığında, 19 saniye sürer ( demo , plan ):

DECLARE @T table (c bigint NOT NULL);

DECLARE @n integer = 400;

INSERT @T
    (c)
SELECT
    c = COUNT_BIG(*) 
FROM dbo.N AS N
CROSS JOIN dbo.N AS N2
CROSS JOIN dbo.N AS N3
WHERE 
    N.number <= @n
    AND N2.number <= @n
    AND N3.number <= @n
OPTION
    (OPTIMIZE FOR (@n = 1));

Yürütme planları, bir satırın eklenmesiyle aynı görünür.

Tüm ekstra zamanlar CPU kullanımı tarafından tüketilmiş gibi görünüyor.

İfade neden INSERTbu kadar yavaş?

Yanıtlar:


21

SQL Server, satır düzeyinde kilitleri kullanarak döngüler birleşimlerinin iç tarafındaki yığın tablolarını taramayı seçer. Tam bir tarama normalde sayfa düzeyinde kilitlemeyi seçer, ancak tablonun boyutu ve yüklemin bir kombinasyonu, en ucuz strateji olarak göründüğü için depolama motorunun satır kilitlerini seçtiği anlamına gelir.

Kardinalite yanlış tahminleri OPTIMIZE FOR, yığınların optimize edicinin beklediğinden çok daha fazla taranması yoluyla kasten getirilir ve normalde olduğu gibi bir makara getirmez.

Bu faktör kombinasyonu, performansın çalışma zamanında gereken kilit sayısına çok duyarlı olduğu anlamına gelir.

SELECTSağlayan bir optimizasyon gelen açıklamada faydaları satır düzeyinde paylaşılan kilitler Atlanacak kaydedilmemiş verileri okuma tehlikesi yoktur (yalnızca niyet paylaşılan sayfa seviyesinde kilitleri alarak) ve hiçbir dışı satır veri yoktur.

INSERT...SELECTRID kilitler milyonlarca-niyet paylaşılan sayfa düzeyinde kilitleri ile birlikte alınır ve ikinci durumda her saniye serbest bırakılır böylece açıklamada, bu optimizasyon yarar gelmez.

Büyük miktarda kilitleme etkinliği, ekstra CPU ve geçen süreyi açıklar.

En doğal çözüm, iyileştiricinin (ve depolama motorunun) iyi seçimler yapabilmeleri için iyi kardinalite tahminleri almasını sağlamaktır.

Bu gerçek kullanım durumunda pratik değilse, INSERTve SELECTifadeleri SELECTbir değişken içinde tutulan sonucuyla ayrılabilir . Bu, SELECTifadenin kilit atlama optimizasyonundan faydalanmasını sağlar.

Yalıtım düzeyinin değiştirilmesi, paylaşılan kilitler alınmadan veya kilit yükselmesinin hızlı bir şekilde gerçekleşmesini sağlayarak da işe yarayabilir.

Son bir ilgi noktası olarak, sorgulanmamış SELECTbelge bayrak 8691 kullanarak makaraların kullanımını zorlayarak sorgu optimize edilmiş durumda daha hızlı çalıştırmak için yapılabilir .

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.