Apakah ada penalti karena menggunakan BINARY (16) dan bukannya UNIQUEIDENTIFIER?

19

Saya baru-baru ini mewarisi database SQL Server yang menggunakan BINARY(16)alih-alih UNIQUEIDENTIFIERuntuk menyimpan Guids. Ini melakukan ini untuk semuanya termasuk kunci utama.

Haruskah saya khawatir?

Jonathan Allen
sumber
Apakah ini menggunakan biner (16) secara konsisten di seluruh? Termasuk untuk variabel dan parameter? Jika tidak, Anda perlu mempertimbangkan efek gips implisit.
Martin Smith
Ya, untungnya saya tidak harus berurusan dengan para pemain implisit juga.
Jonathan Allen

Jawaban:

21

Haruskah saya khawatir?

Nah, ada beberapa hal di sini yang sedikit memprihatinkan.

Pertama: sementara benar bahwa UNIQUEIDENTIFIER(yaitu Guid) adalah nilai biner 16-byte, juga benar bahwa:

  1. Semua data dapat disimpan dalam bentuk biner (mis. INTDapat disimpan BINARY(4), DATETIMEdapat disimpan BINARY(8), dll), karenanya # 2 ↴
  2. Mungkin ada alasan untuk memiliki tipe data terpisah untuk GUID di luar kenyamanan semata (misalnya sysnamesebagai alias untuk NVARCHAR(128)).

Tiga perbedaan perilaku yang dapat saya temukan adalah:

  • Membandingkan UNIQUEIDENTIFIERnilai dalam SQL Server, baik atau buruk, sebenarnya tidak dilakukan dengan cara yang sama seperti membandingkan BINARY(16)nilai. Menurut halaman MSDN untuk Membandingkan GUID dan Uniqueidentifier Values , ketika membandingkan UNIQUEIDENTIFIERnilai dalam SQL Server:

    enam byte terakhir dari nilai paling signifikan

  • Meskipun nilai-nilai ini tidak sering diurutkan, ada sedikit perbedaan antara kedua jenis ini. Menurut halaman MSDN untuk pengidentifikasi unik :

    pemesanan tidak diterapkan dengan membandingkan pola bit dari dua nilai.

  • Mengingat bahwa ada perbedaan dalam bagaimana nilai-nilai GUID ditangani antara SQL Server dan .NET (dicatat dalam halaman "Membandingkan GUID dan Uniqueidentifier Values" yang ditautkan di atas), menarik data ini dari SQL Server ke kode aplikasi mungkin tidak ditangani dengan benar di kode aplikasi jika perlu meniru perilaku perbandingan SQL Server. Perilaku itu dapat ditiru dengan mengonversi ke SqlGuid, tetapi apakah pengembang tahu untuk melakukan itu?

Kedua: berdasarkan pernyataan berikut

Ini melakukan ini untuk semuanya termasuk kunci utama.

Saya akan prihatin secara umum untuk kinerja sistem dengan menggunakan GUID sebagai PK alih-alih sebagai Kunci Alternatif bersamaan dengan menggunakan INTatau bahkan BIGINTsebagai PK. Dan bahkan lebih khawatir jika PK GUID ini adalah Indeks Clustered.

MEMPERBARUI

Komentar berikut, yang dibuat oleh OP pada jawaban @ Rob, memunculkan keprihatinan tambahan:

itu bermigrasi dari saya pikir MySQL

GUID dapat disimpan dalam 2 format biner yang berbeda . Jadi, mungkin ada alasan untuk khawatir tergantung pada:

  1. di mana sistem representasi biner dihasilkan, dan
  2. jika nilai string digunakan di luar sistem asli, seperti dalam kode aplikasi atau diberikan kepada klien untuk digunakan dalam file impor, dll.

