Performa tabel temporal yang buruk pada nilai yang lebih lama

8

Saya menghadapi masalah aneh yang terjadi ketika mengakses catatan sejarah dalam tabel temporal. Kueri yang mengakses entri yang lebih lama di tabel temporal melalui AS OF sub-klausa membutuhkan waktu lebih lama daripada kueri pada entri historis terbaru.

Tabel historis dihasilkan oleh SQL Server (termasuk indeks berkerumun pada kolom tanggal dan menggunakan kompresi halaman), saya telah menambahkan 50 juta baris ke tabel historis, dan pertanyaan saya mengambil sekitar 25.000 baris.

Saya telah mencoba menentukan akar penyebab masalah tetapi belum dapat mengidentifikasinya. Sejauh ini saya sudah menguji:

  • Membuat tabel uji dengan 50 juta baris dengan indeks berkerumun untuk melihat apakah perlambatan hanya karena volume. Saya dapat mengambil 25K baris pada waktu yang konstan (~ 400 ms).
  • Menghapus kompresi halaman dari tabel historis. Itu tidak berpengaruh pada waktu pengambilan tetapi tidak secara signifikan meningkatkan ukuran tabel.
  • Saya mencoba mengakses baris tabel sejarah secara langsung menggunakan kolom ID vs kolom tanggal. Di sinilah hal-hal sedikit lebih menarik. Saya bisa mengakses baris yang lebih tua di tabel di ~ 400ms di mana seperti dengan AS OF sub klausa akan butuh ~ 1200ms. Saya mencoba memfilter pada tabel pengujian saya pada kolom tanggal dan melihat perlambatan serupa jika dibandingkan dengan pemfilteran pada kolom ID. Ini membuat saya percaya bahwa perbandingan tanggal ada di belakang beberapa perlambatan.

Saya ingin melihat ini lebih banyak tetapi saya juga ingin memastikan bahwa saya tidak menggonggong pohon yang salah. Pertama, apakah ada orang lain yang mengalami perilaku yang sama ketika mengakses data historis yang lebih lama di tabel temporal (kami hanya melihat perlambatan melewati 10 juta baris)? Kedua, apa sajakah strategi yang dapat saya gunakan untuk lebih mengisolasi akar penyebab masalah kinerja (saya baru saja mulai melihat ke rencana eksekusi tetapi masih agak samar bagi saya)?

Rencana eksekusi

Ini adalah permintaan pengambilan sederhana: yang pertama mengakses baris yang lebih tua, yang kedua mengakses baris yang lebih baru.

Baris Lama ~ waktu eksekusi 1200 ms

Baris terbaru ~ 350 ms waktu eksekusi

Detail tabel

Ini adalah kolom dalam tabel temporal. Tabel sejarah memiliki kolom yang sama tetapi tidak memiliki kunci utama (sesuai persyaratan tabel riwayat): Kolom tabel temporal

Di bawah ini adalah indeks pada tabel sejarah: Indeks pada tabel sejarah

Ebrahim Behbahani
sumber

Jawaban:

6

Dalam komentar dari Zane tentang pertanyaan Anda, ia menyatakan:

... Sepertinya bagian dari masalah Anda adalah Anda membaca 50 juta baris untuk mengembalikan 20 ribu dalam rencana.

Ini memang masalah. Tidak ada indeks yang tersedia untuk mendorong sebagian, atau semua, dari predikat ke mesin penyimpanan. Microsoft merekomendasikan strategi pengindeksan dasar ini untuk tabel temporal di artikel Documents Pertimbangan dan Batasan Tabel Temporal :

Strategi pengindeksan yang optimal akan mencakup indeks toko kolom berkerumun dan / atau indeks rowstore pohon-B pada tabel saat ini dan indeks columnstore berkerumun di tabel sejarah untuk ukuran dan kinerja penyimpanan yang optimal. Jika Anda membuat / menggunakan tabel riwayat Anda sendiri, kami sangat menyarankan Anda membuat jenis indeks ini yang terdiri dari kolom periode dimulai dengan kolom akhir periode untuk mempercepat kueri temporal serta mempercepat kueri yang merupakan bagian dari konsistensi data memeriksa. Tabel sejarah default memiliki indeks rowstore berkerumun yang dibuat untuk Anda berdasarkan kolom periode (akhir, mulai). Minimal, indeks rowstore non-cluster dianjurkan

Ungkapan itu agak membingungkan (bagi saya, sih). Namun, yang perlu Anda lakukan adalah membuat beberapa indeks ini untuk meningkatkan kinerja, jika tidak cukup banyak:

Indeks NC pada tabel saat ini, memimpin dengan SysEndTime:

CREATE NONCLUSTERED INDEX IX_SysEndTime_SysStartTime 
ON dbo.Benefits (SysEndTime, SysStartTime)
/*INCLUDE (ideally, include your other important fields here)*/;

Ini akan memungkinkan Anda untuk menghindari membaca beberapa baris dalam tabel saat ini dengan mencari waktu akhir yang sesuai.

