Basit bir CCI satır grubu oluşturmak neden 30 saniye kadar sürebilir?


20

Eklerimin bazılarının beklenenden daha uzun sürdüğünü fark ettiğimde CCI'leri içeren bir demo üzerinde çalışıyordum. Çoğaltılacak tablo tanımları:

DROP TABLE IF EXISTS dbo.STG_1048576;
CREATE TABLE dbo.STG_1048576 (ID BIGINT NOT NULL);
INSERT INTO dbo.STG_1048576
SELECT TOP (1048576) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) RN
FROM master..spt_values t1
CROSS JOIN master..spt_values t2;

DROP TABLE IF EXISTS dbo.CCI_BIGINT;
CREATE TABLE dbo.CCI_BIGINT (ID BIGINT NOT NULL, INDEX CCI CLUSTERED COLUMNSTORE);

Testler için 1048576 satırın tümünü hazırlama tablosundan ekliyorum. Herhangi bir nedenle kırpılmadığı sürece tam olarak bir sıkıştırılmış satır grubunu doldurmak yeterlidir.

Tüm mod mod 17000'i eklersem bir saniyeden az sürer:

TRUNCATE TABLE dbo.CCI_BIGINT;

INSERT INTO dbo.CCI_BIGINT WITH (TABLOCK)
SELECT ID % 17000
FROM dbo.STG_1048576
OPTION (MAXDOP 1);

SQL Server Yürütme Süreleri: CPU süresi = 359 ms, geçen süre = 364 ms.

Ancak, aynı tamsayı mod 16000'i eklersem bazen 30 saniyeden fazla sürer:

TRUNCATE TABLE dbo.CCI_BIGINT;

INSERT INTO dbo.CCI_BIGINT WITH (TABLOCK)
SELECT ID % 16000
FROM dbo.STG_1048576
OPTION (MAXDOP 1);

SQL Server Yürütme Süreleri: CPU süresi = 32062 ms, geçen süre = 32511 ms.

Bu, birden fazla makinede yapılan tekrarlanabilir bir testtir. Mod değeri değiştikçe geçen sürede net bir örüntü var gibi görünüyor:

MOD_NUM TIME_IN_MS
1000    2036
2000    3857
3000    5463
4000    6930
5000    8414
6000    10270
7000    12350
8000    13936
9000    17470
10000   19946
11000   21373
12000   24950
13000   28677
14000   31030
15000   34040
16000   37000
17000   563
18000   583
19000   576
20000   584

Testleri kendiniz yapmak istiyorsanız, burada yazdığım test kodunu değiştirmekten çekinmeyin .

Mod 16000 insert için sys.dm_os_wait_stats'ta ilginç bir şey bulamadım:

╔════════════════════════════════════╦══════════════╗
             wait_type               diff_wait_ms 
╠════════════════════════════════════╬══════════════╣
 XE_DISPATCHER_WAIT                        164406 
 QDS_PERSIST_TASK_MAIN_LOOP_SLEEP          120002 
 LAZYWRITER_SLEEP                           97718 
 LOGMGR_QUEUE                               97298 
 DIRTY_PAGE_POLL                            97254 
 HADR_FILESTREAM_IOMGR_IOCOMPLETION         97111 
 SQLTRACE_INCREMENTAL_FLUSH_SLEEP           96008 
 REQUEST_FOR_DEADLOCK_SEARCH                95001 
 XE_TIMER_EVENT                             94689 
 SLEEP_TASK                                 48308 
 BROKER_TO_FLUSH                            48264 
 CHECKPOINT_QUEUE                           35589 
 SOS_SCHEDULER_YIELD                           13 
╚════════════════════════════════════╩══════════════╝

Kesici uç neden kesici ID % 16000uçtan çok daha uzun sürüyor ID % 17000?

Yanıtlar:


12

Birçok bakımdan bu beklenen davranıştır. Herhangi bir sıkıştırma yordamı kümesi, giriş veri dağıtımına bağlı olarak geniş bir aralıkta performansa sahip olacaktır. Depolama boyutu ve çalışma zamanı sorgulama performansı için veri yükleme hızıyla işlem yapmayı umuyoruz.