Masalah dengan di mana representasi biner dihasilkan berkaitan dengan pemesanan byte dari 3 pertama dari 4 "bidang". Jika Anda mengikuti tautan di atas ke artikel Wikipedia, Anda akan melihat bahwa RFC 4122 menentukan untuk menggunakan pengkodean "Big Endian" untuk semua 4 bidang, namun Microsoft GUID menentukan dengan menggunakan Endianness "Asli". Yah, arsitektur Intel adalah Little Endian, maka urutan byte untuk 3 bidang pertama dibalik dari sistem mengikuti RFC (serta GUID gaya Microsoft yang dihasilkan pada sistem Big Endian). Bidang pertama, "Data 1", adalah 4 byte. Dalam satu Endianness itu akan direpresentasikan sebagai (secara hipotesis) 0x01020304. Tetapi di Endianness lain itu akan menjadi 0x04030201. Jadi jika database saat ini 'BINARY(16)bahwa representasi biner dihasilkan pada sistem yang mengikuti RFC, lalu mengonversi data yang saat ini ada di dalam BINARY(16)bidang UNIQUEIDENTIFIERakan menghasilkan GUID yang berbeda dari yang awalnya dibuat. Ini tidak benar-benar menimbulkan masalah JIKA nilai-nilai tidak pernah meninggalkan database, dan nilai-nilai hanya pernah dibandingkan untuk kesetaraan dan bukan pemesanan.

Kekhawatiran dengan pemesanan hanya karena mereka tidak akan berada dalam urutan yang sama setelah mengonversi UNIQUEIDENTIFIER. Untungnya, jika sistem asli benar-benar MySQL maka pemesanan tidak pernah dilakukan pada representasi biner karena MySQL hanya memiliki string representasi UUID .

Kekhawatiran dengan nilai string yang digunakan di luar database lebih serius, sekali lagi, jika representasi biner dihasilkan di luar Windows / SQL Server. Karena pemesanan byte berpotensi berbeda, maka GUID yang sama dalam bentuk string akan menghasilkan 2 representasi biner yang berbeda, tergantung di mana konversi itu terjadi. Jika kode aplikasi atau pelanggan diberi GUID dalam bentuk string yang ABCberasal dari bentuk biner 123 dan representasi biner dihasilkan pada sistem yang mengikuti RFC, maka representasi biner yang sama (yaitu 123) akan menerjemahkan ke bentuk string DEFketika dikonversi ke a UNIQUEIDENTIFIER. Demikian juga, bentuk string asli ABCakan dikonversi ke bentuk biner 456saat dikonversi ke a UNIQUEIDENTIFIER.

Jadi, jika GUID tidak pernah meninggalkan database maka tidak ada yang perlu dikhawatirkan di luar pemesanan. Atau, jika impor dari MySQL dilakukan dengan mengonversi bentuk string (yaitu FCCEC3D8-22A0-4C8A-BF35-EC18227C9F40) maka mungkin ok. Selain itu, jika GUID itu diberikan kepada pelanggan atau dalam kode aplikasi, Anda dapat menguji untuk melihat bagaimana mereka mengonversi dengan mendapatkannya dan mengonversi melalui SELECT CONVERT(UNIQUEIDENTIFIER, 'value found outside of the database');dan melihat apakah Anda menemukan catatan yang diharapkan. Jika Anda tidak dapat mencocokkan catatan maka Anda mungkin harus menjaga bidang sebagai BINARY(16).

Kemungkinan besar tidak akan ada masalah, tetapi saya menyebutkan ini karena dalam kondisi yang tepat mungkin ada masalah.

Dan bagaimana pula GUID baru bisa dimasukkan? Dihasilkan dalam kode aplikasi?

PEMBARUAN 2

Jika penjelasan sebelumnya tentang masalah potensial terkait dengan mengimpor representasi biner dari GUID yang dihasilkan pada sistem lain sedikit (atau banyak) membingungkan, mudah-mudahan hal berikut akan sedikit lebih jelas:

DECLARE @GUID UNIQUEIDENTIFIER = NEWID();
SELECT @GUID AS [String], CONVERT(BINARY(16), @GUID) AS [Binary];
-- String = 5FED23BE-E52C-40EE-8F45-49664C9472FD
-- Binary = 0xBE23ED5F2CE5EE408F4549664C9472FD
--          BE23ED5F-2CE5-EE40-8F45-49664C9472FD

