Statistik hilang setelah pembaruan tambahan


21

Kami memiliki database SQL Server yang dipartisi besar menggunakan statistik tambahan. Semua indeks dipartisi selaras. Ketika kami mencoba untuk membangun kembali sebuah partisi secara online dengan partisi semua statistik hilang setelah indeks dibangun kembali.

Di bawah ini adalah skrip untuk mereplikasi masalah di SQL Server 2014 dengan database AdventureWorks2014.

--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!!!!!!'

Seperti yang ditunjukkan, kami tidak dapat membangun kembali indeks berdasarkan partisi online tanpa kehilangan semua statistik untuk indeks. Ini adalah masalah perawatan utama bagi kami. Tampaknya hampir bahwa opsi statistik tambahan perlu menjadi bagian dari sintaks indeks ulang tunggal atau opsi online perlu menanganinya dengan benar seperti yang dilakukan opsi offline.

Tolong beri tahu saya jika saya kehilangan sesuatu?

Pembaruan:

Sejauh kebutuhan kami akan statistik tambahan: Kami mempartisi berdasarkan id pelanggan internal dan bukan tanggal. Jadi, ketika klien baru dimasukkan (data back-load besar) kami cukup memperbarui statistik untuk partisi dan dengan cepat menghindari rencana buruk yang dibuat untuk pelanggan baru ini. Saya pikir saya akan mengajukannya dengan Microsoft sebagai bug dan melihat apa yang mereka katakan dan pergi dengan solusi hanya sampling ulang statistik untuk partisi itu.

Hubungkan laporan bug:

Statistik menghilang setelah indeks online dibangun kembali dengan statistik tambahan

Pembaruan: Microsoft telah mengkonfirmasi bahwa itu adalah bug.


1
Pembaruan: Microsoft mengirimi saya email pagi ini bahwa bug ini akan diperbaiki pada pembaruan CU berikutnya untuk SQL 2014.
JasonR

Anda tahu CU mana yang memperbaikinya, atau apa KB yang mereka laporkan dalam email itu? Mencoba melihat kapan diperbaiki.
mbourgon

1
Cukup yakin itu adalah bug nomor VSTS 8046729 KB nomor Artikel 3194959 yang merupakan bagian dari CU 9 untuk SQL Server 2014 SP1. Tautan ke KB ada di sini .
JasonR

Ya, sepertinya - dan baru saja diperbaiki pada 2016SP1 bulan lalu. BANYAK BANYAK terima kasih!
mbourgon

Koreksi: baru saja diperbaiki pada 2016 SP1 CU2. Itu terjadi pada 2016 SP1 CU1.
mbourgon

Jawaban:


17

Tidak yakin apakah itu bug, per se tapi itu jelas suatu kejadian yang menarik. Rekondisi partisi online baru di SQL Server 2014 sehingga mungkin ada beberapa internal untuk menyortir dengan ini.

Inilah penjelasan terbaik saya untuk Anda. Statistik tambahan benar-benar mengharuskan semua partisi disampel pada tingkat yang sama sehingga ketika mesin menggabungkan halaman statistik, dapat yakin bahwa distribusi sampel dapat dibandingkan. REBUILDtentu sampel data pada tingkat sampel 100%. Tidak ada jaminan bahwa laju sampel 100% pada partisi 9 akan selalu menjadi laju sampel yang tepat dari sisa partisi. Karena ini, sepertinya mesin tidak dapat menggabungkan sampel dan Anda berakhir dengan gumpalan statistik kosong. Namun, objek statistik masih ada:

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'

Anda dapat mengisi gumpalan melalui sejumlah cara:

UPDATE STATISTICS dbo.TransactionHistory (IDX_ProductId) WITH RESAMPLE;

atau

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

atau Anda bisa menunggu AutoStats untuk memperbarui pada kompilasi pertama dari rencana permintaan menggunakan objek itu:

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

Setelah mengatakan semua itu, pos yang mencerahkan ini oleh Erin Stellato menyoroti apa yang kemudian dianggap sebagai kekurangan utama dari statistik tambahan. Data tingkat partisi mereka tidak digunakan oleh pengoptimal dalam pembuatan paket kueri, mengurangi manfaat statistik tambahan yang diperkirakan. Lalu, apa manfaat statistik inkremental saat ini? Saya akan menyampaikan bahwa utilitas utama mereka adalah dalam kemampuan untuk sampel tabel besar lebih konsisten pada tingkat yang lebih tinggi daripada dengan statistik tradisional.

Menggunakan contoh Anda, beginilah tampilannya:

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.

Pembaruan statistik fullscan pada tambahan biaya statistik 131 ms. Pembaruan statistik fullscan pada statistik non-partisi-aligned 66 ms. Statistik yang tidak selaras lebih lambat kemungkinan besar karena biaya overhead yang dikeluarkan dengan menggabungkan halaman statistik individu kembali ke histogram utama. Namun, menggunakan objek statistik partisi-sejajar, kita dapat memperbarui satu partisi dan menggabungkannya kembali ke gumpalan histogram utama dalam 5 ms. Jadi pada titik ini administrator dengan statistik tambahan dihadapkan dengan keputusan. Mereka dapat mengurangi waktu pemeliharaan statistik keseluruhan dengan hanya memperbarui partisi yang secara tradisional perlu diperbarui, atau mereka dapat bereksperimen dengan laju sampel yang lebih tinggi sehingga mereka berpotensi mendapatkan lebih banyak sampel baris dalam periode waktu yang sama dengan jangka waktu pemeliharaan sebelumnya. Yang pertama memungkinkan ruang bernapas di jendela pemeliharaan, yang terakhir mungkin mendorong statistik di meja yang sangat besar ke tempat di mana pertanyaan mendapatkan rencana yang lebih baik berdasarkan statistik yang lebih akurat. Ini bukan jaminan dan jarak tempuh Anda mungkin beragam.

