İstatistikler, artımlı güncelleme sonrasında kayboluyor


21

Artımlı istatistikleri kullanan büyük bir bölümlenmiş SQL Server veritabanına sahibiz. Tüm indeksler hizalı olarak bölümlenir. Çevrimiçi olarak bir bölümü yeniden bölümlendirmeye çalıştığımızda, dizin yeniden oluşturulduktan sonra tüm istatistikler kaybolur.

Aşağıda, SQL Server 2014'teki sorunu AdventureWorks2014 veritabanıyla çoğaltmak için bir komut dosyası verilmiştir.

--Example against AdventureWorks2014 Database

CREATE PARTITION FUNCTION TransactionRangePF1 (DATETIME)
AS RANGE RIGHT FOR VALUES 
(
   '20130501', '20130601', '20130701', '20130801', 
   '20130901', '20131001', '20131101', '20131201', 
   '20140101', '20140201', '20140301'
);
GO

CREATE PARTITION SCHEME TransactionsPS1 AS PARTITION TransactionRangePF1 TO 
(
  [PRIMARY], [PRIMARY], [PRIMARY], [PRIMARY], [PRIMARY], 
  [PRIMARY], [PRIMARY], [PRIMARY], [PRIMARY], [PRIMARY], 
  [PRIMARY], [PRIMARY], [PRIMARY]
);
GO

CREATE TABLE dbo.TransactionHistory 
(
  TransactionID        INT      NOT NULL, -- not bothering with IDENTITY here
  ProductID            INT      NOT NULL,
  ReferenceOrderID     INT      NOT NULL,
  ReferenceOrderLineID INT      NOT NULL DEFAULT (0),
  TransactionDate      DATETIME NOT NULL DEFAULT (GETDATE()),
  TransactionType      NCHAR(1) NOT NULL,
  Quantity             INT      NOT NULL,
  ActualCost           MONEY    NOT NULL,
  ModifiedDate         DATETIME NOT NULL DEFAULT (GETDATE()),
  CONSTRAINT CK_TransactionType 
    CHECK (UPPER(TransactionType) IN (N'W', N'S', N'P'))
) 
ON TransactionsPS1 (TransactionDate);


INSERT INTO dbo.TransactionHistory
SELECT * FROM Production.TransactionHistory
--  SELECT * FROM sys.partitions
--  WHERE object_id = OBJECT_ID('dbo.TransactionHistory');

CREATE NONCLUSTERED INDEX IDX_ProductId ON dbo.TransactionHistory (ProductId) 
  WITH (DATA_COMPRESSION = ROW, STATISTICS_INCREMENTAL=ON)  
  ON TransactionsPS1 (TransactionDate)

DBCC SHOW_STATISTICS('dbo.TransactionHistory', IDX_ProductId);
PRINT 'Stats are avialable'  

ALTER INDEX [IDX_ProductId] ON [dbo].[TransactionHistory] REBUILD 
  PARTITION = 9 WITH (ONLINE = ON , DATA_COMPRESSION = ROW)

PRINT 'After online index rebuild by partition stats are now gone'
DBCC SHOW_STATISTICS('dbo.TransactionHistory', IDX_ProductId);

PRINT 'Rebuild the stats with a rebuild for all paritions (this works)' 
ALTER INDEX [IDX_ProductId] ON [dbo].[TransactionHistory] REBUILD 
  PARTITION = ALL WITH (ONLINE = ON , DATA_COMPRESSION = ROW, 
  STATISTICS_INCREMENTAL = ON)

PRINT 'Stats are back'
DBCC SHOW_STATISTICS('dbo.TransactionHistory', IDX_ProductId);

PRINT 'Works correctly for an offline rebuild by partition'
ALTER INDEX [IDX_ProductId] ON [dbo].[TransactionHistory] REBUILD 
  PARTITION = 9 WITH (ONLINE = OFF , DATA_COMPRESSION = ROW)

    --stats still there  
DBCC SHOW_STATISTICS('dbo.TransactionHistory', IDX_ProductId);

ALTER INDEX [IDX_ProductId] ON [dbo].[TransactionHistory] REBUILD 
  PARTITION = 9 WITH (ONLINE = ON , DATA_COMPRESSION = ROW)

DBCC SHOW_STATISTICS('dbo.TransactionHistory', IDX_ProductId);
PRINT' stats are gone!!!!!!'

Gösterildiği gibi, dizin için tüm istatistikleri kaybetmeden çevrimiçi bölümlere göre dizinleri yeniden oluşturamıyoruz. Bu bizim için büyük bir bakım konusudur. İstatistikler artımlı seçeneğin, tek dizin yeniden oluşturma sözdiziminin bir parçası olması gerektiği veya çevrimiçi seçeneğin, çevrimdışı seçeneğin yaptığı gibi düzgün şekilde çalışması gerektiği anlaşılıyor.

