Apa praktik terbaik untuk menggunakan GUID sebagai kunci utama, khususnya terkait kinerja?

336

Saya memiliki aplikasi yang menggunakan GUID sebagai Kunci Utama di hampir semua tabel dan saya telah membaca bahwa ada masalah tentang kinerja saat menggunakan GUID sebagai Kunci Utama. Jujur, saya belum melihat masalah, tapi saya akan memulai aplikasi baru dan saya masih ingin menggunakan GUID sebagai Kunci Utama, tapi saya berpikir untuk menggunakan Kunci Utama Komposit (GUID dan mungkin bidang lain .)

Saya menggunakan GUID karena mereka bagus dan mudah dikelola ketika Anda memiliki lingkungan yang berbeda seperti "produksi", "tes" dan "dev" database, dan juga untuk data migrasi antar database.

Saya akan menggunakan Entity Framework 4.3 dan saya ingin menetapkan Guid dalam kode aplikasi, sebelum memasukkannya ke dalam database. (Yaitu saya tidak ingin membiarkan SQL menghasilkan Guid).

Apa praktik terbaik untuk membuat Kunci Utama berbasis GUID, untuk menghindari dugaan kinerja yang terkait dengan pendekatan ini?

VAAA
sumber
20
Masalahnya tidak seharusnya. Jika PK Anda berkerumun maka hampir setiap sisipan berpotensi menyebabkan pemisahan halaman. Dalam versi modern SQL Server ini "diperbaiki" dengan NEWSEQUENTIALID (), tetapi ini kehilangan manfaat karena dapat menghitungnya sebelumnya. Saya sangat menyarankan Anda membaca tentang GUID di tempat lain karena ini adalah pertanyaan yang terlalu luas dan kemungkinan akan mengundang pertempuran agama yang akan berlangsung berjam-jam ...
Aaron Bertrand
4
Saya juga menambahkan bahwa kata server ambigu dalam Saya ingin menetapkan Guid di sisi server (tidak ingin membiarkan SQL untuk membuat GUID) .
Erik Philips
Pertanyaan ini memiliki kesamaan dengan "sql-server-guid-sort-algorithm-why" stackoverflow.com/questions/7810602/…
Clinton Ward

Jawaban:

495

GUIDs tampaknya menjadi pilihan alami untuk kunci utama Anda - dan jika Anda benar-benar harus melakukannya, Anda mungkin bisa berdebat untuk menggunakannya untuk KUNCI UTAMA tabel. Apa yang saya sangat menyarankan untuk tidak lakukan adalah menggunakan kolom GUID sebagai kunci pengelompokan , yang SQL Server lakukan secara default, kecuali jika Anda secara khusus mengatakannya untuk tidak melakukannya.

Anda benar-benar harus memisahkan dua masalah:

  1. yang kunci utama adalah membangun logis - salah satu kunci kandidat yang unik dan terpercaya mengidentifikasi setiap baris dalam tabel Anda. Ini bisa apa saja, benar-benar - sebuah INT, sebuah GUID, string - pilih apa yang paling masuk akal untuk skenario Anda.

  2. yang kunci pengelompokan (kolom atau kolom yang mendefinisikan "indeks berkerumun" di atas meja) - ini adalah fisik hal penyimpanan-terkait, dan di sini, sebuah, stabil, terus meningkat tipe data kecil adalah memilih yang terbaik Anda - INTatau BIGINTsebagai Anda pilihan standar.

Secara default, kunci utama pada tabel SQL Server juga digunakan sebagai kunci pengelompokan - tetapi itu tidak harus seperti itu! Saya pribadi telah melihat keuntungan kinerja besar ketika memecah Primer / Clustered Key berbasis GUID sebelumnya menjadi dua kunci terpisah - kunci primer (logis) pada GUID, dan kunci pengelompokan (pemesanan) pada INT IDENTITY(1,1)kolom terpisah .

Karena Kimberly Tripp - Ratu Pengindeksan - dan yang lainnya telah menyatakan berkali-kali - a GUIDkarena kunci pengelompokan tidak optimal, karena karena keacakannya, itu akan menyebabkan fragmentasi halaman dan indeks yang masif dan pada umumnya kinerja yang buruk.

Ya, saya tahu - ada newsequentialid()di SQL Server 2005 dan lebih tinggi - tetapi bahkan itu tidak benar-benar dan sepenuhnya berurutan dan dengan demikian juga menderita masalah yang sama dengan GUID- hanya sedikit kurang begitu mencolok.

