Apa yang bisa menjadi kelemahan dari selalu memiliki kolom integer tunggal sebagai kunci utama?

18

Dalam satu aplikasi Web yang saya kerjakan, semua operasi basis data diabstraksi menggunakan beberapa repositori generik yang didefinisikan melalui Entity Framework ORM.

Namun, untuk memiliki desain sederhana untuk repositori generik, semua tabel yang terlibat harus mendefinisikan integer unik ( Int32dalam C #, intdalam SQL). Sampai sekarang, ini selalu menjadi PK tabel dan juga IDENTITY.

Kunci asing banyak digunakan dan mereferensikan kolom integer ini. Mereka diperlukan untuk konsistensi dan untuk menghasilkan properti navigasi oleh ORM.

Lapisan aplikasi biasanya melakukan operasi berikut:

  • pemuatan data awal dari tabel (*) -SELECT * FROM table
  • Perbarui -UPDATE table SET Col1 = Val1 WHERE Id = IdVal
  • Hapus -DELETE FROM table WHERE Id = IdVal
  • Sisipkan -INSERT INTO table (cols) VALUES (...)

Operasi yang lebih jarang:

  • Sisipan massal - BULK INSERT ... into tablediikuti (*) oleh semua data yang dimuat (untuk mengambil pengidentifikasi yang dihasilkan)
  • Hapus massal - ini adalah operasi penghapusan normal, tetapi "besar" dari perspektif ORM:DELETE FROM table where OtherThanIdCol = SomeValue
  • Pembaruan massal - ini adalah operasi pembaruan normal, tetapi "besar" dari perspektif ORM:UPDATE table SET SomeCol = SomeVal WHERE OtherThanIdCol = OtherValue

* semua tabel kecil di-cache di level aplikasi dan hampir semua SELECTstidak akan mencapai basis data. Pola tipikal adalah beban awal dan banyak INSERTs, UPDATEs dan DELETEs.

Berdasarkan penggunaan aplikasi saat ini, ada kemungkinan sangat kecil untuk mencapai 100 juta catatan di salah satu tabel.

Pertanyaan: Dari sudut pandang DBA, apakah ada masalah signifikan yang dapat saya hadapi dengan memiliki batasan desain tabel ini?

[EDIT]

Setelah membaca jawabannya (terima kasih atas umpan baliknya yang luar biasa) dan artikel yang direferensikan, saya merasa harus menambahkan rincian lebih lanjut:

  1. Khusus aplikasi saat ini - Saya tidak menyebutkan tentang aplikasi web saat ini, karena saya ingin memahami apakah model tersebut dapat digunakan kembali untuk aplikasi lain juga. Namun, kasus khusus saya adalah aplikasi yang mengekstrak banyak metadata dari DWH. Sumber data sangat berantakan (didenormalkan dengan cara yang aneh, memiliki beberapa inkonsistensi, tidak ada pengidentifikasi alami dalam banyak kasus, dll.) Dan aplikasi saya menghasilkan entitas yang jelas terpisah. Juga, banyak pengidentifikasi yang dihasilkan ( IDENTITY) ditampilkan, sehingga pengguna dapat menggunakannya sebagai kunci bisnis. Ini, selain refactoring kode besar-besaran, tidak termasuk penggunaan GUID .

  2. "mereka seharusnya tidak menjadi satu-satunya cara untuk mengidentifikasi secara unik satu baris" (Aaron Bertrand ♦) - itu adalah saran yang sangat bagus. Semua tabel saya juga mendefinisikan KONSTRA UNIK untuk memastikan bahwa duplikat bisnis tidak diperbolehkan.

  3. Desain berbasis aplikasi front-end vs. desain berbasis database - pilihan desain disebabkan oleh faktor-faktor ini

    1. Batasan Entity Framework - beberapa kolom PK diizinkan, tetapi nilainya tidak dapat diperbarui

    2. Batasan khusus - memiliki kunci integer tunggal sangat menyederhanakan struktur data dan kode non-SQL. Misalnya: semua daftar nilai memiliki kunci integer dan nilai yang ditampilkan. Lebih penting, itu menjamin bahwa setiap tabel yang ditandai untuk caching akan dapat dimasukkan ke dalam Unique int key -> valuepeta.

  4. Kueri pemilihan kompleks - ini hampir tidak akan pernah terjadi karena semua data tabel kecil (<20-30K catatan) di-cache di tingkat aplikasi. Ini membuat hidup sedikit lebih sulit ketika menulis kode aplikasi (lebih sulit untuk menulis LINQ), tetapi basis datanya lebih baik:

    1. Tampilan daftar - tidak akan menghasilkan SELECTkueri saat dimuat (semuanya di-cache) atau kueri yang terlihat seperti ini:

      SELECT allcolumns FROM BigTable WHERE filter1 IN (val1, val2) AND filter2 IN (val11, val12)

      Semua nilai lain yang diperlukan diambil melalui pencarian cache (O (1)), sehingga tidak ada permintaan kompleks yang akan dihasilkan.

    2. Edit tampilan - akan menghasilkan SELECTpernyataan seperti ini:

      SELECT allcolumns FROM BigTable WHERE PKId = value1

(semua filter dan nilai adalah ints)

Alexei
sumber
Anda mungkin menemukan pos-pos relevansi ini, karena beberapa aspek logis, fisik, dan praktis dibahas sehubungan dengan penggunaan kolom dengan nilai pengganti yang dihasilkan sistem.
MDCCL

Jawaban:

19

Selain ruang disk tambahan (dan pada gilirannya penggunaan memori dan I / O), tidak ada salahnya menambahkan kolom IDENTITY bahkan ke tabel yang tidak membutuhkannya (contoh tabel yang tidak memerlukan kolom IDENTITY adalah tabel persimpangan sederhana, seperti memetakan pengguna ke izinnya).

Saya menolak menambahkan secara membabi buta ke setiap tabel dalam posting blog dari 2010:

Tetapi kunci pengganti memang memiliki kasus penggunaan yang valid - hanya berhati-hati untuk tidak menganggap bahwa mereka menjamin keunikan (yang kadang-kadang mengapa mereka ditambahkan - mereka seharusnya bukan satu - satunya cara untuk secara unik mengidentifikasi baris). Jika Anda perlu menggunakan kerangka kerja ORM, dan kerangka kerja ORM Anda memerlukan kunci integer satu kolom bahkan dalam kasus ketika kunci asli Anda bukan bilangan bulat, atau tidak satu kolom, atau tidak, pastikan Anda mendefinisikan batasan / indeks unik untuk kunci Anda yang sebenarnya juga.

Aaron Bertrand
sumber
Terima kasih atas balasan cepatnya. Ya, aplikasi menggunakan ORM (EF). Itu tidak memerlukan kunci kolom integer tunggal, tetapi saya telah memperkenalkan batasan ini untuk membuat beberapa operasi generik lebih mudah (berdasarkan desain). Juga, semua cache aplikasi menyimpan segala sesuatu di peta (kamus) untuk pengambilan cepat dengan kunci dan kunci harus unik. Karena saya telah memilih int daripada guids, saya terpaksa menggunakan IDENTITY untuk setiap tabel yang saya masukkan. Untuk tabel nilai tetap, IDENTITY tidak diperlukan.
Alexei
Saya pikir beberapa kasus yang menyerukan untuk menghindari pemeriksaan keunikan pada kunci alami ada. Sebagai seseorang yang bekerja dengan data GIS, salah satu yang langsung terlintas di benak adalah di mana kunci alami adalah hanya geometri itu sendiri atau geometri ditambah beberapa kunci asing. Mencari hal-hal dengan geometri yang tepat selalu tidak praktis, sehingga kendala keunikan tidak akan banyak membantu dan mungkin memiliki kelemahan kinerja. Hal yang sama bisa benar jika bagian dari kunci alami adalah kolom teks yang panjang. Tetapi saya setuju: kapan pun praktis, ya, batasan unik pada kunci alami harus diterapkan.
jpmc26
13

Dari pengalaman saya, alasan utama dan luar biasa untuk menggunakan ID terpisah untuk setiap tabel adalah sebagai berikut:

Dalam hampir setiap kasus pelanggan saya bersumpah darah dalam fase konsepsi bahwa beberapa bidang "alami" eksternal XYZBLARGH_IDakan tetap unik selamanya, dan tidak akan pernah berubah untuk entitas tertentu, dan tidak akan pernah digunakan kembali, akhirnya muncul kasus di mana Properti Primary Key rusak. Itu tidak berhasil seperti itu.

Kemudian, dari sudut pandang DBA, hal-hal yang membuat DB lambat atau kembung tentu bukan 4 byte (atau apa pun) per baris, tetapi hal-hal seperti indeks yang salah atau hilang, reorganisasi tabel / indeks yang dilupakan, RAM yang salah / parameter pengaturan ruang tablespace , lalai untuk menggunakan variabel bind dan sebagainya. Itu bisa memperlambat DB dengan faktor 10, 100, 10000 ... bukan kolom ID tambahan.

Jadi, bahkan jika ada kelemahan teknis dan terukur dari memiliki tambahan 32 bit per baris, itu bukan pertanyaan apakah Anda dapat mengoptimalkan ID pergi, tetapi apakah ID akan sangat penting di beberapa titik, yang akan lebih mungkin daripada tidak. Dan saya tidak akan menghitung semua manfaat "lunak" dari pendirian pengembangan perangkat lunak (seperti contoh ORM Anda, atau fakta bahwa hal itu membuatnya lebih mudah bagi pengembang perangkat lunak ketika semua ID dengan desain memiliki tipe data yang sama dan seterusnya) .

NB: perhatikan bahwa Anda tidak memerlukan ID terpisah untuk n:mtabel asosiasi karena untuk tabel seperti itu ID entitas terkait harus membentuk kunci utama. Contoh tandingan akan menjadi n:masosiasi aneh yang memungkinkan banyak asosiasi antara dua entitas yang sama untuk alasan aneh apa pun - mereka akan memerlukan kolom ID mereka sendiri kemudian, untuk membuat PK. Ada yang ORM perpustakaan yang tidak dapat menangani PKS multi-kolom, jadi itu akan menjadi alasan untuk bersikap lunak dengan pengembang, jika mereka harus bekerja dengan perpustakaan tersebut.

AnoE
sumber
2
"asosiasi n: m aneh yang memungkinkan banyak asosiasi antara dua entitas yang sama" SANGAT umum dalam kehidupan nyata. Misalnya seseorang memiliki mobil, maka persyaratannya berubah menjadi ditarik ketika kepemilikan dimulai dan berakhir, (Seseorang dapat menjual mobil dan membelinya kembali nanti, dan merusak perangkat lunak Anda ....)
Ian Ringrose
Yup, sesuatu seperti itu, @IanRingrose.
AnoE
6

Jika Anda selalu menambahkan kolom tambahan yang tidak berarti ke setiap tabel dan hanya merujuk kolom-kolom itu sebagai kunci asing maka Anda hampir pasti akan membuat basis data lebih kompleks dan sulit digunakan. Secara efektif Anda akan menghapus data yang menarik bagi pengguna dari atribut kunci asing dan memaksa pengguna / aplikasi untuk melakukan join tambahan untuk mengambil informasi yang sama. Pertanyaan menjadi lebih kompleks, pekerjaan pengoptimal menjadi lebih sulit dan kinerja dapat menurun.

Tabel Anda akan lebih jarang diisi dengan data "nyata" daripada sebelumnya. Oleh karena itu, basis data akan lebih sulit untuk dipahami dan diverifikasi. Anda juga mungkin merasa sulit atau tidak mungkin untuk menegakkan batasan berguna tertentu (di mana kendala akan melibatkan banyak atribut yang tidak lagi ada dalam tabel yang sama).

Saya sarankan Anda memilih kunci Anda lebih hati-hati dan membuatnya bilangan bulat hanya jika / ketika Anda punya alasan bagus untuk itu. Basis desain database Anda pada analisis yang baik, integritas data, kepraktisan, dan hasil yang dapat diverifikasi daripada mengandalkan aturan dogmatis.

nvogel
sumber
1
Namun banyak sistem memiliki kunci primer integer sintetik pada setiap tabel (misalnya, hampir setiap aplikasi Ruby on Rails yang pernah ditulis), tanpa mengalami masalah seperti itu. Mereka juga tidak pernah menderita dari masalah harus mendorong perubahan ke kunci utama (yang tidak pernah seharusnya terjadi) ke semua tabel kunci asing.
David Aldridge
2
Pertanyaan itu menanyakan kemungkinan kerugian, maka jawaban saya. Saya tidak menyangkal bahwa kunci pengganti bisa masuk akal jika digunakan dengan bijak. Tapi saya telah melihat tabel dengan 3,4,5 (atau lebih banyak) kunci asing yang tidak berarti yang memerlukan 3,4,5 atau lebih bergabung untuk mendapatkan hasil yang bermanfaat dari mereka. Desain yang lebih pragmatis mungkin tidak membutuhkan sambungan sama sekali.
nvogel
1
Saya tidak yakin bahwa itu adalah pelaksanaan pertanyaan seperti itu yang merupakan masalah utama yang orang miliki dengan desain seperti itu - itu adalah penulisan permintaan yang sering mereka tolak.
David Aldridge
5

Dalam pengalaman saya dengan berbagai database, kunci primer Integer selalu lebih baik daripada aplikasi yang tidak memiliki kunci yang ditentukan sama sekali. Atau yang memiliki kunci yang bergabung dengan setengah lusin kolom varchar dengan cara canggung yang tidak logis ... (huh)

Saya telah melihat aplikasi yang beralih dari PK integer ke GUID. Alasan mereka melakukan itu adalah karena ada kebutuhan untuk menggabungkan data dari beberapa sumber database dalam kasus-kasus tertentu. Pengembang mengalihkan semua kunci ke GUID sehingga penggabungan dapat terjadi tanpa takut akan tabrakan data, bahkan pada tabel yang bukan bagian dari penggabungan (kalau-kalau tabel tersebut pernah menjadi bagian dari penggabungan di masa mendatang).

Saya akan mengatakan integer PK tidak akan menggigit Anda kecuali jika Anda berencana untuk menggabungkan data dari sumber yang berbeda atau Anda mungkin memiliki data yang melampaui batas ukuran integer Anda - semuanya menyenangkan dan permainan sampai Anda kehabisan ruang untuk memasukkan .

Saya akan mengatakan, meskipun, bahwa itu bisa masuk akal untuk mengatur indeks berkerumun Anda pada kolom selain PK Anda, jika tabel akan bertanya lebih sering seperti itu. Tapi itu kasus outliar, terutama jika sebagian besar pembaruan dan pemilihan didasarkan pada nilai-nilai PK.

CaM
sumber
2
Kedengarannya seperti pembenaran yang mengerikan untuk mengubah semua kunci menjadi panduan. Saat ini saya bekerja dengan database yang menggunakan panduan untuk semua kunci pengganti .. itu tidak menyenangkan.
Andy
2
Tidak. Menggunakan GUID tidak menyenangkan. Saya tidak suka mereka, tetapi saya menghargai nilainya dalam kasus penggunaan tertentu.
CaM
2

Kesampingkan:

  • Perang agama (google surrogate vs natural key)
  • Masalah terpisah apa indeks berkerumun untuk menentukan pada tabel Anda
  • Kelangsungan caching semua data Anda

Asalkan Anda menggunakan penghapusan massal / pembaruan di mana sesuai, dan memiliki indeks untuk mendukung operasi tersebut, saya tidak berpikir Anda akan mengalami masalah karena standar PK yang Anda gunakan.
Mungkin saja jika nanti Anda memiliki EF menghasilkan kueri dengan bergabung dll, bahwa mereka tidak akan seefisien mereka dengan repositori berbasis kunci alam, tapi saya tidak cukup tahu tentang area itu untuk mengatakan dengan pasti.

TH
sumber
4
Saya tidak bisa memikirkan satu kasus di mana gabungan pada kunci alami akan lebih efisien daripada gabungan pada bilangan bulat - tidak banyak kunci alami bisa lebih kecil dari 4 byte, dan jika ya, tidak ada cukup unik baris untuk membuat perbedaan materi.
Aaron Bertrand
Untuk SQL yang kompeten dan dapat dioptimalkan, saya setuju, tetapi saya merujuk pada kemungkinan keterbatasan generator SQL. Satu-satunya pengalaman saya di bidang ini adalah diminta untuk membuat pandangan yang luas dengan mana EF dapat diberi makan - meskipun mungkin. Net devs tidak cukup tahu tentang EF, atau bahwa ada alasan lain.
TH
@ AaronBertrand Saya akan mengatakan bahwa satu-satunya cara di mana mereka mungkin lebih efisien adalah jika bergabung tidak diperlukan sama sekali. Satu-satunya tempat saya menganggap penggunaan kunci alami adalah dengan daftar kode standar seperti kode mata uang ISO4127 (yang dapat dikenali manusia), dan saya dapat menggunakan GBP, EUR dll sebagai kunci asing untuk kunci utama atau alternatif pada kode mata uang meja.
David Aldridge
@ David Tentu saja, saya berbicara tentang kasus di mana bergabung diperlukan. Ada banyak kasus di mana saya tidak ingin kunci alami berkembang biak di semua tabel terkait, karena kunci alam dapat berubah, dan itu adalah hal yang menyakitkan.
Aaron Bertrand
Hmmm, saya melihat bagaimana jawaban saya bisa disalahpahami untuk mempromosikan kunci asing alami daripada pengganti. Untuk lebih jelasnya, saya sebenarnya hanya menyebutkannya karena a) Saya membaca pertanyaan Alexei sebagai "apakah itu masalah yang tidak kita gunakan dengan kunci alami?", B) Pertanyaan penutup Alexei dimulai dengan "dari perspektif DBA" dan saya merasa saya harus semacam mengakui ada lebih dari satu perspektif dan c) karena saya akan berpikir fitur ORM yang akan digunakan sebagian besar menentukan pilihan (jika itu benar-benar dapat membuat perbedaan). Saya sendiri di kamp kunci asing pengganti.
TH
2