CCI di tabel sejarah

CREATE CLUSTERED COLUMNSTORE INDEX ix_BenefitsHistory
ON dbo.BenefitsHistory
WITH (DROP_EXISTING = ON);

Ini akan memungkinkan Anda mendapatkan mode batch di tabel histori, yang seharusnya membuat pemindaian lebih cepat.

Indeks NC pada tabel saat ini, memimpin dengan SysStartTime:

Lihat jawaban Paul untuk pertanyaan Cara Paling Efisien untuk Mengambil Rentang Tanggal untuk detail lebih lanjut tentang mengapa pengindeksan untuk kueri rentang tanggal sulit. Berdasarkan logika di sana, masuk akal untuk menambahkan indeks NC lain pada tabel saat ini yang mengarah dengan SysStartTime, sehingga pengoptimal dapat memilih mana yang akan digunakan berdasarkan statistik dan parameter spesifik dari kueri Anda:

CREATE NONCLUSTERED INDEX IX_SysStartTime_SysEndTime
ON dbo.Benefits (SysStartTime, SysEndTime)
/*INCLUDE (ideally, include your other important fields here)*/;

Membuat 3 indeks yang diuraikan di atas membuat perbedaan yang signifikan dalam penggunaan sumber daya dalam kasus pengujian saya. Saya menyiapkan test case yang menjalankan dua query yang mengembalikan 1,5 juta total baris. Baik tabel histori dan saat ini memiliki 50 juta baris).

Catatan: Untuk mengurangi overhead SSMS, saya menjalankan tes dengan opsi "Buang hasil setelah eksekusi" diaktifkan.

Rencana Eksekusi - Indeks Default

Berbunyi logis: 1.330.612
Waktu CPU: 00: 00: 14.718
Waktu berlalu: 00: 00: 06.198

Rencana Eksekusi - Dengan Indeks Diuraikan Di Atas

Berbunyi logis: 27.656 (8.111 toko baris + 19.545 kolom)
Waktu CPU: 00: 00: 01.828
Waktu yang berlalu: 00: 00: 01.150

Seperti yang Anda lihat, ketiga ukuran turun secara signifikan - termasuk total waktu yang telah berlalu, dari 6 detik menjadi 1 detik.


Opsi lain yang disajikan oleh artikel Documents adalah untuk melepaskan dua indeks NC pada tabel saat ini yang mendukung indeks toko kolom berkerumun. Dalam pengujian saya, kinerja sangat mirip dengan solusi pengindeksan yang dijelaskan di atas.

Josh Darnell
sumber
2

The FOR SYSTEM TIME AS OFklausul mencoba untuk kembali dataset seperti yang ada pada saat dinyatakan. Ini berarti bahwa pembaruan harus diputar kembali secara internal, penghapusan harus 'dibatalkan penghapusan', dan sisipan harus diabaikan, berdasarkan waktu sistem permintaan.

Semakin lama waktu AS, semakin banyak pekerjaan yang perlu divalidasi untuk memastikan bahwa tabel temporal seperti yang ada pada waktu sistem yang ditentukan, dan dengan demikian semakin lama kueri akan berlangsung.

JIKA tabel data hanyalah tabel pencatatan, dan tidak ada perubahan yang dilakukan pada data, maka menggunakan tanggal yang dicatat dan indeks akan mengembalikan data lebih cepat dan lebih konsisten. Apakah akan menggunakan fitur sementara dalam hal ini tidak perlu. Namun, jika perubahan dilakukan pada baris (selain sisipan), maka menggunakan fitur tabel temporal adalah satu-satunya cara untuk mengembalikan data tepat yang diminta (keadaan tabel seperti yang ada pada waktu tertentu), dan Anda akan hanya harus menerima overhead tambahan dari pertanyaan temporal.

Catatan: "rollbacks" bukan rollbacks yang sebenarnya. Tabel temporal menggunakan dua tabel - tabel Current, dan tabel History. Ketika suatu baris diubah, salinan dari versi sebelumnya dimasukkan ke dalam tabel Riwayat dengan rentang waktu bahwa baris itu valid. Jika Anda menyisipkan baris pada 10/20/2018 10: 20: 20.18, perbarui nilai pada 10/25/2018 10: 25: 20.18, dan perbarui lagi di 12/01/2018 12: 01: 20.18, Anda harus versi terbaru dari baris dalam tabel saat ini dengan tanggal mulai 12/01/2018 12: 01: 20.18, dan dua baris dalam tabel sejarah dengan rentang valid 10/20 hingga 10/25/2018, dan 10 / 25 hingga 12/01/2018

Tertawa Vergil
sumber
Terima kasih atas tanggapannya! Itu jelas masuk akal secara intuisi, tetapi saya tidak menemukan penyebutan jenis perilaku itu dalam dokumen yang saya baca (saya hanya membahas dasar-dasar tabel temporal dalam dokumen MS). Apakah Anda tahu ada dokumentasi yang menggambarkan perilaku dengan sedikit lebih detail?
Ebrahim Behbahani