Lalu ada masalah lain yang perlu dipertimbangkan: kunci pengelompokan pada tabel akan ditambahkan ke masing-masing dan setiap entri pada masing-masing dan setiap indeks yang tidak berkerumun di meja Anda juga - sehingga Anda benar-benar ingin memastikan itu sekecil mungkin. Biasanya, sebuah INTdengan 2+ miliar baris harus cukup untuk sebagian besar tabel - dan dibandingkan dengan GUIDsebagai kunci pengelompokan, Anda dapat menghemat ratusan megabyte penyimpanan pada disk dan memori server.

Penghitungan cepat - menggunakan INTvs. GUIDsebagai Primer dan Kunci Clustering:

  • Tabel Dasar dengan 1'000'000 baris (3,8 MB vs. 15,26 MB)
  • 6 indeks nonclustered (22,89 MB vs 91,55 MB)

JUMLAH: 25 MB vs. 106 MB - dan itu hanya satu tabel!

Beberapa lebih banyak makanan untuk dipikirkan - hal-hal yang sangat baik oleh Kimberly Tripp - baca, baca lagi, cerna! Ini adalah pengindeksan SQL Server, sungguh.

PS: tentu saja, jika Anda berurusan dengan hanya beberapa ratus atau beberapa ribu baris - sebagian besar argumen ini tidak akan benar-benar berdampak pada Anda. Namun: jika Anda masuk ke dalam puluhan atau ratusan ribu baris, atau Anda mulai menghitung dalam jutaan - maka poin-poin itu menjadi sangat penting dan sangat penting untuk dipahami.

Pembaruan: jika Anda ingin PKGUIDmenjadikan kolom Anda sebagai kunci utama (tetapi bukan kunci pengelompokan Anda), dan kolom lain MYINT( INT IDENTITY) sebagai kunci pengelompokan Anda - gunakan ini:

CREATE TABLE dbo.MyTable
(PKGUID UNIQUEIDENTIFIER NOT NULL,
 MyINT INT IDENTITY(1,1) NOT NULL,
 .... add more columns as needed ...... )

ALTER TABLE dbo.MyTable
ADD CONSTRAINT PK_MyTable
PRIMARY KEY NONCLUSTERED (PKGUID)

CREATE UNIQUE CLUSTERED INDEX CIX_MyTable ON dbo.MyTable(MyINT)

Pada dasarnya: Anda hanya perlu memberi tahu kendala secara eksplisitPRIMARY KEY bahwa itu NONCLUSTERED(jika tidak dibuat sebagai indeks berkerumun Anda, secara default) - dan kemudian Anda membuat indeks kedua yang didefinisikan sebagaiCLUSTERED

Ini akan berfungsi - dan ini opsi yang valid jika Anda memiliki sistem yang sudah ada yang perlu "direkayasa ulang" untuk kinerja. Untuk sistem baru, jika Anda mulai dari awal, dan Anda tidak berada dalam skenario replikasi, maka saya akan selalu memilih ID INT IDENTITY(1,1)sebagai kunci utama saya yang terkelompok - jauh lebih efisien daripada yang lainnya!

marc_s
sumber
2
Ini adalah jawaban yang bagus, satu hal yang saya sebutkan adalah kemampuan untuk menghasilkan kunci sebelum memasukkan sering berguna. Menggunakan "new followingentialid ()" dapat membantu dengan pengelompokan, tetapi itu memerlukan tambahan round-trip ke SQL. Jadi manfaat lain dari pendekatan "kunci pengganti" adalah Anda dapat menghasilkan id baru, di sisi klien, dengan lebih sedikit masalah fragmentasi indeks.
Andrew Theken
2
Cara saya membaca ini adalah bahwa memiliki kolom pengidentifikasi unik yang tidak berkerumun dan kolom identitas int, FK juga harus pengidentifikasi unik? Jika Anda melakukannya, kapan Anda benar-benar akan menggunakan kolom identitas secara langsung, atau tidak?
pinkfloydx33
2
Pertanyaan kecil, haruskah GUID sekarang digunakan pada bergabung, atau int id? Naluri saya memberi tahu saya bahwa GUID harus digunakan, tetapi saya gagal melihat masalah teknis dengan menggunakan id ...
Nicolas Belley
3
@marc_s tetapi dalam skenario replikasi, jika kolom int adalah identitas, bukankah kita harus menggunakan GUID karena kolom int dapat terulang di perangkat?
Nicolas Belley
6
@Kipei: masalah utama adalah JIKA Anda memiliki nilai alami - maka ya, Anda dapat menggunakannya sebagai kunci utama. TETAPI : nilai-nilai seperti DATETIMEmisalnya TIDAK berguna untuk kunci pengelompokan, karena mereka hanya memiliki akurasi 3,33 ms, dan dengan demikian duplikat dapat ada. Jadi dalam kasus seperti itu, Anda * masih memerlukan yang INT IDENTITYsebaliknya - karena itu, saya biasanya menggunakannya secara default, karena dari 20 tahun pengalaman saya, kunci alam yang benar-benar dapat digunakan hampir tidak pernah benar-benar ada ....
marc_s
51

