Saya mencoba untuk membujuk beberapa kinerja lagi dari permintaan yang mengakses tabel dengan ~ 250 juta catatan. Dari pembacaan saya tentang rencana pelaksanaan aktual (tidak diperkirakan), hambatan pertama adalah kueri yang terlihat seperti ini:
select
b.stuff,
a.added,
a.value
from
dbo.hugetable a
inner join
#smalltable b on a.fk = b.pk
where
a.added between @start and @end;
Lihat lebih jauh ke bawah untuk definisi tabel & indeks yang terlibat.
Rencana eksekusi menunjukkan bahwa loop bersarang sedang digunakan di #smalltable, dan bahwa pemindaian indeks melalui hugetable dieksekusi 480 kali (untuk setiap baris di #smalltable). Ini kelihatannya terbalik bagi saya, jadi saya telah mencoba untuk memaksa gabungan bergabung untuk digunakan sebagai gantinya:
select
b.stuff,
a.added,
a.value
from
dbo.hugetable a with(index = ix_hugetable)
inner merge join
#smalltable b with(index(1)) on a.fk = b.pk
where
a.added between @start and @end;
Indeks yang dimaksud (lihat definisi lengkap di bawah) mencakup kolom fk (predikat gabungan), ditambahkan (digunakan dalam klausa where) & id (tidak berguna) dalam urutan menaik, dan termasuk nilai .
Namun, ketika saya melakukan ini, kueri meledak dari 2 1/2 menit menjadi lebih dari 9. Saya berharap bahwa petunjuk akan memaksa bergabung lebih efisien yang hanya melakukan satu melewati setiap tabel, tetapi jelas tidak.
Bimbingan apa pun diterima. Informasi tambahan disediakan jika diperlukan.
Pembaruan (2011/06/02)
Setelah mengatur ulang pengindeksan di atas meja, saya telah membuat terobosan kinerja yang signifikan, namun saya telah mengalami hambatan baru ketika meringkas data dalam tabel besar. Hasilnya adalah ringkasan berdasarkan bulan, yang saat ini terlihat seperti berikut:
select
b.stuff,
datediff(month, 0, a.added),
count(a.value),
sum(case when a.value > 0 else 1 end) -- this triples the running time!
from
dbo.hugetable a
inner join
#smalltable b on a.fk = b.pk
group by
b.stuff,
datediff(month, 0, a.added);
Saat ini, hugetable memiliki indeks berkerumun pk_hugetable (added, fk)
(kunci utama), dan indeks non-berkerumun menuju sebaliknya ix_hugetable (fk, added)
.
Tanpa kolom ke-4 di atas, pengoptimal menggunakan gabungan loop bersarang seperti sebelumnya, menggunakan #smalltable sebagai input luar, dan indeks non-cluster mencari sebagai loop dalam (mengeksekusi 480 kali lagi). Yang mengkhawatirkan saya adalah perbedaan antara baris yang diperkirakan (12.958,4) dan baris aktual (74.668.468). Biaya relatif dari upaya ini adalah 45%. Namun waktu berjalan kurang dari satu menit.
Dengan kolom ke-4, waktu berjalan meningkat menjadi 4 menit. Itu mencari pada indeks berkerumun kali ini (2 eksekusi) untuk biaya relatif yang sama (45%), agregat melalui pertandingan hash (30%), kemudian melakukan hash bergabung di #smalltable (0%).
Saya tidak yakin dengan tindakan selanjutnya. Kekhawatiran saya adalah bahwa baik pencarian rentang tanggal maupun predikat gabungan tidak dijamin atau bahkan semua yang mungkin secara drastis mengurangi set hasil. Rentang tanggal dalam kebanyakan kasus hanya akan memangkas mungkin 10-15% dari catatan, dan gabungan dalam pada fk dapat menyaring mungkin 20-30%.
Seperti yang diminta oleh Will A, hasil dari sp_spaceused
:
name | rows | reserved | data | index_size | unused
hugetable | 261774373 | 93552920 KB | 18373816 KB | 75167432 KB | 11672 KB
#smalltable didefinisikan sebagai:
create table #endpoints (
pk uniqueidentifier primary key clustered,
stuff varchar(6) null
);
Sedangkan dbo.hugetable didefinisikan sebagai:
create table dbo.hugetable (
id uniqueidentifier not null,
fk uniqueidentifier not null,
added datetime not null,
value decimal(13, 3) not null,
constraint pk_hugetable primary key clustered (
fk asc,
added asc,
id asc
)
with (
pad_index = off, statistics_norecompute = off,
ignore_dup_key = off, allow_row_locks = on,
allow_page_locks = on
)
on [primary]
)
on [primary];
Dengan indeks berikut ditentukan:
create nonclustered index ix_hugetable on dbo.hugetable (
fk asc, added asc, id asc
) include(value) with (
pad_index = off, statistics_norecompute = off,
sort_in_tempdb = off, ignore_dup_key = off,
drop_existing = off, online = off,
allow_row_locks = on, allow_page_locks = on
)
on [primary];
Bidang id adalah redundan, sebuah artefak dari DBA sebelumnya yang bersikeras bahwa semua tabel di mana saja harus memiliki GUID, tanpa pengecualian.
sumber
Jawaban:
ix_hugetable
Penampilan Anda tidak berguna karena:Selain itu: - ditambahkan atau fk harus menjadi yang pertama - ID lebih dulu = tidak banyak digunakan
Coba ubah kunci yang dikelompokkan ke
(added, fk, id)
dan lepasix_hugetable
. Anda sudah mencoba(fk, added, id)
. Jika tidak ada yang lain, Anda akan menghemat banyak ruang disk dan pemeliharaan indeksPilihan lain mungkin untuk mencoba petunjuk FORCE ORDER dengan urutan tabel dengan cara boh dan tidak ada petunjuk GABUNG / INDEKS. Saya mencoba untuk tidak menggunakan petunjuk GABUNG / INDEKS secara pribadi karena Anda menghapus opsi untuk pengoptimal. Bertahun-tahun yang lalu saya diberitahu (seminar dengan SQL Guru) bahwa petunjuk FORCE ORDER dapat membantu ketika Anda memiliki meja besar GABUNG meja kecil: YMMV 7 tahun kemudian ...
Oh, dan beri tahu kami di mana DBA tinggal sehingga kami dapat mengatur beberapa penyesuaian perkusi
Edit, setelah pembaruan 02 Juni
Kolom ke-4 bukan bagian dari indeks non-clustered sehingga menggunakan indeks clustered.
Coba ubah indeks NC ke TERMASUK kolom nilai sehingga tidak perlu mengakses kolom nilai untuk indeks berkerumun
Catatan: Jika nilainya tidak nullable maka sama dengan
COUNT(*)
semantik. Tetapi untuk SUM itu membutuhkan nilai aktual , bukan keberadaan .Sebagai contoh, jika Anda mengubah
COUNT(value)
keCOUNT(DISTINCT value)
tanpa mengubah indeks itu harus istirahat query lagi karena memiliki memproses nilai sebagai nilai, bukan sebagai eksistensi.Permintaan membutuhkan 3 kolom: ditambahkan, fk, nilai. 2 yang pertama difilter / digabung begitu juga kolom kunci. nilai hanya digunakan sehingga bisa dimasukkan. Penggunaan klasik dari indeks penutup.
sumber
Tentukan indeks
hugetable
hanya padaadded
kolom.DB akan menggunakan indeks multi-bagian (multi-kolom) hanya sejauh kanan dari daftar kolom karena memiliki nilai yang dihitung dari kiri. Kueri Anda tidak menentukan
fk
di mana klausa kueri pertama, sehingga mengabaikan indeks.sumber
Ini adalah urutan yang saya harapkan akan digunakan pengoptimal kueri, dengan asumsi bahwa sebuah loop bergabung dalam pilihan yang tepat. Alternatifnya adalah untuk mengulang sebanyak 250 juta kali dan melakukan pencarian ke tabel #temp setiap kali - yang bisa memakan waktu berjam-jam / hari.
Indeks yang Anda paksakan untuk digunakan dalam gabungan MERGE cukup banyak baris 250M * 'ukuran setiap baris' - tidak kecil, setidaknya beberapa GB. Dilihat dari
sp_spaceused
output 'sepasang GB' mungkin cukup meremehkan - penggabungan MERGE mengharuskan Anda menjelajah melalui indeks yang akan menjadi sangat intensif I / O.sumber
Indeks Anda salah. Lihat indeks dos dan larangan .
Pada dasarnya, satu-satunya indeks berguna Anda adalah pada kunci utama tabel kecil itu. Dengan demikian, satu-satunya rencana yang masuk akal adalah dengan memindai tabel kecil dan menumpuk kekacauan dengan tabel besar.
Coba tambahkan indeks berkerumun di
hugetable(added, fk)
. Ini harus membuat perencana mencari baris yang berlaku dari tabel besar, dan loop sarang atau bergabung bergabung dengan mereka dengan tabel kecil.sumber