Haruskah saya menonaktifkan "statistik pembaruan otomatis" dalam skenario pergudangan data?

12

Saya memiliki gudang data 200 GB di SQL Server.

Saya telah mengalami waktu eksekusi yang sangat lambat untuk beberapa permintaan; misalnya 12 jam untuk deletekueri sederhana dengan inner join.

Setelah melakukan riset dengan rencana eksekusi, saya memperbarui statistik dari 2 tabel yang terlibat dalam kueri, menggunakan WITH FULLSCANopsi.

Permintaan sekarang dijalankan dalam waktu kurang dari satu detik, sehingga tampaknya statistik tidak mutakhir.

Saya sedang mempertimbangkan menonaktifkan auto update statisticspada database dan berjalan UPDATE STATISTICSsecara manual setelah data warehouse dimuat. Gudang data dimuat secara bertahap dari sistem ERP sumber setiap hari, pada malam hari.

Apakah saya benar mengasumsikan auto update statisticsdalam skenario data pergudangan tidak benar-benar berguna? Sebaliknya, apakah lebih masuk akal untuk memperbarui statistik secara manual setelah data dimuat?

saso
sumber
Ini adalah bacaan yang sangat baik tentang statistik: simple-talk.com/sql/performance/… kami juga menjalankan pekerjaan sehari-hari menggunakan solusi Ola ola.hallengren.com/… untuk memperbarui statistik pada 1TB db. Saya tidak akan menonaktifkan opsi statistik pembaruan otomatis.
Joy Walker
1
Itu sangat tergantung pada berapa banyak catatan di tabel Anda dan berapa banyak yang Anda tambahkan dalam satu batch. Dalam tabel baris 1b tempat Anda menambahkan baris 20m per malam, statistik Anda akan diperbarui setiap 10 hari yang tidak terlalu bagus.
JNK
2
Hanya FYI - ada tanda jejak (2371) untuk mengubah ambang batas untuk pembaruan statistik dari tetap 20% menjadi tingkat persentase dinamis. Lihat lebih lanjut di sini: blogs.msdn.com/b/saponsqlserver/archive/2011/09/07/...
DaniSQL
Tautan yang sangat informatif, terima kasih! @ JNK: Ya, itu adalah DB besar. Tabel staging memiliki 300m + baris dan tergantung pada hari kami memasukkan sesuatu antara 1m hingga 10m setiap hari. Akan melihat lebih dekat pada statistik, seperti yang diusulkan oleh jawaban di bawah ini.
saso

Jawaban:

11

Ini adalah whitepaper pada saat auto_update statistik terjadi . Berikut adalah poin penting vis-a-vis pembaruan otomatis untuk statistik:

  • Ukuran tabel telah berubah dari 0 hingga> 0 baris (tes 1).
  • Jumlah baris dalam tabel ketika statistik dikumpulkan adalah 500 atau kurang, dan colmodctr dari kolom terkemuka objek statistik telah berubah lebih dari 500 sejak saat itu (tes 2).
  • Tabel memiliki lebih dari 500 baris ketika statistik dikumpulkan, dan colmodctr dari kolom terkemuka objek statistik telah berubah lebih dari 500 + 20% dari jumlah baris dalam tabel ketika statistik dikumpulkan (uji 3) .

Jadi @JNK membuat poin dalam komentar bahwa jika Anda memiliki 1 miliar baris dalam sebuah tabel, Anda harus memiliki 20.000.5000 tulisan di kolom pertama dalam statistik untuk memicu pembaruan.

Mari kita ambil struktur berikut:

CREATE TABLE dbo.test_table (
    test_table_id INTEGER IDENTITY(1,1) NOT NULL, 
    test_table_value VARCHAR(50), 
    test_table_value2 BIGINT, 
    test_table_value3 NUMERIC(10,2)
);

CREATE CLUSTERED INDEX cix_test_table ON dbo.test_table (test_table_id, test_table_value);

Sekarang kita dapat memeriksa untuk melihat apa yang terjadi di tanah statistik.