VertiPaq tescilli bir uygulama olduğundan ve ayrıntılar yakından korunan bir sır olduğundan, burada ne kadar ayrıntılı bir cevap alacağınıza dair kesin bir sınır vardır. Yine de, VertiPaq'ın aşağıdakiler için rutin içerdiğini biliyoruz:

  • Değer kodlaması (az sayıda bite sığacak şekilde ölçekleme ve / veya çeviri değerleri)
  • Sözlük kodlaması (benzersiz değerlere tamsayı referansları)
  • Çalışma Uzunluğu Kodlaması (tekrarlanan değerlerin çalışmalarını [değer, sayım] çiftleri olarak saklamak)
  • Bit paketleme (akışı mümkün olduğunca az bitte depolama)

Tipik olarak, veriler değer veya sözlük kodlu olacak, daha sonra RLE veya bit paketleme uygulanacaktır (veya segment verilerinin farklı alt bölümlerinde kullanılan RLE ve bit paketleme hibriti). Hangi tekniklerin uygulanacağına karar verme süreci, maksimum bit tasarrufunun nasıl sağlanabileceğini belirlemeye yardımcı olmak için bir histogram oluşturulmasını içerebilir.

Yavaş durumu Windows Performans Kaydedici ile yakalayıp sonucu Windows Performans Çözümleyicisi ile analiz ederek, yürütme süresinin büyük çoğunluğunun verilerin kümelenmesine bakmak, histogramlar oluşturmak ve en iyi şekilde nasıl bölümlendirileceğine karar vermek için tüketildiğini görebiliriz tasarruf:

WPA Analizi

En pahalı işlem, segmentte en az 64 kez görünen değerler için gerçekleşir. Bu, saf RLE'nin ne zaman faydalı olacağını belirlemek için bir buluşsal yöntemdir . Daha hızlı vakalar , daha büyük bir son depolama boyutuna sahip, örneğin bit paketlenmiş sunum gibi saf olmayan depolamaya neden olur . Hibrit durumlarda, 64 veya daha fazla tekrar içeren değerler RLE kodludur ve geri kalanı bit paketlidir.

En uzun süre, 64 tekrarlı maksimum farklı değer sayısı mümkün olan en büyük segmentte göründüğünde, yani her biri 64 girişli 16.384 değer içeren 1.048.576 satırda görülür. Kodun incelenmesi, pahalı işlem için sabit kodlanmış bir zaman sınırı ortaya koymaktadır. Bu, SSAS gibi diğer VertiPaq uygulamalarında yapılandırılabilir, ancak SQL Server'da anlatabildiğim kadarıyla yapılandırılamaz.

Son depolama düzenlemesine ilişkin bazı bilgiler belgesiz DBCC CSINDEXkomut kullanılarak edinilebilir . Bu, RLE üstbilgisi ve dizi girişlerini, RLE verilerine yapılan tüm yer işaretlerini ve bit paketi verilerinin (varsa) kısa bir özetini gösterir.

Daha fazla bilgi için bakınız:


9

Tam olarak neden bu davranışın gerçekleştiğini söyleyemem ama kaba kuvvet testi ile iyi bir davranış modeli geliştirdiğime inanıyorum. Aşağıdaki sonuçlar yalnızca tek bir sütuna veri yüklerken ve çok iyi dağıtılmış tamsayılarla geçerlidir.

Önce kullanarak CCI içine eklenen satır sayısını değiştirmeye çalıştım TOP. ID % 16000Tüm testler için kullandım . Aşağıda, sıkıştırılmış satır grubu segment boyutuna eklenen satırları karşılaştıran bir grafik bulunmaktadır:

üst vs boyut grafiği

Aşağıda ms cinsinden CPU zamanına eklenen satırların bir grafiği bulunmaktadır. X ekseninin farklı bir başlangıç ​​noktasına sahip olduğuna dikkat edin:

cpu vs üst

