Apakah urutan kolom dalam indeks PK penting?

33

Saya punya beberapa meja yang sangat besar dengan struktur dasar yang sama. Masing-masing memiliki a RowNumber (bigint)dan DataDate (date)kolom. Data dimuat menggunakan SQLBulkImport setiap malam, dan tidak ada data "baru" yang pernah dimuat - ini adalah catatan historis (SQL Standard, bukan Enterprise, jadi tidak ada partisi).

Karena setiap bit data perlu diikat kembali ke sistem lain, dan setiap RowNumber/DataDatekombinasi unik, itulah Kunci Utama saya.

Saya perhatikan bahwa karena cara saya mendefinisikan PK di SSMS Table Designer, RowNumberterdaftar pertama dan DataDatekedua.

Saya juga melihat bahwa fragmentasi saya SANGAT tinggi ~ 99%.

Sekarang, karena masing-masing DataDatehanya muncul sekali, saya akan mengharapkan pengindeks hanya menambahkan ke halaman setiap hari, tetapi saya bertanya-tanya apakah itu sebenarnya pengindeksan berdasarkan RowNumberpertama, dan karenanya harus menggeser semua yang lain di sekitar?


Rownumberbukan kolom identitas, itu int yang dihasilkan oleh sistem eksternal (sayangnya). Ulang di awal masing-masing DataDate.

Contoh Data

RowNumber | DataDate | a | b | c..... 
   1      |2013-08-01| x | y | z 
   2      |2013-08-01| x | y | z 
...
   1      |2013-08-02| x | y | z 
   2      |2013-08-02| x | y | z 
...

Data dimuat secara RowNumberberurutan, satu DataDateper beban.

Proses impor bcp - Saya telah mencoba memuat ke tabel temp dan kemudian memilih secara berurutan dari sana ( ORDER BY RowNumber, DataDate) tetapi masih keluar fragmentasi tinggi.

BlueChippy
sumber

Jawaban:

50

Apakah urutan kolom dalam indeks PK penting?

Ya itu.

Secara default, batasan kunci utama ditegakkan dalam SQL Server oleh indeks berkerumun unik. Indeks berkerumun mendefinisikan urutan logis dari baris dalam tabel. Mungkin ada sejumlah halaman indeks tambahan yang ditambahkan untuk mewakili tingkat atas dari indeks b-tree, tetapi level terendah (daun) dari indeks berkerumun hanyalah urutan logis dari data itu sendiri.

Agar lebih jelas tentang hal itu, baris pada halaman tidak secara fisik disimpan dalam urutan kunci indeks berkerumun. Ada struktur tipuan terpisah di dalam halaman yang menyimpan pointer ke setiap baris. Struktur ini diurutkan berdasarkan kunci indeks berkerumun. Juga, setiap halaman memiliki pointer ke halaman sebelumnya dan berikutnya pada tingkat yang sama dalam urutan kunci indeks berkerumun.

Dengan kunci primer yang dikelompokkan (RowNumber, DataDate), baris secara logis diurutkan pertama RowNumberdan kemudian oleh DataDate- jadi semua baris di mana RowNumber = 1secara logis dikelompokkan bersama, lalu baris di mana RowNumber = 2dan seterusnya.

Ketika Anda menambahkan data baru (dengan RowNumbersdari 1 ke n) baris baru secara logis berada di dalam halaman yang ada, jadi SQL Server kemungkinan harus melakukan banyak pekerjaan pemisahan halaman untuk membuat ruang. Semua aktivitas ini menghasilkan banyak pekerjaan tambahan (termasuk mencatat perubahan) tanpa hasil.

Halaman split juga memulai sekitar 50% kosong, sehingga pemisahan yang berlebihan dapat menghasilkan kepadatan halaman yang rendah (lebih sedikit baris daripada optimal per halaman) juga. Tidak hanya berita buruk untuk membaca dari disk (kepadatan lebih rendah = lebih banyak halaman untuk dibaca), halaman dengan kepadatan lebih rendah juga memakan lebih banyak ruang dalam memori saat di-cache.