select * 
    from sys.stats
        where object_id = OBJECT_ID('dbo.test_table')

stat_container

Namun, untuk melihat apakah ini adalah objek statistik yang bermakna, kita perlu:

dbcc show_statistics('dbo.test_table',cix_test_table)

histogram

Jadi statistik ini belum diperbarui. Itu karena sepertinya statistik tidak diperbarui sampai SELECTterjadi dan itupun SELECTharus berada di luar apa yang SQL Server miliki dalam histogramnya. Berikut ini skrip pengujian yang saya jalankan untuk menguji ini:

    CREATE TABLE test_table (
        test_table_id INTEGER IDENTITY(1,1) NOT NULL, 
        test_table_value VARCHAR(50), 
        test_table_value2 BIGINT, 
        test_table_value3 NUMERIC(10,2)
    );

    CREATE CLUSTERED INDEX cix_test_table ON test_table (test_table_id, test_table_value);

    ALTER TABLE test_table ADD CONSTRAINT pk_test_table PRIMARY KEY  (test_table_id)

    SELECT * 
        FROM sys.stats
            WHERE object_id = OBJECT_ID('dbo.test_table')

    --DBCC SHOW_STATISTICS('dbo.test_table',pk_test_table)
    DBCC SHOW_STATISTICS('dbo.test_table',cix_test_table) WITH STAT_HEADER;

declare @test int = 0

WHILE @test < 1
    BEGIN
        INSERT INTO test_table (test_table_value,test_table_value2,test_table_value3) VALUES
            ('stats test' + CAST(@test AS VARCHAR(10)),@test, @test)
        SET @test = @test + 1;
    END

SELECT 'one row|select < 1', * FROM test_table WHERE test_table_id < 1;
--DBCC SHOW_STATISTICS('dbo.test_table',pk_test_table);
DBCC SHOW_STATISTICS('dbo.test_table',cix_test_table) WITH STAT_HEADER;

SET @test = 1

WHILE @test < 500
    BEGIN
        INSERT INTO test_table (test_table_value,test_table_value2,test_table_value3) VALUES
            ('stats test' + CAST(@test AS VARCHAR(10)),@test, @test)
        SET @test = @test + 1;
    END

SELECT '100 rows(add 99)|select < 100',* FROM test_table WHERE test_table_id < 100;
--DBCC SHOW_STATISTICS('dbo.test_table',pk_test_table);
DBCC SHOW_STATISTICS('dbo.test_table',cix_test_table) WITH STAT_HEADER;
--get the table up to 500 rows/changes
WHILE @test < 500
    BEGIN
        INSERT INTO test_table (test_table_value,test_table_value2,test_table_value3) VALUES
            ('stats test' + CAST(@test AS VARCHAR(10)),@test, @test)
        SET @test = @test + 1;
    END
SELECT '500 rows(add 400)|select < 100',* FROM test_table WHERE test_table_id < 100;
DBCC SHOW_STATISTICS('dbo.test_table',cix_test_table) WITH STAT_HEADER;
SELECT '500 rows(add 400)|select < 500',* FROM test_table WHERE test_table_id < 500;
--DBCC SHOW_STATISTICS('dbo.test_table',pk_test_table);
DBCC SHOW_STATISTICS('dbo.test_table',cix_test_table) WITH STAT_HEADER;
--bump it to 501
SET @test = 500;
WHILE @test < 501
    BEGIN
        INSERT INTO test_table (test_table_value,test_table_value2,test_table_value3) VALUES
            ('stats test' + CAST(@test AS VARCHAR(10)),@test, @test)
        SET @test = @test + 1;
    END


SELECT '501 rows(add 1)|select < 501',* FROM test_table WHERE test_table_id < 501;
--DBCC SHOW_STATISTICS('dbo.test_table',pk_test_table);
DBCC SHOW_STATISTICS('dbo.test_table',cix_test_table) WITH STAT_HEADER;