Satır grubu segment boyutunun doğrusal bir oranda büyüdüğünü ve yaklaşık 1 M satıra kadar az miktarda CPU kullandığını görebiliriz. Bu noktada satır grubu boyutu önemli ölçüde azalır ve CPU kullanımı önemli ölçüde artar. Görünüşe göre bu sıkıştırma için CPU'da ağır bir bedel ödüyoruz.

1024000'den az satır eklerken CCI'da açık bir satır grubu buldum. Ancak, sıkıştırmayı kullanarak zorlama REORGANIZEveya REBUILDboyut üzerinde bir etkisi olmadı. Bir kenara, bir değişken kullandığımda TOPaçık bir satır grubu ile RECOMPILEsona erdiğini, ancak kapalı bir satır grubu ile sona erdiğini ilginç buldum.

Daha sonra, satır sayısını aynı tutarken modül değerini değiştirerek test ettim. 102400 satır eklerken verilerin bir örneği:

╔═══════════╦═════════╦═══════════════╦═════════════╗
 TOP_VALUE  MOD_NUM  SIZE_IN_BYTES  CPU_TIME_MS 
╠═══════════╬═════════╬═══════════════╬═════════════╣
    102400     1580          13504          352 
    102400     1590          13584          316 
    102400     1600          13664          317 
    102400     1601          19624          270 
    102400     1602          25568          283 
    102400     1603          31520          286 
    102400     1604          37464          288 
    102400     1605          43408          273 
    102400     1606          49360          269 
    102400     1607          55304          265 
    102400     1608          61256          262 
    102400     1609          67200          255 
    102400     1610          73144          265 
    102400     1620         132616          132 
    102400     1621         138568          100 
    102400     1622         144512           91 
    102400     1623         150464           75 
    102400     1624         156408           60 
    102400     1625         162352           47 
    102400     1626         164712           41 
╚═══════════╩═════════╩═══════════════╩═════════════╝

1600 mod değerine kadar satır grubu segment boyutu, her ek 10 benzersiz değer için doğrusal olarak 80 bayt artar. İlginç bir tesadüfBIGINTGeleneksel olarak 8 bayt alan ve segment boyutu, her bir benzersiz değer için 8 bayt artar. 1600 mod değerinden sonra segment boyutu stabil hale gelene kadar hızla artar.

Ayrıca, modül değerini aynı bırakıp eklenen satır sayısını değiştirirken verilere bakmak da yararlıdır:

╔═══════════╦═════════╦═══════════════╦═════════════╗
 TOP_VALUE  MOD_NUM  SIZE_IN_BYTES  CPU_TIME_MS 
╠═══════════╬═════════╬═══════════════╬═════════════╣
    300000     5000         600656          131 
    305000     5000         610664          124 
    310000     5000         620672          127 
    315000     5000         630680          132 
    320000     5000          40688         2344 
    325000     5000          40696         2577 
    330000     5000          40704         2589 
    335000     5000          40712         2673 
    340000     5000          40728         2715 
    345000     5000          40736         2744 
    350000     5000          40744         2157 
╚═══════════╩═════════╩═══════════════╩═════════════╝

Eklenen satır sayısı ~ ~ 64 * olduğunda, göreceli olarak düşük sıkıştırma (mod <= 65000 için satır başına 2 bayt) ve düşük, doğrusal CPU kullanımı gördüğümüz zamanki gibi görünüyor. Girilen satır sayısı> ~ 64 * olduğunda benzersiz değerlerin sayısı çok daha iyi sıkıştırma ve daha yüksek, hala doğrusal CPU kullanımı görüyoruz. İki durum arasında benim için modellenmesi kolay olmayan bir geçiş var, ancak grafikte görülebilir. Her benzersiz değer için tam olarak 64 satır eklerken maksimum CPU kullanımını gördüğümüz doğru görünmüyor. Bunun yerine, bir satır grubuna yalnızca en fazla 1048576 satır ekleyebiliriz ve benzersiz değer başına 64'ten fazla satır olduğunda çok daha yüksek CPU kullanımı ve sıkıştırması görürüz.