Mengubah indeks clustered ke (DataDate, RowNumber) berarti bahwa data baru (dengan, mungkin, lebih tinggi DataDatesdari yang disimpan saat ini) ditambahkan ke akhir logis dari indeks clustered pada halaman baru. Ini akan menghapus overhead yang tidak perlu dari halaman pemisah dan menghasilkan waktu pemuatan yang lebih cepat. Data yang kurang terfragmentasi juga berarti bahwa aktivitas baca-depan (membaca halaman dari disk tepat sebelum dibutuhkan untuk permintaan yang sedang berlangsung) dapat lebih efisien.

Jika tidak ada yang lain, kueri Anda lebih cenderung mencari DataDatedaripada RowNumber. Indeks berkerumun aktif (DataDate, RowNumber) mendukung indeks mencari DataDate(dan kemudian RowNumber). Pengaturan yang ada hanya mendukung pencarian pada RowNumber(dan hanya kemudian, mungkin, pada DataDate). Anda mungkin dapat menghapus indeks nonclustered yang ada DataDatesaat kunci primer diubah. Indeks berkerumun akan lebih luas daripada indeks nonclustered menggantikannya, jadi Anda harus menguji untuk memastikan bahwa kinerja tetap dapat diterima.

Saat mengimpor data baru bcp, Anda mungkin mendapatkan kinerja yang lebih tinggi jika data dalam file impor diurutkan berdasarkan kunci indeks yang dikelompokkan (idealnya (DataDate, RowNumber)) dan Anda menentukan bcpopsi:

-h "ORDER(DataDate,RowNumber), TABLOCK"

Untuk kinerja pemuatan data terbaik, Anda dapat mencoba mencapai sisipan yang minim log. Untuk informasi lebih lanjut, lihat:

Paul White mengatakan GoFundMonica
sumber
4
Jawaban yang sangat bagus - Saya sekarang tahu APA yang harus saya lakukan DAN mengapa. Saya pikir begitu, tetapi tidak DIKETAHUI begitu! Terima kasih.
BlueChippy
Butuh LOOOOONG sementara untuk mendapatkan DB ke SQL Server lokal saya untuk pengujian: Sebelum mengubah beban indeks butuh 45 menit ... setelah itu, hanya butuh 5 !!!
BlueChippy
13

Ya, pesanan sangat penting. Saya sangat meragukan Anda pernah meminta oleh RowNumber (misalnya WHERE RowNumber=1). Rangkaian waktu yang sangat banyak ditanyakan berdasarkan tanggal ( WHERE DataDate BEWEEN @start AND @end) dan pertanyaan seperti itu akan membutuhkan organisasi yang dikelompokkan oleh DataDate.

Fragmentasi pada umumnya adalah herring merah. Mengurangi fragmentasi seharusnya tidak menjadi tujuan Anda di sini, tetapi memiliki organisasi yang tepat untuk pertanyaan Anda harus dilakukan. Mengurangi fragmentasi sebagai tambahan adalah pemikiran yang baik untuk dimiliki, tetapi bukan tujuan itu sendiri. Jika Anda memiliki model data yang terorganisir dengan baik yang sesuai dengan beban kerja Anda (pertanyaan Anda dicakup dengan benar) dan Anda memiliki pengukuran yang menunjukkan fragmentasi sebagai dampak kinerja, maka kita dapat membicarakannya.

Remus Rusanu
sumber
Saya juga memiliki indeks non-clustered pada DataDate, yang seperti yang Anda katakan sering WHEREklausa dalam kueri.
BlueChippy
1
Jika ORDER kolom sangat penting, apakah dampak dari urutan penambahan akan melihat I / O saya meningkat? Pikir saya adalah bahwa itu dipesan oleh RowNumber dan karena itu harus banyak berdoa pada indeks setiap kali, sedangkan itu harus didasarkan pada DataDate?
BlueChippy