--bump it to 600
SET @test = 501;
WHILE @test < 600
    BEGIN
        INSERT INTO test_table (test_table_value,test_table_value2,test_table_value3) VALUES
            ('stats test' + CAST(@test AS VARCHAR(10)),@test, @test)
        SET @test = @test + 1;
    END

SELECT '600 rows (add 100)|select < 600',* FROM test_table WHERE test_table_id < 600;
--DBCC SHOW_STATISTICS('dbo.test_table',pk_test_table);
DBCC SHOW_STATISTICS('dbo.test_table',cix_test_table) WITH STAT_HEADER;

--bump it to 700
SET @test = 600;
WHILE @test < 700
    BEGIN
        INSERT INTO test_table (test_table_value,test_table_value2,test_table_value3) VALUES
            ('stats test' + CAST(@test AS VARCHAR(10)),@test, @test)
        SET @test = @test + 1;
    END

SELECT '700 rows (add 100)|select < 700', * FROM test_table WHERE test_table_id < 700;
--DBCC SHOW_STATISTICS('dbo.test_table',pk_test_table);
DBCC SHOW_STATISTICS('dbo.test_table',cix_test_table) WITH STAT_HEADER;

--bump it to 1200
SET @test = 700;
WHILE @test < 1200
    BEGIN
        INSERT INTO test_table (test_table_value,test_table_value2,test_table_value3) VALUES
            ('stats test' + CAST(@test AS VARCHAR(10)),@test, @test)
        SET @test = @test + 1;
    END

SELECT '1200 rows (add 500)|select < 1200',* FROM test_table WHERE test_table_id < 1200;
--DBCC SHOW_STATISTICS('dbo.test_table',pk_test_table);
DBCC SHOW_STATISTICS('dbo.test_table',cix_test_table) WITH STAT_HEADER;
--DROP TABLE test_table

Alih-alih menonaktifkan statistik auto_update secara membabi buta, saya akan mencoba memeriksa set data Anda untuk condong. Jika data Anda menunjukkan kemiringan yang signifikan, maka Anda perlu mempertimbangkan untuk membuat statistik yang difilter dan kemudian memutuskan apakah mengelola pembaruan statistik secara manual adalah tindakan yang benar.

Untuk menganalisis kemiringan Anda perlu menjalankan DBCC SHOW_STATISTICS(<stat_object>, <index_name>);(dalam skrip di atas tanpa WITH STAT_HEADER) pada kombinasi stat / indeks tertentu yang ingin Anda periksa. Cara cepat untuk melihat kemiringan Anda adalah dengan melihat histogram (set hasil ketiga) dan memeriksa varians di EQ_ROWS. Jika itu cukup konsisten maka kemiringan Anda minimal. Untuk meningkatkannya, Anda melihat RANGE_ROWSkolom dan melihat varians di sana karena ini mengukur berapa banyak baris yang ada di antara setiap langkah. Akhirnya, Anda bisa mengambil [All density]hasil dari DENSITY_VECTOR(set hasil kedua) dan mengalikannya dengan [Rows Sampled]nilai dalam STAT_HEADER(set hasil pertama) dan melihat apa yang diharapkan rata-rata untuk kueri pada kolom itu. Anda membandingkan rata-rata itu dengan AndaEQ_ROWS dan jika ada banyak tempat di mana itu bervariasi secara signifikan, maka Anda punya kecenderungan.

Jika ternyata Anda memiliki kemiringan, maka Anda perlu mempertimbangkan untuk membuat beberapa statistik yang difilter pada rentang yang sangat tinggi RANGE_ROWSsehingga Anda dapat memberikan langkah-langkah tambahan untuk perkiraan yang lebih baik pada nilai-nilai tersebut.

Setelah Anda memiliki statistik yang disaring ini, maka Anda dapat melihat kemungkinan memperbarui statistik secara manual.

swasheck
sumber
Terima kasih atas jawaban komprehensifnya. Saya tidak punya banyak pengalaman dengan statistik dan ini tampaknya lebih rumit seperti yang saya pikirkan. Pasti akan melihat lebih dekat dan mengikuti panduan Anda.
saso