Dalam output yang ditunjukkan di atas, nilai "String" dan "Binary" berasal dari GUID yang sama. Nilai di bawah garis "Binary" adalah nilai yang sama dengan baris "Binary", tetapi diformat dalam gaya yang sama dengan garis "String" (yaitu dihapus "0x" dan menambahkan empat garis). Membandingkan nilai pertama dan ketiga, mereka tidak persis sama, tetapi mereka sangat dekat: paling kanan dua bagian identik, tetapi paling kiri tiga bagian tidak. Tetapi jika Anda melihat lebih dekat, Anda dapat melihat bahwa itu adalah byte yang sama di masing-masing dari tiga bagian, hanya dalam urutan yang berbeda. Mungkin lebih mudah untuk melihat apakah saya hanya menampilkan tiga bagian pertama, dan memberi nomor byte sehingga lebih mudah untuk melihat bagaimana urutannya berbeda antara dua representasi:

String = 1 5F 2 ED 3 23 4 BE - 5 E5 6 2C - 7 40 8 EE
Binary = 4 BE 3 23 2 ED 1 5F - 6 2C 5 E5 - 8 EE 7 40 (di Windows / SQL Server)

Jadi dalam setiap pengelompokan, urutan byte dibalik, tetapi hanya di Windows dan juga SQL Server. Namun, pada sistem yang mematuhi RFC, representasi biner akan mencerminkan representasi sengatan karena tidak akan ada pembalikan urutan byte.

Bagaimana data dibawa ke SQL Server dari MySQL? Berikut ini beberapa pilihan:

SELECT CONVERT(BINARY(16), '5FED23BE-E52C-40EE-8F45-49664C9472FD'),
       CONVERT(BINARY(16), 0x5FED23BEE52C40EE8F4549664C9472FD),
    CONVERT(BINARY(16), CONVERT(UNIQUEIDENTIFIER, '5FED23BE-E52C-40EE-8F45-49664C9472FD'));

Pengembalian:

0x35464544323342452D453532432D3430  
0x5FED23BEE52C40EE8F4549664C9472FD  
0xBE23ED5F2CE5EE408F4549664C9472FD

Dengan asumsi itu adalah biner langsung ke biner (yaitu Konversi # 2 di atas), maka GUID yang dihasilkan, jika dikonversi menjadi aktual UNIQUEIDENTIFIER, akan menjadi:

SELECT CONVERT(UNIQUEIDENTIFIER, 0x5FED23BEE52C40EE8F4549664C9472FD);

Pengembalian:

BE23ED5F-2CE5-EE40-8F45-49664C9472FD

Yang salah. Dan itu meninggalkan kita dengan tiga pertanyaan:

  1. Bagaimana data diimpor ke SQL Server?
  2. Bahasa apa yang digunakan kode aplikasi?
  3. Platform apa yang menjalankan kode aplikasi?
Solomon Rutzky
sumber
Saya akan berasumsi bahwa GUID dihasilkan dalam aplikasi, karena saya tidak melihatnya dalam database.
Jonathan Allen
Saya tidak bisa mengatakan saya benar-benar mengikuti penjelasan tentang pemesanan byte, tetapi itu membuat saya berpikir tentang pengindeksan. Akankah pengidentifikasi unik lebih atau kurang cenderung menghasilkan fragmentasi indeks daripada biner?
Jonathan Allen
2
@JonathanAllen Saya menambahkan bagian UPDATE lain untuk semoga menjelaskan lebih baik. Dan tidak, pengindeksan tidak akan berbeda di antara mereka.
Solomon Rutzky
"Syukurlah", SQL Server tidak mengubah urutan antara Variant 1 dan Variant 2 - bahkan jika 'bisa' disimpan secara berbeda pada disk, itu adalah urutan membingungkan yang sama secara konsisten.
user2864740
5

Anda selalu bisa khawatir. ;)

Sistem mungkin telah dimigrasikan dari beberapa sistem lain yang tidak mendukung pengidentifikasi unik. Apakah ada kompromi lain yang tidak Anda ketahui?

Perancang mungkin tidak tahu tentang tipe pengenal unik. Hal-hal apa lagi yang tidak mereka ketahui?

Namun secara teknis - itu seharusnya tidak menjadi perhatian utama.

Rob Farley
sumber
Ya, itu dimigrasikan dari saya pikir MySQL. Dan ya, ada banyak ... hal menarik untuk dilihat.
Jonathan Allen