Saya telah menggunakan GUID sebagai PK sejak 2005. Dalam dunia basis data terdistribusi ini, ini benar-benar cara terbaik untuk menggabungkan data terdistribusi. Anda bisa memecat dan melupakan menggabungkan tabel tanpa khawatir ints cocok dengan tabel yang bergabung. Gabung GUID dapat disalin tanpa khawatir.

Ini adalah pengaturan saya untuk menggunakan GUID:

  1. PK = GUID. GUID diindeks mirip dengan string, sehingga tabel baris tinggi (lebih dari 50 juta catatan) mungkin membutuhkan partisi tabel atau teknik kinerja lainnya. SQL Server menjadi sangat efisien, sehingga masalah kinerja semakin tidak berlaku.

  2. Guid PK adalah indeks NON-Clustered. Jangan pernah mengelompokkan indeks GUID kecuali NewSequentialID. Tetapi meskipun demikian, reboot server akan menyebabkan jeda besar dalam pemesanan.

  3. Tambahkan ClusterID Int ke setiap tabel. Ini adalah Indeks CLUSTERED Anda ... yang memesan meja Anda.

  4. Bergabung dengan ClusterIDs (int) lebih efisien, tetapi saya bekerja dengan 20-30 juta tabel rekaman, jadi bergabung dengan GUID tidak tampak memengaruhi kinerja. Jika Anda menginginkan kinerja maksimal, gunakan konsep ClusterID sebagai kunci utama Anda & gabung di ClusterID.

Ini tabel Email saya ...

CREATE TABLE [Core].[Email] (
    [EmailID]      UNIQUEIDENTIFIER CONSTRAINT [DF_Email_EmailID] DEFAULT (newsequentialid()) NOT NULL,        
    [EmailAddress] NVARCHAR (50)    CONSTRAINT [DF_Email_EmailAddress] DEFAULT ('') NOT NULL,        
    [CreatedDate]  DATETIME         CONSTRAINT [DF_Email_CreatedDate] DEFAULT (getutcdate()) NOT NULL,      
    [ClusterID] INT NOT NULL IDENTITY,
    CONSTRAINT [PK_Email] PRIMARY KEY NonCLUSTERED ([EmailID] ASC)
);
GO

CREATE UNIQUE CLUSTERED INDEX [IX_Email_ClusterID] ON [Core].[Email] ([ClusterID])
GO

CREATE UNIQUE NONCLUSTERED INDEX [IX_Email_EmailAddress] ON [Core].[Email] ([EmailAddress] Asc)
Robert J. Bagus
sumber
Bisakah Anda menjelaskan batasan PK_Email? Mengapa Anda ... NonClustered (EmailID ASC) alih-alih ... Nonclustered (ClusterID ASC)?
Phil
2
Anda bertaruh. Dua hal utama yang terjadi dengan indeks: 1. Clustered on ClusterID - Pesanan meja Anda pada disk (0% fragmentasi). 2. NonClustered on EmailID - Mengindeks bidang EmailID untuk mempercepat pencarian ID GUID. Pencarian bidang GUID berperilaku string-ish, sehingga pencarian EmailID akan lambat tanpa indeks.
Robert J. Good
@ RobertJ.Baik saya telah melihat metode ini dibahas sebelumnya yaitu menambahkan kunci int pengganti untuk cluster. Tapi saya tidak dapat menemukan di mana pun yang menunjukkan kenaikan kinerja dalam memiliki indeks kunci utama pengganti menggunakan tumpukan. Apakah Anda memiliki tautan ke data benchmark?
Dale K
1
Hai @aleBurrell, indeks berkerumun adalah untuk mencegah fragmentasi tabel. Keuntungan kinerja terjadi saat tabel tumbuh secara berurutan pada disk, dengan fragmentasi rendah.
Robert J. Good
@ RobertJ.Good Apakah itu aplikasi web? Apa yang Anda gunakan di url / hrefs? guid atau int?
dariol
10

Saat ini saya sedang mengembangkan aplikasi web dengan EF Core dan di sini adalah pola yang saya gunakan:

Semua kelas saya (tabel) dan PK int dan FK. Saya sudah mendapat kolom tambahan dengan tipe Guid (dihasilkan oleh konstruktor c #) dengan indeks non clustered di atasnya.

Semua gabungan tabel dalam EF dikelola melalui kunci int sementara semua akses dari luar (pengontrol) dilakukan dengan Panduan.

Solusi ini memungkinkan untuk tidak menunjukkan kunci int pada url tetapi menjaga model tetap rapi dan cepat.

EricImhauser
sumber
Apakah ada yang perlu Anda lakukan untuk mengonfigurasi integer pK sebagai clustered, seperti anotasi data, atau hanya dikonfigurasi secara otomatis?
Allen Wang
Apa nama properti yang Anda gunakan untuk Guid satu?
Trong Phan
3

Jika Anda menggunakan GUID sebagai kunci utama dan membuat indeks berkerumun maka saya sarankan gunakan nilai default NEWSEQUENTIALID () untuk itu

AnandPhadke
sumber
Kenapa kamu ingin melakukan itu?
genuinefafa
3

Tautan ini mengatakan lebih baik daripada yang saya bisa dan membantu dalam pengambilan keputusan saya. Saya biasanya memilih int sebagai kunci utama, kecuali saya memiliki kebutuhan khusus untuk tidak melakukannya dan saya juga membiarkan SQL server menghasilkan otomatis / memelihara bidang ini kecuali saya memiliki alasan khusus untuk tidak melakukannya. Pada kenyataannya, masalah kinerja perlu ditentukan berdasarkan aplikasi spesifik Anda. Ada banyak faktor yang berperan di sini termasuk tetapi tidak terbatas pada ukuran db yang diharapkan, pengindeksan yang tepat, permintaan yang efisien, dan banyak lagi. Meskipun orang mungkin tidak setuju, saya pikir dalam banyak skenario Anda tidak akan melihat perbedaan dengan salah satu opsi dan Anda harus memilih apa yang lebih sesuai untuk aplikasi Anda dan apa yang memungkinkan Anda untuk mengembangkan lebih mudah, lebih cepat, dan lebih efektif (Jika Anda tidak pernah menyelesaikan aplikasi apa bedanya :).

https://web.archive.org/web/20120812080710/http://databases.aspfaq.com/database/what-should-i-choose-for-my-primary-key.html

PS Saya tidak yakin mengapa Anda akan menggunakan PK Komposit atau manfaat apa yang Anda yakini akan memberi Anda.

Mat
sumber
Setuju!! Tetapi itu berarti bahwa jika saya memiliki GUID sebagai PK atau PK Komposit dengan GUID dan bidang lainnya akan sama, kan?
VAAA
1
PK (indeks) akan terdiri dari dua kolom, tetapi kecuali jika Anda memiliki alasan spesifik bisnis untuk melakukan ini, sepertinya tidak perlu.
Matt
1
BTW pertanyaan ini adalah salah satu pertanyaan paling polarisasi dan diperdebatkan di luar sana dan karena itu sangat sulit untuk mendapatkan jawaban untuk itu Anda akan merasa 100% nyaman dengan. Kedua metode dilengkapi dengan pengorbanan, semoga sukses :)
Matt
0

Memiliki ID sekuensial membuatnya menjadi BANYAK lebih mudah bagi peretas atau penambang data untuk berkompromi dengan situs dan data Anda. Ingatlah itu ketika memilih PK untuk situs web.

DaBlue
sumber
Bisakah Anda memberikan logika atau bukti untuk mendukung klaim ini? Saya berjuang untuk melihat bagaimana id berurutan dapat membahayakan keamanan.
jonaglon
Tentu, jika Anda tahu nomor ID adalah bilangan bulat, Anda dapat menebak rekaman secara berurutan dalam DB. Jadi, jika Anda kueri satu item, Anda dapat mengatakan bahwa item berikutnya adalah pk + 1. Jika Anda memiliki GUID acak, itu tidak akan mengikuti pola. Hampir tidak mungkin untuk meminta catatan lain dari yang sebelumnya Anda tanyakan (Dan ketahui PK).
DaBlue
1
Jika seorang hacker dapat menanyakan database Anda yang sudah Anda kompromi, saya gagal melihat bagaimana id berurutan membuat situasi lebih buruk.
jonaglon
1
Jika pengguna dapat mengganti 1012 untuk nomor lain dan melihat data yang seharusnya tidak ada, maka ada masalah keamanan yang sangat serius, masalah itu bukan disebabkan oleh pilihan kunci utama tetapi diperburuk olehnya. Saya benar-benar memahami maksud Anda, terima kasih telah mengutarakannya.
jonaglon
2
Anda dapat menggunakan GUID untuk menemukan catatan di halaman web, itu bukan PK tabel. Menggunakan parameter kueri di situs web seharusnya tidak menentukan bagaimana Anda menyusun skema DB Anda. PK tidak ada hubungannya dengan input dan parameter di UI atau sistem backend.
Panos Roditakis