Aşağıda, eklenen satırların sayısı ve benzersiz satırların sayısı değiştikçe cpu zamanının nasıl değiştiğine dair bir kontur grafiği bulunmaktadır. Yukarıda açıklanan modelleri görebiliriz:

kontur işlemci

Aşağıda, segment tarafından kullanılan konturun bir alan grafiği verilmiştir. Belirli bir noktadan sonra yukarıda açıklandığı gibi çok daha iyi sıkıştırma görmeye başlarız:

kontur boyutu

Burada en az iki farklı sıkıştırma algoritması var gibi görünüyor. Yukarıdakiler göz önüne alındığında, 1048576 satır eklerken maksimum CPU kullanımını göreceğimiz mantıklıdır. Ayrıca, yaklaşık 16000 satır eklerken bu noktada en fazla CPU kullanımını gördüğümüz de anlamlıdır. 1048576/64 = 16384.

Birisinin analiz etmek istemesi durumunda tüm ham verilerimi buraya yükledim .

Paralel planlarda neler olduğunu anlatmaya değer. Bu davranışı sadece eşit dağıtılmış değerlerle gözlemledim. Paralel bir ekleme yaparken genellikle bir rastgelelik unsuru vardır ve dişler genellikle dengesizdir.

2097152 satırı hazırlama tablosuna yerleştirin:

DROP TABLE IF EXISTS STG_2097152;
CREATE TABLE dbo.STG_2097152 (ID BIGINT NOT NULL);
INSERT INTO dbo.STG_2097152 WITH (TABLOCK)
SELECT TOP (2097152) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) RN
FROM master..spt_values t1
CROSS JOIN master..spt_values t2;

Bu kesici uç bir saniyeden daha kısa sürede biter ve düşük sıkıştırmaya sahiptir:

DROP TABLE IF EXISTS dbo.CCI_BIGINT;
CREATE TABLE dbo.CCI_BIGINT (ID BIGINT NOT NULL, INDEX CCI CLUSTERED COLUMNSTORE);

INSERT INTO dbo.CCI_BIGINT WITH (TABLOCK)
SELECT ID % 16000
FROM dbo.STG_2097152 
OPTION (MAXDOP 2);

Dengesiz dişlerin etkisini görebiliriz:

╔════════════╦════════════╦══════════════╦═══════════════╗
 state_desc  total_rows  deleted_rows  size_in_bytes 
╠════════════╬════════════╬══════════════╬═══════════════╣
 OPEN             13540             0         311296 
 COMPRESSED     1048576             0        2095872 
 COMPRESSED     1035036             0        2070784 
╚════════════╩════════════╩══════════════╩═══════════════╝

İplikleri dengeli olmaya ve aynı sıra dağılımına sahip olmaya zorlamak için yapabileceğimiz çeşitli püf noktaları var. İşte bunlardan biri:

DROP TABLE IF EXISTS dbo.CCI_BIGINT;
CREATE TABLE dbo.CCI_BIGINT (ID BIGINT NOT NULL, INDEX CCI CLUSTERED COLUMNSTORE);

INSERT INTO dbo.CCI_BIGINT WITH (TABLOCK)
SELECT FLOOR(0.5 * ROW_NUMBER() OVER (ORDER BY (SELECT NULL)))  % 15999
FROM dbo.STG_2097152
OPTION (MAXDOP 2)

Burada modül için tek bir sayı seçmek önemlidir. SQL Server, hazırlama tablosunu seri olarak tarar, satır numarasını hesaplar, ardından satırları paralel iş parçacıklarına yerleştirmek için yuvarlak robin dağıtımını kullanır. Bu da son derece dengeli ipliklerle sonuçlanacağımız anlamına geliyor.

denge 1

Kesici uç, seri kesici uca benzer şekilde yaklaşık 40 saniye sürer. Güzel sıkıştırılmış satır grupları elde ederiz:

╔════════════╦════════════╦══════════════╦═══════════════╗
 state_desc  total_rows  deleted_rows  size_in_bytes 
╠════════════╬════════════╬══════════════╬═══════════════╣
 COMPRESSED     1048576             0         128568 
 COMPRESSED     1048576             0         128568 