Bir şey kaçırırsam lütfen bana bildirin.

Güncellemeler:

Artımlı istatistiklere olan ihtiyacımız kadar: Bir tarihte değil, dahili bir müşteri kimliğine ayrılıyoruz. Bu nedenle, yeni bir müşteri getirildiğinde (büyük veri geri yükleme), bölümün istatistiklerini güncelleyebilir ve bu yeni müşteri için çirkin planların oluşmasını hızla önleyebiliriz. Microsoft'a bir hata olarak dosyalayacağım ve ne söyleyeceklerini görecek ve sadece o bölümün istatistiklerini yeniden örnekleme çözümüyle devam edeceğimi düşünüyorum.

Hata raporunu bağla:

Çevrimiçi endeks, artan istatistikler ile yeniden oluşturulduktan sonra istatistikler kayboluyor

Güncelleştirme: Microsoft, bunun bir hata olduğunu onayladı.


1
Güncelleme: Microsoft bu sabah SQL 2014 için bir sonraki CU güncellemesinde düzeltilecek olan bir e-posta gönderdi.
JasonR

Hangi CU'ların düzelttiğini veya bu e-postada bildirdikleri KB'nin ne olduğunu biliyor musunuz? Ne zaman tamir edildiğini görmeye çalışıyorum.
mbourgon

1
VSTS hata numarası 8046729 KB olduğundan emin olun. SQL Server 2014 SP1 için CU 9'un bir parçası olan 3194959 numaralı Makale. KB'ye bir link burada .
JasonR

Evet, öyle görünüyor - ve geçen ay 2016 SP1’de düzeltildi. Çok çok teşekkürler!
mbourgon

Düzeltme: 2016 SP1 CU2'de henüz düzeltildi. 2016 SP1 CU1'de gerçekleşir.
mbourgon

Yanıtlar:


17

Değil emin bir hata, eğer kendi başına ama kesinlikle ilginç bir olay bu. Çevrimiçi bölüm yeniden yapılandırmaları, SQL Server 2014'te yenidir, bu nedenle bununla ilgili bazı iç bağlantılar olabilir.

İşte size en iyi açıklamam. Artan istatistikler kesinlikle tüm bölümlerin aynı oranda örneklenmesini gerektirir, böylece motor istatistik sayfalarını birleştirdiğinde örneklenen dağılımın karşılaştırılabilir olduğundan emin olabilir. REBUILDmutlaka% 100 numune hızında veri örnekleri. Bölüm 9'daki% 100 örnekleme oranının her zaman bölümlerin geri kalanının kesin örnekleme oranı olacağının garantisi yoktur. Bu nedenle, motor örnekleri birleştiremez gibi görünüyor ve boş bir istatistik blobuna ulaşıyorsunuz. Ancak, istatistik nesnesi hala orada:

select 
    check_time = sysdatetime(),                         
    schema_name = sh.name,
    table_name = t.name,
    stat_name = s.name,
    index_name = i.name,
    stats_column = index_col(quotename(sh.name)+'.'+quotename(t.name),s.stats_id,1),
    s.stats_id,
    s.has_filter,                       
    s.is_incremental,
    s.auto_created,
    sp.last_updated,    
    sp.rows,
    sp.rows_sampled,                        
    sp.unfiltered_rows,
    modification_counter 
from sys.stats s 
join sys.tables t 
    on s.object_id = t.object_id
join sys.schemas sh
    on t.schema_id = sh.schema_id
left join sys.indexes i 
    on s.object_id = i.object_id
    and s.name = i.name
outer apply sys.dm_db_stats_properties(s.object_id, s.stats_id) sp
where t.name = 'TransactionHistory' and sh.name = 'dbo'

Blob'u istediğiniz sayıda yolla doldurabilirsiniz:

UPDATE STATISTICS dbo.TransactionHistory (IDX_ProductId) WITH RESAMPLE;

veya

UPDATE STATISTICS dbo.TransactionHistory (IDX_ProductId) WITH RESAMPLE ON PARTITIONS (9);

veya bu nesneyi kullanarak bir sorgu planının ilk derlemesinde AutoStats'ın güncellenmesini bekleyebilirsiniz:

-- look at my creative query
select * 
from dbo.TransactionHistory
where TransactionDate = '20140101';

Bunların hepsini söyledikten sonra, Erin Stellato'nun bu aydınlatıcı yazısı , artımlı istatistiklerin önemli bir eksikliği olarak algılanan şeyin altını çiziyor . Bölüm düzeyindeki veriler, sorgulayıcı oluşturmada optimize edici tarafından kullanılmaz, bu da artan istatistiklerin varsayılan faydasını azaltır. Öyleyse, artımlı istatistiklerin mevcut faydası nedir? Temel faydalarının, büyük tabloları geleneksel istatistiklere göre daha tutarlı bir şekilde örnekleme kabiliyetine sahip olduğunu söyleyebilirim.

