Saya telah melihat masalah kerapatan yang sama pada beberapa indeks nonclustered pada database terbesar yang saya miliki aksesnya. Pertama saya akan mulai dengan beberapa pengamatan yang telah saya lakukan tentang histogram dan perhitungan kepadatan:
- SQL Server dapat menggunakan kunci utama di atas meja untuk menyimpulkan sesuatu tentang kepadatan kedua kolom. Ini berarti bahwa kepadatan yang mencakup kolom PK biasanya akan sangat akurat.
- Perhitungan kepadatan untuk kolom pertama dalam statistik konsisten dengan histogram. Jika histogram tidak memodelkan data dengan baik maka kerapatan mungkin tidak aktif.
- Untuk membuat histogram,
StatMan
fungsi membuat kesimpulan tentang data yang hilang. Perilaku dapat berubah tergantung pada tipe data kolom.
Untuk satu cara untuk melihat masalah, anggaplah Anda sampel 100 baris dari tabel 10.000 baris dan Anda mendapatkan 100 nilai yang berbeda. Satu tebakan pada apa sisa data dalam tabel adalah bahwa ada 10.000 nilai unik. Dugaan lain adalah bahwa ada 100 nilai yang berbeda tetapi masing-masing nilai diulang 100 kali. Tebakan kedua mungkin tampak tidak masuk akal bagi Anda, yang akan saya setujui. Namun, bagaimana Anda menyeimbangkan kedua pendekatan ketika data sampel kembali tidak terdistribusi secara merata? Ada beberapa set algoritma yang dikembangkan untuk ini oleh Microsoft yang terkandung dalam StatMan
fungsi. Algoritme mungkin tidak berfungsi untuk semua gangguan data dan semua level sampel.
Mari kita lihat contoh yang relatif sederhana. Saya akan menggunakan VARCHAR
kolom seperti di meja Anda untuk melihat beberapa perilaku yang sama. Namun, saya hanya akan menambahkan satu nilai miring ke tabel. Saya sedang menguji terhadap SQL Server 2016 SP1. Mulai dengan 100rb baris dengan 100rb nilai unik untuk FK
kolom:
DROP TABLE IF EXISTS X_STATS_SMALL;
CREATE TABLE X_STATS_SMALL (
ID VARCHAR(10) NOT NULL,
FK VARCHAR(10) NOT NULL,
PADDING VARCHAR(900) NOT NULL,
PRIMARY KEY (ID)
);
-- insert 100k rows
INSERT INTO X_STATS_SMALL WITH (TABLOCK)
SELECT N, N, REPLICATE('Z', 900)
FROM dbo.GetNums(100000);
CREATE INDEX IX_X_STATS_SMALL ON X_STATS_SMALL (FK);
-- get sampled stats
UPDATE STATISTICS X_STATS_SMALL IX_X_STATS_SMALL;
Berikut ini beberapa contoh dari statistik:
╔═════════════╦════════════════╦═════════╗
║ All density ║ Average Length ║ Columns ║
╠═════════════╬════════════════╬═════════╣
║ 1.00001E-05 ║ 4.888205 ║ FK ║
║ 1.00001E-05 ║ 9.77641 ║ FK, ID ║
╚═════════════╩════════════════╩═════════╝
╔══════════════╦════════════╦═════════╦═════════════════════╦════════════════╗
║ RANGE_HI_KEY ║ RANGE_ROWS ║ EQ_ROWS ║ DISTINCT_RANGE_ROWS ║ AVG_RANGE_ROWS ║
╠══════════════╬════════════╬═════════╬═════════════════════╬════════════════╣
║ 1005 ║ 0 ║ 1 ║ 0 ║ 1 ║
║ 10648 ║ 665.0898 ║ 1 ║ 664 ║ 1.002173 ║
║ 10968 ║ 431.6008 ║ 1 ║ 432 ║ 1 ║
║ 11182 ║ 290.0924 ║ 1 ║ 290 ║ 1 ║
║ 1207 ║ 445.7517 ║ 1 ║ 446 ║ 1 ║
║ ... ║ ... ║ ... ║ ... ║ ... ║
║ 99989 ║ 318.3941 ║ 1 ║ 318 ║ 1 ║
╚══════════════╩════════════╩═════════╩═════════════════════╩════════════════╝
Untuk data yang terdistribusi secara merata dengan satu nilai unik per baris, kami mendapatkan kerapatan yang akurat, bahkan dengan VARCHAR
kolom histogram dan ukuran sampel 14294 baris.
Sekarang mari kita tambahkan nilai miring dan perbarui statistik lagi:
-- add 70k rows with a FK value of '35000'
INSERT INTO X_STATS_SMALL WITH (TABLOCK)
SELECT N + 100000 , '35000', REPLICATE('Z', 900)
FROM dbo.GetNums(70000);
UPDATE STATISTICS X_STATS_SMALL IX_X_STATS_SMALL;
Dengan ukuran sampel 17010 baris, kepadatan kolom pertama lebih kecil dari seharusnya:
╔══════════════╦════════════════╦═════════╗
║ All density ║ Average Length ║ Columns ║
╠══════════════╬════════════════╬═════════╣
║ 6.811061E-05 ║ 4.935802 ║ FK ║
║ 5.882353E-06 ║ 10.28007 ║ FK, ID ║
╚══════════════╩════════════════╩═════════╝
╔══════════════╦════════════╦══════════╦═════════════════════╦════════════════╗
║ RANGE_HI_KEY ║ RANGE_ROWS ║ EQ_ROWS ║ DISTINCT_RANGE_ROWS ║ AVG_RANGE_ROWS ║
╠══════════════╬════════════╬══════════╬═════════════════════╬════════════════╣
║ 10039 ║ 0 ║ 1 ║ 0 ║ 1 ║
║ 10978 ║ 956.9945 ║ 1 ║ 138 ║ 6.954391 ║
║ 11472 ║ 621.0283 ║ 1 ║ 89 ║ 6.941863 ║
║ 1179 ║ 315.6046 ║ 1 ║ 46 ║ 6.907561 ║
║ 11909 ║ 91.62713 ║ 1 ║ 14 ║ 6.74198 ║
║ ... ║ ... ║ ... ║ ... ║ ... ║
║ 35000 ║ 376.6893 ║ 69195.05 ║ 54 ║ 6.918834 ║
║ ... ║ ... ║ ... ║ ... ║ ... ║
║ 99966 ║ 325.7854 ║ 1 ║ 47 ║ 6.909731 ║
╚══════════════╩════════════╩══════════╩═════════════════════╩════════════════╝
Mengejutkan bahwa AVG_RANGE_ROWS
seragam ini cukup seragam untuk semua langkah di sekitar 6,9, bahkan untuk ember kunci yang sampelnya tidak dapat menemukan nilai duplikat. Saya tidak tahu mengapa ini terjadi. Penjelasan yang paling mungkin adalah bahwa algoritma yang digunakan untuk menebak halaman yang hilang tidak bekerja dengan baik dengan distribusi data dan ukuran sampel ini.
Seperti yang dinyatakan sebelumnya, adalah mungkin untuk menghitung kepadatan untuk kolom FK menggunakan histogram. Jumlah DISTINCT_RANGE_ROWS
nilai untuk semua langkah adalah 14497. Ada 179 langkah histogram sehingga kepadatannya harus sekitar 1 / (179 + 14497) = 0,00006813845 yang cukup dekat dengan nilai yang dilaporkan.
Pengujian dengan tabel yang lebih besar dapat menunjukkan bagaimana masalah bisa menjadi lebih buruk karena tabel semakin besar. Kali ini kita akan mulai dengan 1 baris M:
DROP TABLE IF EXISTS X_STATS_LARGE;
CREATE TABLE X_STATS_LARGE (
ID VARCHAR(10) NOT NULL,
FK VARCHAR(10) NOT NULL,
PADDING VARCHAR(900) NOT NULL,
PRIMARY KEY (ID));
INSERT INTO X_STATS_LARGE WITH (TABLOCK)
SELECT N, N, REPLICATE('Z', 900)
FROM dbo.Getnums(1000000);
CREATE INDEX IX_X_STATS_LARGE ON X_STATS_LARGE (FK);
-- get sampled stats
UPDATE STATISTICS X_STATS_LARGE IX_X_STATS_LARGE;
Objek statistik belum menarik. Densitas untuk FK
adalah 1.025289E-06 yang mendekati tepat (1.0E-06).
Sekarang mari kita tambahkan nilai miring dan perbarui statistik lagi:
INSERT INTO X_STATS_LARGE WITH (TABLOCK)
SELECT N + 1000000 , '350000', REPLICATE('Z', 900)
FROM dbo.Getnums(700000);
UPDATE STATISTICS X_STATS_LARGE IX_X_STATS_LARGE;
Dengan ukuran sampel 45627 baris, kepadatan kolom pertama lebih buruk daripada sebelumnya:
╔══════════════╦════════════════╦═════════╗
║ All density ║ Average Length ║ Columns ║
╠══════════════╬════════════════╬═════════╣
║ 2.60051E-05 ║ 5.93563 ║ FK ║
║ 5.932542E-07 ║ 12.28485 ║ FK, ID ║
╚══════════════╩════════════════╩═════════╝
╔══════════════╦════════════╦═════════╦═════════════════════╦════════════════╗
║ RANGE_HI_KEY ║ RANGE_ROWS ║ EQ_ROWS ║ DISTINCT_RANGE_ROWS ║ AVG_RANGE_ROWS ║
╠══════════════╬════════════╬═════════╬═════════════════════╬════════════════╣
║ 100023 ║ 0 ║ 1 ║ 0 ║ 1 ║
║ 107142 ║ 8008.354 ║ 1 ║ 306 ║ 26.17787 ║
║ 110529 ║ 4361.357 ║ 1 ║ 168 ║ 26.02392 ║
║ 114558 ║ 3722.193 ║ 1 ║ 143 ║ 26.01217 ║
║ 116696 ║ 2556.658 ║ 1 ║ 98 ║ 25.97568 ║
║ ... ║ ... ║ ... ║ ... ║ ... ║
║ 350000 ║ 5000.522 ║ 700435 ║ 192 ║ 26.03268 ║
║ ... ║ ... ║ ... ║ ... ║ ... ║
║ 999956 ║ 2406.266 ║ 1 ║ 93 ║ 25.96841 ║
╚══════════════╩════════════╩═════════╩═════════════════════╩════════════════╝
AVG_RANGE_ROWS
hingga 26. Menariknya, jika saya mengubah ukuran sampel ke 170100 baris (10X tabel lainnya) maka nilai rata-rata untuk AVG_RANGE_ROWS
lagi tepat di sekitar 6,9. Ketika tabel Anda bertambah besar, SQL Server akan memilih ukuran sampel yang lebih kecil yang berarti perlu membuat perkiraan tentang persentase halaman yang lebih besar dalam tabel. Ini dapat membesar-besarkan masalah statistik untuk jenis data tertentu yang condong.
Kesimpulannya, penting untuk diingat bahwa SQL Server tidak menghitung kepadatan seperti ini:
SELECT COUNT(DISTINCT FK) * 1700000. / COUNT(*) -- 1071198.9 distinct values for one run
FROM X_STATS_LARGE TABLESAMPLE (45627 ROWS);
Yang untuk beberapa distribusi data akan sangat akurat. Sebaliknya ia menggunakan algoritma tidak berdokumen . Dalam pertanyaan Anda, Anda mengatakan bahwa data Anda tidak condong, tetapi INSTANCEELEMENTID
nilai dengan jumlah tertinggi ID terkait memiliki 12 dan angka paling umum adalah 1. Untuk keperluan algoritma yang digunakan oleh Statman
yang dapat miring.
Pada saat itu tidak ada yang dapat Anda lakukan kecuali mengumpulkan statistik dengan laju sampel yang lebih tinggi. Salah satu strategi umum adalah untuk mengumpulkan statistik dengan FULLSCAN
dan NORECOMPUTE
. Anda dapat menyegarkan statistik dengan pekerjaan pada interval apa pun yang masuk akal untuk tingkat perubahan data Anda. Dalam pengalaman saya, FULLSCAN
pembaruan tidak seburuk yang dipikirkan kebanyakan orang, terutama terhadap indeks. SQL Server hanya dapat memindai seluruh indeks, bukan seluruh tabel (seperti yang akan dilakukan untuk tabel rowstore terhadap kolom yang tidak diindeks). Selain itu, di SQL Serer 2014 hanya FULLSCAN
pembaruan statistik yang dilakukan secara paralel, sehingga FULLSCAN
pembaruan dapat selesai lebih cepat daripada beberapa pembaruan sampel.
tablesample