Saat memilih primary key biasanya Anda juga memilih clustered key. Keduanya sering dikacaukan, tetapi Anda harus memahami perbedaannya.
Kunci utama logis bisnis elemen. Kunci utama digunakan oleh aplikasi Anda untuk mengidentifikasi entitas, dan diskusi tentang kunci utama sebagian besar apakah akan menggunakan kunci alami atau kunci pengganti. Tautan masuk ke lebih detail, tetapi ide dasarnya adalah bahwa kunci alami berasal dari properti entitas yang ada seperti ssn
atau phone number
, sedangkan kunci pengganti tidak memiliki arti apa pun terkait dengan entitas bisnis, seperti id
atau rowid
dan biasanya bertipe IDENTITY
atau semacam uuid. Pendapat pribadi saya adalah bahwa kunci pengganti lebih unggul daripada kunci alami, dan pilihannya harus selalu nilai identitas untuk aplikasi lokal saja, panduan untuk segala jenis data terdistribusi. Kunci utama tidak pernah berubah selama masa hidup entitas.
Kunci yang dikelompokkan adalah kunci yang mendefinisikan penyimpanan fisik baris dalam tabel. Sering kali mereka tumpang tindih dengan kunci utama (pengidentifikasi entitas logis), tetapi itu tidak benar-benar ditegakkan atau diperlukan. Ketika keduanya berbeda itu berarti ada indeks unik yang tidak berkerumun pada tabel yang mengimplementasikan kunci utama. Nilai kunci yang dikelompokkan sebenarnya dapat berubah selama masa pakai baris, sehingga baris dipindahkan secara fisik dalam tabel ke lokasi baru. Jika Anda harus memisahkan primary key dari clustered key (dan terkadang Anda melakukannya), memilih clustered key yang baik jauh lebih sulit daripada memilih primary key. Ada dua faktor utama yang mendorong desain kunci berkerumun Anda:
- Pola akses data yang lazim .
- Pertimbangan penyimpanan .
Pola Akses Data . Dengan ini saya mengerti cara tabel ditanyakan dan diperbarui. Ingat bahwa kunci berkerumun menentukan urutan sebenarnya dari baris dalam tabel. Untuk pola akses tertentu, beberapa tata letak membuat semua perbedaan di dunia dalam hal kecepatan kueri atau untuk memperbarui konkurensi:
-
data saat ini vs. arsip. Di banyak aplikasi, data bulan berjalan sering diakses, sedangkan yang lama jarang diakses. Dalam kasus seperti itu, desain tabel menggunakan partisi tabel berdasarkan tanggal transaksi, sering kali menggunakan algoritma jendela geser. Partisi bulan saat ini disimpan di grup file yang terletak di disk cepat panas, data lama yang diarsipkan dipindahkan ke grup file yang dihosting di penyimpanan yang lebih murah tetapi lebih lambat. Jelas dalam hal ini clustered key (tanggal) bukan primary key (id transaksi). Pemisahan keduanya didorong oleh persyaratan skala, karena pengoptimal kueri akan dapat mendeteksi bahwa kueri hanya tertarik pada partisi saat ini dan bahkan tidak melihat pada partisi historis.
-
Pemrosesan gaya antrian FIFO. Dalam hal ini tabel memiliki dua hot spot:tail dimana insert terjadi (enqueue), dan head dimana terjadi delete (dequeue). Kunci berkerumun harus memperhitungkan ini dan mengatur tabel untuk memisahkan secara fisik lokasi ekor dan kepala pada disk, untuk memungkinkan konkurensi antara enqueue dan dequeue, misalnya. dengan menggunakan kunci pemesanan enqueue. Dalam murni antrian kunci yang dikelompokkan ini adalah satu-satunya kunci, karena tidak ada kunci utama pada tabel (berisi pesan , bukan entitas ). Tapi seringkali antrian tidak murni, juga bertindak sebagai penyimpanan untuk entitas, dan garis antara antrian dan tabel kabur. Dalam hal ini ada juga kunci utama, yang tidak dapat menjadi kunci berkerumun:entitas dapat diantrekan ulang, sehingga mengubah nilai kunci kluster urutan urutan enqueue, tetapi mereka tidak dapat mengubah nilai kunci utama. Kegagalan untuk melihat pemisahan adalah alasan utama mengapa antrian yang didukung tabel pengguna sangat sulit untuk diperbaiki dan penuh dengan kebuntuan:karena enqueue dan dequeue terjadi disisipkan melalui tabel, alih-alih dilokalisasi di bagian ekor dan kepala antrian.
-
Pemrosesan yang berkorelasi. Ketika aplikasi dirancang dengan baik, itu akan mempartisi pemrosesan item yang berkorelasi di antara utas pekerjanya. Misalnya prosesor dirancang untuk memiliki 8 utas pekerja (katakanlah untuk mencocokkan 8 CPU di server) sehingga prosesor mempartisi data di antara mereka sendiri, mis. pekerja 1 hanya mengambil akun bernama A ke E, pekerja 2 F ke J dll. Dalam kasus seperti itu tabel harus benar-benar dikelompokkan berdasarkan nama akun (atau dengan kunci gabungan yang memiliki posisi paling kiri huruf pertama nama akun), sehingga pekerja melokalkan kueri dan pembaruan mereka di tabel. Tabel seperti itu akan memiliki 8 titik panas yang berbeda, di sekitar area yang menjadi konsentrasi setiap pekerja saat ini, tetapi yang penting adalah mereka tidak tumpang tindih (tidak ada pemblokiran). Jenis desain ini lazim pada desain OLTP throughput tinggi dan dalam beban benchmark TPCC, di mana jenis partisi ini juga mencerminkan lokasi memori halaman yang dimuat di kumpulan buffer (lokalitas NUMA), tapi saya ngelantur.
Pertimbangan Penyimpanan . Kunci yang dikelompokkan lebar memiliki dampak besar dalam penyimpanan tabel. Untuk satu kunci menempati ruang di setiap halaman non-daun dari b-tree, sehingga kunci besar akan menempati lebih banyak ruang. Kedua, dan seringkali yang lebih penting, adalah bahwa kunci yang dikelompokkan digunakan sebagai kunci pencarian oleh setiap kunci yang tidak berkerumun, jadi setiap kunci yang tidak berkerumun harus menyimpan lebar penuh dari kunci berkerumun untuk setiap baris. Inilah yang membuat kunci berkerumun besar seperti varchar(256) dan panduan pilihan yang buruk untuk kunci indeks berkerumun.
Juga pilihan kunci berdampak pada fragmentasi indeks berkerumun, terkadang secara drastis memengaruhi kinerja.
Kedua kekuatan ini kadang-kadang bisa menjadi antagonis, pola akses data yang membutuhkan kunci berkerumun besar tertentu yang akan menyebabkan masalah penyimpanan. Dalam kasus seperti itu tentu saja diperlukan keseimbangan, tetapi tidak ada formula ajaib. Anda mengukur dan menguji untuk mencapai sweet spot.
Jadi apa yang kita buat dari semua ini? Selalu mulai dengan mempertimbangkan kunci berkerumun yang juga merupakan kunci utama dari bentuk entity_id IDENTITY(1,1) NOT NULL
. Pisahkan keduanya dan atur tabel sesuai dengan itu (mis. partisi berdasarkan tanggal) bila sesuai.