Örneğinizi kullanarak işte işler böyle görünüyor:

set statistics time on;

update statistics dbo.TransactionHistory(IDX_ProductId)
with fullscan;

--SQL Server Execution Times:
--  CPU time = 94 ms,  elapsed time = 131 ms.


update statistics dbo.TransactionHistory(IDX_ProductId)
with resample on partitions(2);

 --SQL Server Execution Times:
 --  CPU time = 0 ms,  elapsed time = 5 ms.

drop index IDX_ProductId On dbo.TransactionHistory;

CREATE NONCLUSTERED INDEX IDX_ProductId ON dbo.TransactionHistory (ProductId) 
  WITH (DATA_COMPRESSION = ROW)  
  ON [PRIMARY]

update statistics dbo.TransactionHistory(IDX_ProductId)
with fullscan;

 --SQL Server Execution Times:
 --  CPU time = 76 ms,  elapsed time = 66 ms.

Artımlı istatistiklerle ilgili tam kapsamlı bir istatistik güncellemesi 131 msn. Bölünmemiş hizalanmış istatistiklerle ilgili tam kapsamlı bir istatistik güncellemesi 66 msn. Tek tek istatistik sayfalarını ana histograma birleştirmek için yapılan genel gider nedeniyle hizalı olmayan istatistik daha yavaştır. Bununla birlikte, bölüm hizalanmış istatistik nesnesini kullanarak, bir bölümü güncelleyebilir ve onu 5 ms'de ana histogram bloğuna geri birleştirebiliriz. Bu noktada, bu noktada artımlı istatistiklere sahip yönetici bir kararla karşı karşıya kalmaktadır. Genel istatistik bakım sürelerini yalnızca bölümleri güncelleyerek geleneksel olarak güncellemeleri gerekebilecek şekilde azaltabilirler veya daha yüksek örnekleme hızları ile deney yapabilirler, böylece önceki bakım zamanlarıyla aynı zaman diliminde örneklenen potansiyel olarak daha fazla satır alabilirler. Eski bakım penceresinde yeterli boşluk sağlar ikincisi kudreti sorguları daha doğru istatistiklere dayalı iyi planlar elde bir yere çok büyük bir masanın üzerine itme istatistikleri. Bu bir garanti değildir ve kilometreniz değişebilir.

Okuyucu 66 ms'nin bu tablodaki acı istatistiklerin güncelleme süresi olmadığını görebilir, bu yüzden stackexchange veri setinde bir test kurmayı denedim. İndirdiğim son dökümü içinde 6.418.608 gönderi var (StackOverflow gönderileri ve 2012'deki tüm gönderiler - benim açımdan veri hatası hariç).

Verileri bölümlere [CreationDate]ayırdım çünkü ... demo.