Anda memiliki beberapa faktor untuk membantu membimbing Anda,

  1. Definisi dan spesifikasi.

    Jika sesuatu didefinisikan unik oleh tugas atau hukum fisika, Anda membuang-buang waktu dengan kunci pengganti.

  2. Keunikan.

    Untuk kewarasan pribadi, bergabung, dan fungsionalitas basis data tingkat tinggi, Anda akan memerlukan, (a) kolom unik, (b) serangkaian kolom unik

    Semua skema normalisasi cukup (1NF) menyediakan salah satu dari berikut ini. Jika tidak, Anda harus selalu membuatnya. Jika Anda memiliki daftar orang yang akan menjadi sukarelawan pada hari Minggu, dan itu termasuk nama belakang dan nama depan, Anda akan ingin tahu kapan Anda memiliki dua Joe Bobs.

  3. Implementasi dan optimalisasi.

    Int cenderung menjadi bentuk data kecil yang cepat untuk perbandingan, dan kesetaraan. Bandingkan dengan string Unicode yang collation-nya dapat bergantung pada lokal (lokasi dan bahasa). Menyimpan 4242 dalam string ASCII / UTF8 adalah 4 byte. Menyimpannya sebagai integer cocok dalam 2 byte.

Jadi ketika datang ke kerugian Anda punya beberapa faktor.

  1. Kebingungan dan ambiguitas.

    1. Entri blog @Aaron Bertrand merangkum ini dengan baik. Tidak mendokumentasikan diri sendiri untuk memiliki OrderID dengan spesifikasi dan tugas, dan kemudian memaksakan " OrderID " melalui implementasi database. Kadang-kadang Anda harus mengklarifikasi itu atau membuat konvensi tetapi ini cenderung menambah kebingungan.
  2. Ruang.

    Integer masih menambah ruang pada baris. Dan, jika Anda tidak menggunakannya tidak ada tujuan.

  3. Clustering

    Anda hanya dapat memesan data satu arah. Jika Anda memaksakan kunci pengganti yang tidak diperlukan, apakah Anda mengelompok seperti itu atau cara kunci alami?

Evan Carroll
sumber
Pro & kontra bagus dan pendek.
Alexei
@Alexei terima kasih, pertimbangkan untuk menandainya sebagai yang dipilih jika memenuhi apa yang Anda cari. Atau, meminta klarifikasi.
Evan Carroll