Pembaca dapat melihat bahwa 66 ms bukan waktu pembaruan statistik yang menyakitkan pada tabel ini, jadi saya mencoba menyiapkan tes pada set data stackexchange. Ada 6.418.608 posting (tidak termasuk posting StackOverflow dan semua posting dari 2012 - kesalahan data di pihak saya) di dump terbaru yang saya unduh.

Saya telah mempartisi data oleh [CreationDate]karena ... demo.

Berikut adalah beberapa pengaturan waktu untuk beberapa skenario standar yang cantik (100% - pembangunan kembali indeks, default - statistik pembaruan otomatis atau UPDATE STATISTICStanpa laju sampel yang ditentukan:

  • Buat Statistik Non-Incremental dengan Fullscan: Waktu CPU = 23500 ms, waktu yang berlalu = 22521 ms.
  • Buat Statistik Incremental Dengan Fullscan: Waktu CPU = 20406 ms, waktu yang berlalu = 15413 ms.
  • Perbarui Statistik Non-Tambahan dengan Tingkat Sampel Default: Waktu CPU = 406 ms, waktu yang berlalu = 408 ms.
  • Perbarui Statistik Tambahan dengan Tingkat Sampel Default: Waktu CPU = 453 ms, waktu yang berlalu = 507 ms.

Katakanlah kita lebih canggih dari skenario default ini dan telah memutuskan bahwa tingkat sampel 10% adalah tingkat minimum yang seharusnya memberi kita rencana yang kita butuhkan sambil menjaga waktu pemeliharaan ke jangka waktu yang masuk akal.

  • Perbarui Statistik Non-Incremental dengan sampel 10 persen: Waktu CPU = 2344 ms, waktu yang berlalu = 2441 ms.
  • Perbarui Statistik Tambahan dengan sampel 10 persen: Waktu CPU = 2344 ms, waktu yang berlalu = 2388 ms.

Sejauh ini tidak ada manfaat yang jelas untuk memiliki statistik tambahan. Namun, jika kami memanfaatkan DMV yang tidak berdokumen sys.dm_db_stats_properties_internal() (di bawah), Anda bisa mendapatkan wawasan tentang partisi mana yang ingin Anda perbarui. Katakanlah kami membuat perubahan pada data di partisi 3 dan kami ingin memastikan statistik baru untuk kueri yang masuk. Berikut ini pilihan kami:

  • Perbarui Non-Incremental di Default (juga perilaku default dari Pembaruan Statistik Otomatis): 408 ms.
  • Perbarui Non-inkremental pada 10%: 2441 ms.
  • Perbarui Statistik Tambahan, Partisi 3 Dengan Sampel Ulang (10% - laju sampel yang ditentukan kami): Waktu CPU = 63 ms, waktu yang berlalu = 63 ms.

Di sinilah kita perlu mengambil keputusan. Apakah kita mengambil kemenangan 63 ms. pembaruan statistik berbasis partisi, atau apakah kita meningkatkan laju sampel lebih tinggi? Katakanlah kita bersedia untuk mengambil hit sampel awal sebesar 50% pada statistik tambahan:

  • Perbarui Statistik Tambahan pada 50%: waktu yang berlalu = 16840 ms.
  • Perbarui Statistik Tambahan, Partisi 3 dengan Resample (50% - waktu pembaruan baru kami): waktu yang berlalu = 295 ms.

Kami dapat mengambil sampel lebih banyak data, mungkin menyiapkan pengoptimal untuk membuat perkiraan yang lebih baik tentang data kami (meskipun belum menggunakan statistik tingkat partisi) dan kami dapat melakukan ini lebih cepat sekarang karena kami memiliki statistik tambahan.

Namun, satu hal yang menyenangkan untuk dipecahkan. Bagaimana dengan pembaruan statistik sinkron? Apakah tingkat sampel 50% dipertahankan bahkan ketika autostats masuk?

Saya menghapus data dari partisi 3 dan menjalankan kueri pada CreationDate dan memeriksa kemudian memeriksa tarif dengan kueri yang sama di bawah ini. Tingkat sampel 50% dipertahankan.

Jadi, cerita panjang pendek: Statistik tambahan dapat menjadi alat yang berguna dengan jumlah pemikiran yang tepat dan pekerjaan pengaturan awal. Namun, Anda harus tahu masalah yang Anda coba selesaikan dan kemudian Anda harus menyelesaikannya dengan tepat. Jika Anda mendapatkan perkiraan kardinalitas yang buruk, Anda mungkin bisa mendapatkan rencana yang lebih baik dengan laju sampel strategis dan beberapa intervensi yang diinvestasikan. Namun, Anda hanya mendapatkan sebagian kecil dari manfaatnya karena histogram yang digunakan adalah halaman statistik tunggal yang digabungkan dan bukan informasi tingkat partisi. Jika Anda merasa sakit di jendela pemeliharaan, maka mungkin statistik tambahan dapat membantu Anda, tetapi mungkin akan mengharuskan Anda menyiapkan proses intervensi pemeliharaan sentuhan tinggi. Bagaimanapun,:

  • Statistik dibuat dengan indeks yang tidak disejajarkan dengan tabel dasar.
  • Statistik dibuat pada database sekunder AlwaysOn yang dapat dibaca.
  • Statistik dibuat pada basis data baca-saja.
  • Statistik dibuat pada indeks yang difilter.
  • Statistik dibuat pada tampilan.
  • Statistik dibuat di tabel internal.
  • Statistik dibuat dengan indeks spasial atau indeks XML.

Semoga ini membantu

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)
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.