İşte bazı oldukça standart senaryolar için bazı zamanlamalar: (% 100 - dizin yeniden oluşturma, varsayılan - istatistikler otomatik güncelleme veya UPDATE STATISTICSbelirli bir örnekleme hızı olmadan:

  • Fullscan ile Artımlı Olmayan İstatistikler Yaratın: CPU süresi = 23500 ms, geçen süre = 22521 ms.
  • Fullscan ile Artımlı İstatistik Oluşturma: CPU süresi = 20406 ms, geçen süre = 15413 ms.
  • Artımlı Olmayan İstatistiği Varsayılan Örnek Hızı ile Güncelleyin: CPU süresi = 406 ms, geçen süre = 408 ms.
  • Varsayılan Örnek Hızı ile Artımlı İstatistik Güncelleme: CPU süresi = 453 ms, geçen süre = 507 ms.

Diyelim ki bu varsayılan senaryolardan daha sofistike olduk ve% 10'luk bir örnekleme oranının, bakım süresini makul bir zaman diliminde tutarken bize gereken planlara ulaşması gereken minimum oran olduğuna karar verdik.

  • Artımlı Olmayan İstatistiği, yüzde 10 örnekle güncelleyin: CPU süresi = 2344 ms, geçen süre = 2441 ms.
  • Güncelleme Artımlı İstatistiği yüzde 10 örnekle: CPU süresi = 2344 ms, geçen süre = 2388 ms.

Şimdiye kadar artan bir istatistiklere sahip olmanın net bir faydası yok. Bununla birlikte, belgelendirilmemiş sys.dm_db_stats_properties_internal() DMV'den (aşağıda) yararlanırsak, hangi bölümleri güncellemek isteyebileceğiniz konusunda bir fikir edinebilirsiniz. Diyelim ki bölüm 3'teki verilerde değişiklikler yaptık ve istatistiklerin gelen sorgular için taze olmasını istiyoruz. İşte seçeneklerimiz:

  • Varsayılan Olarak Artımlı Olmayan Güncelle (ayrıca Otomatik İstatistik Güncelleştirmesi'nin varsayılan davranışı): 408 ms.
  • Artımlı Olmayan% 10'da güncelleme: 2441 ms.
  • Artımlı İstatistikleri Güncelle, Bölüm 3 Yeniden Örnekle (% 10 - tanımlanmış örnekleme oranımız): CPU süresi = 63 ms, geçen süre = 63 ms.

İşte karar vermemiz gereken yer. 63 ms kazanıyor muyuz? bölüm tabanlı istatistik güncellemeleri mi, yoksa örnekleme oranını daha da yükseltiyor muyuz? Diyelim ki, örneklemenin ilk vuruşunu% 50 oranında artan bir istatistikle almaya hazırız:

  • Artımlı İstatistikleri% 50'de güncelle: geçen süre = 16840 ms.
  • Artımlı İstatistikleri Güncelle, Örnek 3 ile Bölüm 3 (% 50 - yeni güncelleme zamanımız): geçen zaman = 295 ms.

Verilerimiz hakkında daha iyi tahminler yapmak için optimize ediciyi ayarlayarak çok daha fazla veri örnekleyebiliyoruz (henüz bölüm düzeyinde istatistikler kullanmasa da) ve bunu şimdi daha hızlı yapabiliyoruz. artımlı istatistikler.

Anlamak için son bir eğlenceli şey. Senkron istatistik güncellemeleri ne durumda? Autostatlar devreye girdiğinde bile% 50 numune oranı korunuyor mu?

3. bölümdeki verileri sildim ve CreationDate üzerinde bir sorgu çalıştırdım ve kontrol ettikten sonra oranları aynı sorgu ile kontrol ettim. % 50 numune oranı korunmuştur.

Bu yüzden, uzun lafın kısası: Artımlı İstatistikler, doğru miktarda düşünce ve ilk kurulum çalışması için yararlı bir araç olabilir. Ancak, çözmeye çalıştığınız sorunu bilmelisiniz ve ardından uygun şekilde çözmelisiniz. Kötü önem düzeyi tahminleri alıyorsanız, size olabilecek stratejik örnekleme oranı ve bazı yatırım müdahale ile daha iyi planlar elde edebilmek. Ancak, kullanılan histogram, bölüm düzeyinde bir bilgi değil, birleştirilmiş, birleştirilmiş istatistikler sayfası olduğundan, bu avantajın yalnızca küçük bir kısmını alıyorsunuz. Bakım pencerenizde ağrı hissediyorsanız, belki artımlı istatistikler size yardımcı olabilir, ancak muhtemelen yüksek dokunuşlu bir bakım müdahale süreci ayarlamanızı gerektirecektir. Ne olursa olsun,:

  • Temel tabloya göre bölümlere ayrılmayan indekslerle oluşturulan istatistikler.
  • AlwaysOn okunabilir ikincil veritabanlarında oluşturulan istatistikler.
  • Salt okunur veritabanlarında oluşturulan istatistikler.
  • Filtrelenmiş dizinlerde oluşturulan istatistikler.
  • İstatistikler görünümlerde oluşturuldu.
  • İç tablolarda oluşturulan istatistikler.
  • Uzamsal dizinler veya XML dizinleri ile oluşturulan istatistikler.

Bu yardımcı olur umarım

select 
    sysdatetime(),                          
    schema_name = sh.name,
    table_name = t.name,
    stat_name = s.name,
    index_name = i.name,
    leading_column = index_col(quotename(sh.name)+'.'+quotename(t.name),s.stats_id,1),
    s.stats_id,
    parition_number = isnull(sp.partition_number,1),
    s.has_filter,                       
    s.is_incremental,
    s.auto_created,
    sp.last_updated,    
    sp.rows,
    sp.rows_sampled,                        
    sp.unfiltered_rows,
    modification_counter = coalesce(sp.modification_counter, n1.modification_counter) 
from sys.stats s 
join sys.tables t 
    on s.object_id = t.object_id
join sys.schemas sh
    on t.schema_id = sh.schema_id
left join sys.indexes i 
    on s.object_id = i.object_id
        and s.name = i.name
cross apply sys.dm_db_stats_properties_internal(s.object_id, s.stats_id) sp
outer apply sys.dm_db_stats_properties_internal(s.object_id, s.stats_id) n1
where n1.node_id = 1
    and (
            (is_incremental = 0)
               or
            (is_incremental = 1 and sp.partition_number is not null)
         )
    and t.name = 'Posts'
    and s.name like 'st_posts%'
order by s.stats_id,isnull(sp.partition_number,1)
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.