╚════════════╩════════════╩══════════════╩═══════════════╝

Orijinal evreleme tablosundan veri ekleyerek aynı sonuçları elde edebiliriz:

DROP TABLE IF EXISTS dbo.CCI_BIGINT;
CREATE TABLE dbo.CCI_BIGINT (ID BIGINT NOT NULL, INDEX CCI CLUSTERED COLUMNSTORE);

INSERT INTO dbo.CCI_BIGINT WITH (TABLOCK)
SELECT t.ID % 16000 ID
FROM  (
    SELECT TOP (2) ID 
    FROM (SELECT 1 ID UNION ALL SELECT 2 ) r
) s
CROSS JOIN dbo.STG_1048576 t
OPTION (MAXDOP 2, NO_PERFORMANCE_SPOOL);

Burada türetilmiş tablo için yuvarlak robin dağılımı kullanılır, sböylece her paralel iş parçacığında tablonun bir taraması yapılır:

dengeli 2

Sonuç olarak, eşit olarak dağıtılmış tamsayılar eklerken, her bir benzersiz tamsayı 64 kereden fazla göründüğünde çok yüksek sıkıştırma görebilirsiniz. Bunun nedeni, farklı bir sıkıştırma algoritmasının kullanılması olabilir. Bu sıkıştırmayı elde etmek için CPU'da yüksek bir maliyet olabilir. Verilerdeki küçük değişiklikler, sıkıştırılmış satır grubu segmentinin boyutunda dramatik farklılıklara yol açabilir. En kötü durumu görmenin (CPU perspektifinden) en azından bu veri seti için vahşi olacağından şüpheleniyorum. Paralel uçlar yaparken görmek daha da zor.


8

Bunun tek sütun tabloları için sıkıştırmanın dahili optimizasyonları ve sözlük tarafından işgal edilen 64 KB'nin sihirli numarası ile ilgili olduğuna inanıyorum.

Örnek: MOD 16600 ile çalışırsanız , Satır Grubu boyutunun nihai sonucu 1.683 MB olurken, MOD 17000 çalıştırıldığında size 2.001 MB boyutunda bir Satır Grubu verecektir .

Şimdi, oluşturulan sözlüklere bir göz atın ( bunun için CISL kütüphanemi kullanabilirsiniz , cstore_GetDictionaries fonksiyonuna ihtiyacınız olacak veya alternatif olarak sys.column_store_dictionaries DMV'ye gidip sorgulayacaksınız):

(MOD 16600) 61 KB

resim açıklamasını buraya girin

(MOD 17000) 65 KB

resim açıklamasını buraya girin

Komik bir şey, tablonuza başka bir sütun ekleyecekseniz ve buna REALID diyelim:

DROP TABLE IF EXISTS dbo.CCI_BIGINT;
CREATE TABLE dbo.CCI_BIGINT (ID BIGINT NOT NULL, REALID BIGINT NOT NULL, INDEX CCI CLUSTERED COLUMNSTORE);

MOD 16600 için verileri yeniden yükleyin:

TRUNCATE TABLE dbo.CCI_BIGINT;

INSERT INTO dbo.CCI_BIGINT WITH (TABLOCK)
SELECT ID % 16600, ID
FROM dbo.STG_1048576
OPTION (MAXDOP 1);

Bu sefer yürütme hızlı olacak, çünkü optimizatör fazla çalışmamaya ve çok fazla sıkıştırmamaya karar verecek:

select column_id, segment_id, cast(sum(seg.on_disk_size) / 1024. / 1024 as Decimal(8,3) ) as SizeInMB
    from sys.column_store_segments seg
        inner join sys.partitions part
            on seg.hobt_id = part.hobt_id 
    where object_id = object_id('dbo.CCI_BIGINT')
    group by column_id, segment_id;

Sıra Grubu boyutları arasında küçük bir fark olsa da, ihmal edilebilir (2.000 (MOD 16600) ve 2.001 (MOD 17000))

Bu senaryo için, MOD 16000 sözlüğü 1 sütunlu (0.63'e karşı 0.61) ilk senaryoya göre daha büyük olacaktır.

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.