"thaBadDawg" menawarkan jawaban yang bagus. Ada utas paralel tentang Stack Overflow yang membahas topik. Saya menambahkan beberapa komentar ke utas jawaban yang menautkan ke sumber daya dengan lebih detail. Inilah tautan pertanyaannya: stackoverflow.com/questions/547118/storing-mysql-guid-uuids - Saya berharap topik ini menjadi lebih umum ketika orang mulai mempertimbangkan AWS dan Aurora.
Zack Jannsen
Jawaban:
104
DBA saya bertanya ketika saya bertanya tentang cara terbaik untuk menyimpan GUID untuk objek saya mengapa saya perlu menyimpan 16 byte ketika saya bisa melakukan hal yang sama dalam 4 byte dengan Integer. Karena dia memberikan tantangan itu kepada saya, saya pikir sekarang adalah waktu yang tepat untuk menyebutkannya. Yang telah dibilang...
Anda dapat menyimpan panduan sebagai biner CHAR (16) jika Anda ingin memanfaatkan ruang penyimpanan yang paling optimal.
Karena dengan 16 byte, Anda dapat membuat berbagai hal dalam database yang berbeda, pada mesin yang berbeda, pada waktu yang berbeda, dan masih menggabungkan data bersama dengan mulus :)
Billy ONeal
4
perlu dibalas, apa sebenarnya biner char 16? bukan char? bukan biner? Saya tidak melihat itu ketik alat gui mysql, atau dokumentasi di situs mysql. @BillyONeal
nawfal
3
@nawfal: Char adalah tipe data. BINARY adalah specifier tipe terhadap tipe. Satu-satunya efek yang dimilikinya adalah memodifikasi cara MySQL melakukan collation. Lihat dev.mysql.com/doc/refman/5.0/id/charset-binary-op.html untuk lebih jelasnya. Tentu saja Anda bisa langsung menggunakan tipe BINARY jika alat pengeditan database memungkinkan Anda melakukannya. (Alat yang lebih tua tidak tahu tipe data biner tetapi tahu bendera kolom biner)
Billy ONeal
2
bidang CHAR dan BINARY pada dasarnya sama. Jika Anda ingin membawanya ke level paling dasar, CHAR adalah bidang biner yang mengharapkan nilai 0 hingga 255 dengan maksud mewakili nilai tersebut dengan nilai yang dipetakan dari tabel pencarian (dalam kebanyakan kasus sekarang, UTF8). Bidang BINARY mengharapkan jenis nilai yang sama tanpa maksud untuk mewakili data tersebut dari tabel pencarian. Saya menggunakan CHAR (16) pada hari-hari 4.x karena saat itu MySQL tidak sebagus sekarang.
thaBadDawg
15
Ada beberapa alasan bagus mengapa GUID jauh lebih baik daripada peningkatan otomatis. Jeff Atwood mendaftar yang ini . Bagi saya, keuntungan terbaik dalam menggunakan GUID adalah bahwa aplikasi saya tidak perlu bolak-balik basis data untuk mengetahui kunci suatu entitas: Saya dapat mengisinya secara terprogram, yang tidak dapat saya lakukan jika saya menggunakan bidang kenaikan-otomatis. Ini menyelamatkan saya dari beberapa sakit kepala: dengan GUID saya dapat mengelola entitas dengan cara yang sama, terlepas dari entitas yang sudah ada atau yang baru.
Saya tidak mengerti mengapa Anda harus menyimpannya -.
Afshin Mehrabani
2
@AfshinMehrabani Sederhana, lugas, mudah dibaca manusia. Itu tidak perlu, tentu saja, tetapi jika menyimpan byte tambahan itu tidak ada salahnya, ini adalah solusi terbaik.
user1717828
2
Menyimpan tanda hubung mungkin bukan ide yang baik karena akan menyebabkan lebih banyak overhead. Jika Anda ingin menjadikannya dapat dibaca manusia, buat aplikasi dibaca dengan tanda hubung.
Lucca Ferri
@AfshinMehrabani pertimbangan lain adalah menguraikannya dari database. Sebagian besar implementasi akan mengharapkan tanda hubung dalam panduan yang valid.
Ryan Gates
Anda dapat memasukkan tanda hubung saat mengambil untuk mengubah char (32) menjadi char (36) dengan mudah. gunakan FN Masukkan mySql.
joedotnot
33
Menambah jawaban oleh ThaBadDawg, gunakan fungsi-fungsi praktis ini (terima kasih kepada kolega saya yang lebih bijak) untuk mendapatkan dari string 36 panjang kembali ke array byte 16.
CHAR(16)sebenarnya adalah BINARY(16), pilih rasa yang Anda sukai
Untuk mengikuti kode dengan lebih baik, ambil contoh yang diberikan GUID yang dipesan dengan digit di bawah ini. (Karakter ilegal digunakan untuk tujuan ilustrasi - setiap tempat karakter unik.) Fungsi akan mengubah urutan byte untuk mencapai urutan bit untuk pengelompokan indeks superior. Panduan yang diperintahkan ditampilkan di bawah contoh.
Bagi yang penasaran, fungsi-fungsi ini lebih unggul dari hanya UNHEX (REPLACE (UUID (), '-', '')) karena mengatur bit dalam urutan yang akan berkinerja lebih baik dalam indeks berkerumun.
Slashterix
Ini sangat membantu, tetapi saya merasa ini dapat ditingkatkan dengan sumber CHARdan BINARYkesetaraan ( dokumen tampaknya menyiratkan ada perbedaan penting dan penjelasan mengapa kinerja indeks berkerumun lebih baik dengan byte yang disusun ulang.
Patrick M
Ketika saya menggunakan ini panduan saya berubah. Saya sudah mencoba memasukkannya menggunakan kedua unhex (ganti (string, '-', '')) dan fungsi di atas dan ketika saya mengubahnya kembali menggunakan metode yang sama panduan yang dipilih bukan yang dimasukkan. Apa yang mengubah pedoman? Yang saya lakukan hanyalah menyalin kode dari atas.
vsdev
@JonathanOliver Bisakah Anda berbagi kode untuk fungsi BinaryToGuid ()?
Arun Avanathan
27
char (36) akan menjadi pilihan yang bagus. Juga fungsi UUID () MySQL dapat digunakan yang mengembalikan format teks 36 karakter (heks dengan tanda hubung) yang dapat digunakan untuk pengambilan ID tersebut dari db.
"Lebih baik" tergantung pada apa yang Anda optimalkan.
Seberapa besar Anda peduli dengan ukuran / kinerja penyimpanan vs kemudahan pengembangan? Lebih penting - apakah Anda menghasilkan cukup GUID, atau cukup sering mengambilnya, sehingga itu penting?
Jika jawabannya "tidak", char(36)lebih dari cukup baik, dan itu membuat penyimpanan / pengambilan GUID menjadi sangat sederhana. Kalau tidak, binary(16)masuk akal, tetapi Anda harus bersandar pada MySQL dan / atau bahasa pemrograman pilihan Anda untuk mengkonversi bolak-balik dari representasi string yang biasa.
Jika Anda meng-host perangkat lunak (misalnya halaman web) dan tidak menjual / menginstal di klien, Anda selalu dapat memulai dengan char (36) untuk pengembangan mudah pada tahap awal perangkat lunak, dan bermutasi ke yang lebih ringkas format sebagai sistem tumbuh dalam penggunaan dan mulai membutuhkan optimasi.
Xavi Montero
1
Sisi buruk terbesar dari char yang jauh lebih besar (36) adalah seberapa banyak ruang yang akan diambil oleh indeks. Jika Anda memiliki sejumlah besar catatan dalam database, Anda menggandakan ukuran indeks.
bpeikes
8
Binary (16) akan lebih baik, lebih baik daripada penggunaan varchar (32).
Rutin GuidToBinary yang diposting oleh KCD harus disesuaikan untuk memperhitungkan tata letak bit stempel waktu dalam string GUID. Jika string mewakili UUID versi 1, seperti yang dikembalikan oleh rutin uuid () mysql, maka komponen waktu disematkan dalam huruf 1-G, tidak termasuk D.
12345678-9ABC-DEFG-HIJK-LMNOPQRSTUVW
12345678= least significant 4 bytes of the timestamp in big endian order9ABC = middle 2 timestamp bytes in big endian
D =1to signify a version 1 UUID
EFG = most significant 12 bits of the timestamp in big endian
Ketika Anda mengonversi ke biner, urutan terbaik untuk pengindeksan adalah: EFG9ABC12345678D + sisanya.
Anda tidak ingin menukar 12345678 ke 78563412 karena big endian sudah menghasilkan urutan byte indeks biner terbaik. Namun, Anda ingin byte yang paling signifikan dipindahkan di depan byte yang lebih rendah. Oleh karena itu, EFG pergi dulu, diikuti oleh bit tengah dan bit rendah. Hasilkan selusin UUID dengan uuid () selama satu menit dan Anda akan melihat bagaimana pesanan ini menghasilkan peringkat yang benar.
Dua UUID pertama dihasilkan paling mendekati waktu. Mereka hanya bervariasi dalam 3 gigitan terakhir dari blok pertama. Ini adalah bit paling tidak signifikan dari cap waktu, yang berarti kita ingin mendorong mereka ke kanan ketika kita mengonversinya ke array byte yang dapat diindeks. Sebagai contoh balasan, ID terakhir adalah yang terbaru, tetapi algoritma swapping KCD akan menempatkannya di depan ID ke-3 (3e sebelum dc, byte terakhir dari blok pertama).
Perhatikan bahwa saya tidak membagi nibble versi dari 12 bit cap waktu yang tinggi. Ini adalah gigitan D dari contoh Anda. Saya hanya membuangnya di depan. Jadi urutan biner saya akhirnya menjadi DEFG9ABC dan seterusnya. Ini menyiratkan bahwa semua UUID saya yang diindeks mulai dengan gigitan yang sama. Artikel itu melakukan hal yang sama.
Saya membaca artikel itu sebelumnya. Saya merasa sangat menarik tetapi kemudian bagaimana kita melakukan query jika kita ingin memfilter dengan ID yang biner? Saya kira kita perlu hex lagi dan kemudian menerapkan kriteria. Apakah ini sangat menuntut? Mengapa menyimpan binary (16) (tentu lebih baik daripada varchar (36)) daripada bigint 8 byte?
fwiw, UUIDv4 benar-benar acak dan tidak perlu memotong.
Mahmoud Al-Qudsi
2
Saya akan menyarankan menggunakan fungsi di bawah ini karena yang disebutkan oleh @ bigh_29 mengubah panduan saya menjadi yang baru (karena alasan saya tidak mengerti). Juga, ini sedikit lebih cepat dalam tes yang saya lakukan di meja saya. https://gist.github.com/damienb/159151
jika Anda memiliki nilai char / varchar yang diformat sebagai GUID standar, Anda dapat menyimpannya sebagai BINARY (16) menggunakan CAST sederhana (MyString AS BINARY16), tanpa semua urutan CONCAT + SUBSTR yang membingungkan.
BINARY (16) bidang dibandingkan / diurutkan / diindeks jauh lebih cepat daripada string, dan juga mengambil dua kali lebih sedikit ruang dalam database
Menjalankan kueri ini menunjukkan bahwa CAST mengubah string uuid ke ASCII byte: set @a = uuid (); pilih @a, hex (cast (@a AS BINARY (16))); Saya mendapatkan 16f20d98-9760-11e4-b981-feb7b39d48d6: 3136663230643938 2D 39373630 2D 3131 (spasi ditambahkan untuk pemformatan). 0x31 = ascii 1, 0x36 = ascii 6. Kita bahkan mendapatkan 0x2D, yang merupakan tanda hubung. Ini tidak jauh berbeda dari hanya menyimpan panduan sebagai string, kecuali bahwa Anda memotong string pada karakter ke-16, yang memotong bagian dari ID yang spesifik mesin.
bigh_29
Ya, ini hanya pemotongan. select CAST("hello world, this is as long as uiid" AS BINARY(16));menghasilkanhello world, thi
Jawaban:
DBA saya bertanya ketika saya bertanya tentang cara terbaik untuk menyimpan GUID untuk objek saya mengapa saya perlu menyimpan 16 byte ketika saya bisa melakukan hal yang sama dalam 4 byte dengan Integer. Karena dia memberikan tantangan itu kepada saya, saya pikir sekarang adalah waktu yang tepat untuk menyebutkannya. Yang telah dibilang...
Anda dapat menyimpan panduan sebagai biner CHAR (16) jika Anda ingin memanfaatkan ruang penyimpanan yang paling optimal.
sumber
Saya akan menyimpannya sebagai char (36).
sumber
-
.Menambah jawaban oleh ThaBadDawg, gunakan fungsi-fungsi praktis ini (terima kasih kepada kolega saya yang lebih bijak) untuk mendapatkan dari string 36 panjang kembali ke array byte 16.
CHAR(16)
sebenarnya adalahBINARY(16)
, pilih rasa yang Anda sukaiUntuk mengikuti kode dengan lebih baik, ambil contoh yang diberikan GUID yang dipesan dengan digit di bawah ini. (Karakter ilegal digunakan untuk tujuan ilustrasi - setiap tempat karakter unik.) Fungsi akan mengubah urutan byte untuk mencapai urutan bit untuk pengelompokan indeks superior. Panduan yang diperintahkan ditampilkan di bawah contoh.
Tanda hubung dihapus:
sumber
GuidToBinary
($ guid char (36)) RETURNS biner (16) RETURN CONCAT (UNHEX (SUBSTRING ($ guid, 7, 2)), UNHEX (SUBSTRING ($ guid, 5, 2)), UNHEX (SUBSTRING ($ guid, 3, 2)), UNHEX (SUBSTRING ($ guid, 1, 2)), UNHEX (SUBSTRING ($ guid, 12, 2)), UNHEX (SUBSTRING ($ guid, 10, 2)), UNHEX (SUBSTRING ($ guid, 17, 2)), UNHEX (SUBSTRING ($ guid, 15, 2)), UNHEX (SUBSTRING ($ guid, 20, 4)), UNHEX (SUBSTRING ($ guid, 25, 12)));CHAR
danBINARY
kesetaraan ( dokumen tampaknya menyiratkan ada perbedaan penting dan penjelasan mengapa kinerja indeks berkerumun lebih baik dengan byte yang disusun ulang.char (36) akan menjadi pilihan yang bagus. Juga fungsi UUID () MySQL dapat digunakan yang mengembalikan format teks 36 karakter (heks dengan tanda hubung) yang dapat digunakan untuk pengambilan ID tersebut dari db.
sumber
"Lebih baik" tergantung pada apa yang Anda optimalkan.
Seberapa besar Anda peduli dengan ukuran / kinerja penyimpanan vs kemudahan pengembangan? Lebih penting - apakah Anda menghasilkan cukup GUID, atau cukup sering mengambilnya, sehingga itu penting?
Jika jawabannya "tidak",
char(36)
lebih dari cukup baik, dan itu membuat penyimpanan / pengambilan GUID menjadi sangat sederhana. Kalau tidak,binary(16)
masuk akal, tetapi Anda harus bersandar pada MySQL dan / atau bahasa pemrograman pilihan Anda untuk mengkonversi bolak-balik dari representasi string yang biasa.sumber
Binary (16) akan lebih baik, lebih baik daripada penggunaan varchar (32).
sumber
Rutin GuidToBinary yang diposting oleh KCD harus disesuaikan untuk memperhitungkan tata letak bit stempel waktu dalam string GUID. Jika string mewakili UUID versi 1, seperti yang dikembalikan oleh rutin uuid () mysql, maka komponen waktu disematkan dalam huruf 1-G, tidak termasuk D.
Ketika Anda mengonversi ke biner, urutan terbaik untuk pengindeksan adalah: EFG9ABC12345678D + sisanya.
Anda tidak ingin menukar 12345678 ke 78563412 karena big endian sudah menghasilkan urutan byte indeks biner terbaik. Namun, Anda ingin byte yang paling signifikan dipindahkan di depan byte yang lebih rendah. Oleh karena itu, EFG pergi dulu, diikuti oleh bit tengah dan bit rendah. Hasilkan selusin UUID dengan uuid () selama satu menit dan Anda akan melihat bagaimana pesanan ini menghasilkan peringkat yang benar.
Dua UUID pertama dihasilkan paling mendekati waktu. Mereka hanya bervariasi dalam 3 gigitan terakhir dari blok pertama. Ini adalah bit paling tidak signifikan dari cap waktu, yang berarti kita ingin mendorong mereka ke kanan ketika kita mengonversinya ke array byte yang dapat diindeks. Sebagai contoh balasan, ID terakhir adalah yang terbaru, tetapi algoritma swapping KCD akan menempatkannya di depan ID ke-3 (3e sebelum dc, byte terakhir dari blok pertama).
Urutan pengindeksan yang benar adalah:
Lihat artikel ini untuk informasi pendukung: http://mysql.rjweb.org/doc.php/uuid
Perhatikan bahwa saya tidak membagi nibble versi dari 12 bit cap waktu yang tinggi. Ini adalah gigitan D dari contoh Anda. Saya hanya membuangnya di depan. Jadi urutan biner saya akhirnya menjadi DEFG9ABC dan seterusnya. Ini menyiratkan bahwa semua UUID saya yang diindeks mulai dengan gigitan yang sama. Artikel itu melakukan hal yang sama.
sumber
Bagi mereka yang hanya tersandung di sini, sekarang ada alternatif yang jauh lebih baik sesuai penelitian oleh Percona.
Ini terdiri dari reorganisasi potongan UUID untuk pengindeksan yang optimal, kemudian dikonversi menjadi biner untuk penyimpanan berkurang.
Baca artikel selengkapnya di sini
sumber
Saya akan menyarankan menggunakan fungsi di bawah ini karena yang disebutkan oleh @ bigh_29 mengubah panduan saya menjadi yang baru (karena alasan saya tidak mengerti). Juga, ini sedikit lebih cepat dalam tes yang saya lakukan di meja saya. https://gist.github.com/damienb/159151
sumber
jika Anda memiliki nilai char / varchar yang diformat sebagai GUID standar, Anda dapat menyimpannya sebagai BINARY (16) menggunakan CAST sederhana (MyString AS BINARY16), tanpa semua urutan CONCAT + SUBSTR yang membingungkan.
BINARY (16) bidang dibandingkan / diurutkan / diindeks jauh lebih cepat daripada string, dan juga mengambil dua kali lebih sedikit ruang dalam database
sumber
select CAST("hello world, this is as long as uiid" AS BINARY(16));
menghasilkanhello world, thi