Menggunakan tampilan yang diindeks untuk agregat - terlalu bagus untuk menjadi kenyataan?

28

Kami memiliki gudang data dengan jumlah catatan yang cukup besar (10-20 juta baris) dan sering menjalankan kueri yang menghitung catatan antara tanggal tertentu, atau menghitung catatan dengan bendera tertentu, misalnya

SELECT
    f.IsFoo,
    COUNT(*) AS WidgetCount
FROM Widgets AS w
JOIN Flags AS f
    ON f.FlagId = w.FlagId
WHERE w.Date >= @startDate
GROUP BY f.IsFoo

Performanya tidak buruk, tetapi bisa relatif lamban (mungkin 10 detik pada cache dingin).

Baru-baru ini saya menemukan bahwa saya dapat menggunakan GROUP BYdalam tampilan yang diindeks dan mencoba sesuatu yang mirip dengan yang berikut ini

CREATE VIEW TestView
WITH SCHEMABINDING
AS
    SELECT
        Date,
        FlagId,
        COUNT_BIG(*) AS WidgetCount
    FROM Widgets
    GROUP BY Date, FlagId;
GO

CREATE UNIQUE CLUSTERED INDEX PK_TestView ON TestView
(
    Date,
    FlagId
);

Akibatnya kinerja kueri pertama saya sekarang <100 ms, dan tampilan & indeks yang dihasilkan adalah <100rb (meskipun jumlah baris kami besar, kisaran tanggal dan ID bendera berarti bahwa tampilan ini hanya berisi 1000-2000 baris).

Saya berpikir bahwa mungkin ini akan melumpuhkan kinerja menulis ke tabel Widget, tetapi tidak - kinerja menyisipkan dan pembaruan ke dalam tabel ini cukup banyak yang tidak terpengaruh sejauh yang saya tahu (ditambah, menjadi gudang data, tabel ini diperbarui jarang. bagaimanapun)

Bagi saya, ini kelihatannya terlalu bagus untuk menjadi kenyataan - bukan? Apa yang harus saya perhatikan saat menggunakan tampilan yang diindeks dengan cara ini?

Justin
sumber
2
Bisakah Anda menulis ulang skrip Anda sehingga benar-benar SQL yang valid? Skrip Anda SELECTdan CREATE VIEWsalah, karena saya percaya CREATE INDEXskrip Anda .
Mark Sinkinson
2
@MarkSinkinson Maaf, ternyata berusaha menulis SQL yang valid untuk tabel imajiner sulit
Justin
Bagian 'terlalu bagus untuk menjadi kenyataan' bagi saya muncul ketika saya menginginkan tampilan yang lebih maju, seperti yang berisi MAX, gabungan sendiri atau luar, atau pengindeksan tampilan yang sendiri merujuk pada tampilan lain - yang semuanya dalam SQL Server setidaknya tidak diizinkan docs.microsoft.com/en-us/sql/relational-databases/views/… . Jadi saya selalu menjadi terlalu ambisius dan kemudian harus mengukur kembali. Tetapi untuk agregasi sederhana mereka benar-benar hebat - bahkan SUM didukung.
Simon_Weaver

Jawaban:

29

Seperti yang telah Anda catat, tampilan itu sendiri mematerialisasi sejumlah kecil baris - jadi meskipun Anda memperbarui seluruh tabel, tambahannya I / O yang terlibat dengan memperbarui tampilan diabaikan. Anda mungkin sudah merasakan sakit terbesar yang akan Anda rasakan ketika Anda menciptakan pemandangan itu. Yang terdekat berikutnya adalah jika Anda menambahkan trilyun baris ke tabel dasar dengan sekelompok ID baru yang membutuhkan baris baru dalam tampilan.

Ini tidak terlalu bagus untuk menjadi kenyataan. Anda menggunakan tampilan yang diindeks dengan tepat bagaimana mereka seharusnya digunakan - atau setidaknya salah satu cara yang paling efektif: untuk membayar agregasi permintaan di masa mendatang pada saat penulisan. Ini bekerja paling baik ketika hasilnya jauh lebih kecil dari sumber dan tentu saja ketika agregasi diminta lebih sering daripada data dasar diperbarui (lebih umum di DW daripada OLTP, umumnya).

Sayangnya banyak orang berpikir bahwa pengindeksan tampilan adalah sihir - indeks tidak akan membuat semua tampilan lebih efisien, terutama tampilan yang hanya bergabung dengan tabel dan / atau menghasilkan jumlah baris yang sama dengan sumbernya (atau bahkan berlipat ganda). Dalam kasus ini, I / O dari tampilan adalah sama atau bahkan lebih buruk dari permintaan asli, tidak hanya karena ada baris yang sama atau lebih, tetapi seringkali mereka menyimpan dan mematerialisasi lebih banyak kolom juga. Jadi mematerialisasikan mereka di muka tidak memberikan keuntungan apa pun, karena - bahkan dengan SSD - I / O, jaringan, dan pemrosesan / rendering klien masih tetap menjadi hambatan utama dalam mengembalikan hasil yang besar kepada klien. Penghematan yang Anda dapatkan dalam menghindari bergabung saat runtime tidak dapat diukur dibandingkan dengan semua sumber daya lain yang masih Anda gunakan.

Seperti indeks yang tidak berkerumun, berhati-hatilah untuk tidak melakukannya secara berlebihan. Jika Anda menambahkan 10 tampilan indeks yang berbeda ke satu tabel, Anda akan melihat lebih banyak dampak pada bagian tulis dari beban kerja Anda, terutama jika kolom pengelompokan bukan kunci pengelompokan.

Astaga, saya sudah bermaksud untuk blog tentang topik ini.

Aaron Bertrand
sumber
19

Aarons menjawab pertanyaan ini dengan baik. Dua hal untuk ditambahkan:

  1. Tampilan indeks yang diagregasi dapat menyebabkan pertikaian dan kebuntuan lintas baris. Biasanya, dua sisipan tidak menemui jalan buntu (kecuali untuk kondisi yang agak jarang seperti kunci eskalasi atau tabrakan hash kunci). Tetapi jika kedua sisipan membahas grup yang sama dalam tampilan, mereka akan bersaing. Titik yang sama adalah singkatan dari apa pun yang mengambil kunci (DML, kunci petunjuk).
  2. Tampilan terindeks yang tidak agregat dapat berguna juga. Mereka memungkinkan Anda untuk mengindeks pada kolom dari beberapa tabel. Dengan begitu, Anda dapat memfilter secara efisien di satu tabel dan memesan satu kolom dari tabel gabungan. Pola itu dapat mengonversi gabung tabel penuh ke kueri waktu konstan kecil.

Saya telah menggunakan agregasi dan menggabungkan pandangan dengan manfaat ekstrem.

Semua dalam semua kasus penggunaan Anda sepertinya kasus yang sempurna. Pandangan terindeks adalah teknik yang jauh kurang dimanfaatkan.

usr
sumber