Apa perbedaan antara utf8_general_ci dan utf8_unicode_ci?

1063

Antara utf8_general_cidan utf8_unicode_ci, apakah ada perbedaan dalam hal kinerja?

Kahee Teng
sumber
6
Jika Anda suka utf8[mb4]_unicode_ci, Anda mungkin lebih suka utf8[mb4]_unicode_520_ci.
Rick James
8
Saya tidak tahu bagaimana perasaan saya tentang hal itu - alih-alih memperbaiki implementasi mereka untuk mengikuti standar Unicode terbaru mereka tetap menggunakan versi usang sebagai default dan orang harus menambahkan "520" untuk menggunakan yang benar sekarang. Dan itu tidak kompatibel maju dan mundur karena Anda tidak dapat menggunakan versi "520" pada versi MySQL yang lebih lama. Mengapa mereka tidak bisa baru saja memperbarui pemeriksaan mereka yang ada? Sama dengan "mb4", sungguh. Kode apa yang benar-benar bergantung pada perilaku lama, terbatas / usang untuk membenarkan menjaganya sebagai default?
thomasrutter
7
Masih lebih baik 8,0 default utf8mb4_0900_ai_ci.
Rick James

Jawaban:

1591

Kedua kumpulan ini keduanya untuk pengkodean karakter UTF-8. Perbedaannya terletak pada bagaimana teks diurutkan dan dibandingkan.

Catatan: Di MySQL Anda harus menggunakan utf8mb4daripada utf8. Yang membingungkan, utf8adalah implementasi UTF-8 yang cacat dari versi MySQL awal yang tetap hanya untuk kompatibilitas ke belakang. Versi tetap diberi nama utf8mb4.

Catatan: Versi MySQL yang lebih baru telah memperbarui aturan penyortiran Unicode, tersedia dengan nama seperti utf8mb4_0900_ai_ci aturan setara berdasarkan Unicode 9.0 - dan tanpa _general varian yang setara . Orang yang membaca ini sekarang mungkin harus menggunakan salah satu dari kumpulan yang lebih baru ini, bukan salah satu _unicode atau _general . Banyak dari apa yang ditulis di bawah ini tidak menarik lagi jika Anda dapat menggunakan salah satu dari kumpulan yang lebih baru.

Perbedaan utama

  • utf8mb4_unicode_ci didasarkan pada aturan Unicode resmi untuk pengurutan dan perbandingan universal, yang mengurutkan secara akurat dalam berbagai bahasa.

  • utf8mb4_general_ciadalah seperangkat aturan penyortiran yang disederhanakan yang bertujuan untuk melakukan sebaik mungkin sambil mengambil banyak jalan pintas yang dirancang untuk meningkatkan kecepatan. Itu tidak mengikuti aturan Unicode dan akan menghasilkan penyortiran yang tidak diinginkan atau perbandingan dalam beberapa situasi, seperti ketika menggunakan bahasa atau karakter tertentu.

    Pada server modern, peningkatan kinerja ini akan sangat kecil. Itu dirancang pada saat server memiliki sebagian kecil dari kinerja CPU komputer saat ini.

Manfaat utf8mb4_unicode_cilebihutf8mb4_general_ci

utf8mb4_unicode_ci, yang menggunakan aturan Unicode untuk pengurutan dan perbandingan, menggunakan algoritma yang cukup rumit untuk pengurutan yang benar dalam berbagai bahasa dan ketika menggunakan berbagai karakter khusus. Aturan-aturan ini perlu mempertimbangkan konvensi khusus bahasa akun; tidak semua orang mengurutkan karakter mereka dalam apa yang kita sebut 'urutan abjad'.

Sejauh bahasa Latin (yaitu "Eropa"), tidak ada banyak perbedaan antara pengurutan Unicode dan pengurutan yang disederhanakan utf8mb4_general_cidi MySQL, tetapi masih ada beberapa perbedaan:

  • Sebagai contoh, pemeriksaan Unicode mengurutkan "ß" seperti "ss", dan "Œ" seperti "OE" seperti yang diinginkan orang yang menggunakan karakter tersebut, sedangkan utf8mb4_general_cimengurutkannya sebagai karakter tunggal (mungkin masing-masing seperti huruf "s" dan "e") .

  • Beberapa karakter Unicode didefinisikan sebagai diabaikan, yang berarti mereka tidak boleh diperhitungkan dalam urutan pengurutan dan perbandingan harus beralih ke karakter berikutnya. utf8mb4_unicode_cimenangani ini dengan benar.

Dalam bahasa non-latin, seperti bahasa Asia atau bahasa dengan huruf yang berbeda, mungkin ada lebih banyak perbedaan antara pengurutan Unicode dan pengurutan yang disederhanakan utf8mb4_general_ci. Kesesuaian utf8mb4_general_ciakan sangat bergantung pada bahasa yang digunakan. Untuk beberapa bahasa, itu akan sangat tidak memadai.

Apa yang harus Anda gunakan?

Hampir tidak ada alasan untuk menggunakannya utf8mb4_general_cilagi, karena kami telah meninggalkan titik di mana kecepatan CPU cukup rendah sehingga perbedaan kinerja akan menjadi penting. Basis data Anda hampir pasti akan dibatasi oleh kemacetan lain selain ini.

Di masa lalu, beberapa orang merekomendasikan untuk menggunakan utf8mb4_general_cikecuali ketika penyortiran yang akurat akan menjadi cukup penting untuk membenarkan biaya kinerja. Saat ini, biaya kinerja telah hilang sama sekali, dan pengembang memperlakukan internasionalisasi dengan lebih serius.

Ada argumen yang dibuat bahwa jika kecepatan lebih penting bagi Anda daripada akurasi, Anda mungkin juga tidak melakukan penyortiran sama sekali. Itu sepele untuk membuat algoritma lebih cepat jika Anda tidak perlu akurat. Jadi, utf8mb4_general_ciadalah kompromi yang mungkin tidak diperlukan untuk alasan kecepatan dan mungkin juga tidak cocok untuk alasan akurasi.

Satu hal lagi yang akan saya tambahkan adalah bahwa meskipun Anda tahu aplikasi Anda hanya mendukung bahasa Inggris, mungkin masih perlu berurusan dengan nama orang, yang sering kali dapat berisi karakter yang digunakan dalam bahasa lain di mana sama pentingnya untuk mengurutkan dengan benar . Menggunakan aturan Unicode untuk semuanya membantu menambah ketenangan pikiran bahwa orang-orang Unicode yang sangat pintar telah bekerja sangat keras untuk membuat penyortiran berfungsi dengan baik.

Apa arti bagian-bagian itu

Pertama, ciuntuk penyortiran dan perbandingan case-insensitive . Ini berarti cocok untuk data tekstual, dan kasus tidak penting. Tipe-tipe lain dari collation adalah cs(case-sensitive) untuk data tekstual di mana case adalah penting, dan bin, untuk di mana encoding harus cocok, bit for bit, yang cocok untuk bidang yang benar-benar dikodekan data biner (termasuk, misalnya, Base64). Penyortiran case-sensitive mengarah pada beberapa hasil yang aneh dan perbandingan case-sensitive dapat menghasilkan nilai duplikat yang berbeda hanya dalam case letter, sehingga pengumpulan case-sensitive tidak disukai untuk data tekstual - jika case penting bagi Anda, maka tanda baca yang dapat diabaikan dan seterusnya mungkin juga signifikan, dan pemeriksaan biner mungkin lebih tepat.

Berikutnya, unicodeatau generalmengacu pada aturan penyortiran dan perbandingan khusus - khususnya, cara teks dinormalisasi atau dibandingkan. Ada banyak set aturan yang berbeda untuk pengkodean karakter utf8mb4, dengan unicodedan generalmenjadi dua yang berusaha untuk bekerja dengan baik dalam semua bahasa yang mungkin daripada satu yang spesifik. Perbedaan antara kedua perangkat aturan ini adalah subjek dari jawaban ini. Catatan yang unicodemenggunakan aturan dari Unicode 4.0. Versi terbaru dari MySQL menambahkan aturan unicode_520menggunakan aturan dari Unicode 5.2, dan 0900(menjatuhkan bagian "unicode_") menggunakan aturan dari Unicode 9.0.

Dan yang terakhir, utf8mb4tentu saja pengkodean karakter digunakan secara internal. Dalam jawaban ini saya hanya berbicara tentang pengkodean berbasis Unicode.

thomasrutter
sumber
218
@ KahWeeTeng Anda seharusnya tidak pernah menggunakan utf8_general_ci: itu tidak bekerja. Ini adalah kemunduran ke masa lalu yang buruk dari stooopeeedity ASCII dari lima puluh tahun yang lalu. Pencocokan case-insensitive Unicode tidak dapat dilakukan tanpa peta lipat dari UCD. Misalnya, "Σίσυφος" memiliki tiga sigma yang berbeda di dalamnya; atau bagaimana huruf kecil "TSCHüẞ" adalah "tschüβ", tetapi huruf besar dari "tschüβ" adalah "TSCHÜSS". Anda bisa benar, atau Anda bisa cepat. Karena itu Anda harus menggunakan utf8_unicode_ci, karena jika Anda tidak peduli tentang kebenaran, maka itu sepele untuk membuatnya sangat cepat.
tchrist
7
Setelah membaca ini saya juga menemukan bahwa utf8_unicode_ci akan mempertimbangkan karakter dengan bobot pemeriksaan yang sama dengan yang sama untuk keperluan perbandingan kesetaraan. Ini mengarah ke kasus di mana "か" == "が"atau "ǽ" == "æ". Untuk menyortir ini masuk akal tetapi bisa mengejutkan ketika memilih melalui persamaan atau berurusan dengan indeks unik - bugs.mysql.com/bug.php?id=16526
Mat Schaffer
4
@DanHorvat Satu-satunya alasan praktis untuk membatasi diri Anda pada bagian Unicode MySQL yang lebih lama dan lebih terbatas adalah jika Anda memiliki versi lama MySQL yang tidak mendukung utf8mb4 yang lebih lengkap. 5.5.3 berusia di atas 5 tahun. Saya menghargai bahwa Plesk berjalan pada jadwal MySQL yang berbeda, tetapi kebanyakan distro ada di MySQL 5.5 sekarang dan Plesk 11.x tidak mendukung MySQL 5.5 jika Anda memperbarui komponen-komponennya.
thomasrutter
22
Saya tidak setuju bahwa menggunakan varian yang lebih baru, lebih standar-keluhan adalah praktik yang buruk, dan saya pikir itu menjengkelkan untuk memanggil orang pengembang buruk atas sesuatu seperti ini. Anda mungkin juga ingin mencatat bahwa jawaban saya seperti kata " dalam versi baru dari MySQL menggunakan utf8mb4, daripada utf8", penekanan saya.
thomasrutter
24
@DanHorvat utf8mb4adalah satu-satunya pilihan yang benar . Dengan utf8Anda terjebak dalam beberapa MySQL-only, varian 3-byte UTF8 yang hanya MySQL (dan MariaDB) yang tahu apa yang harus dilakukan. Seluruh dunia menggunakan UTF8, yang dapat memuat hingga 4 byte per karakter . Para pengembang MySQL salah utf8menyebut penyandian homebrew mereka dan untuk tidak merusak kompatibilitas, mereka sekarang harus merujuk ke UTF8 yang asli utf8mb4.
Stijn de Witt
162

Saya ingin tahu apa perbedaan kinerja antara menggunakan utf8_general_cidan utf8_unicode_ci, tetapi saya tidak menemukan benchmark apa pun yang tercantum di internet, jadi saya memutuskan untuk membuat benchmark sendiri.

Saya membuat tabel yang sangat sederhana dengan 500.000 baris:

CREATE TABLE test(
  ID INT(11) DEFAULT NULL,
  Description VARCHAR(20) DEFAULT NULL
)
ENGINE = INNODB
CHARACTER SET utf8
COLLATE utf8_general_ci;

Lalu saya mengisinya dengan data acak dengan menjalankan prosedur tersimpan ini:

CREATE PROCEDURE randomizer()
BEGIN
  DECLARE i INT DEFAULT 0;
  DECLARE random CHAR(20) ;
  theloop: loop
    SET random = CONV(FLOOR(RAND() * 99999999999999), 20, 36);
    INSERT INTO test VALUES (i+1, random);
    SET i=i+1;
    IF i = 500000 THEN
      LEAVE theloop;
    END IF;
  END LOOP theloop;
END

Lalu saya membuat prosedur tersimpan berikut untuk membandingkan sederhana SELECT, SELECTdengan LIKE, dan menyortir ( SELECTdengan ORDER BY):

CREATE PROCEDURE benchmark_simple_select()
BEGIN
  DECLARE i INT DEFAULT 0;
  theloop: loop
    SELECT *
    FROM test
    WHERE Description = 'test' COLLATE utf8_general_ci;
    SET i = i + 1;
    IF i = 30 THEN
      LEAVE theloop;
    END IF;
  END LOOP theloop;
END;

CREATE PROCEDURE benchmark_select_like()
BEGIN
  DECLARE i INT DEFAULT 0;
  theloop: loop
    SELECT *
    FROM test
    WHERE Description LIKE '%test' COLLATE utf8_general_ci;
    SET i = i + 1;
    IF i = 30 THEN
      LEAVE theloop;
    END IF;
  END LOOP theloop;
END;

CREATE PROCEDURE benchmark_order_by()
BEGIN
  DECLARE i INT DEFAULT 0;
  theloop: loop
    SELECT *
    FROM test
    WHERE ID > FLOOR(1 + RAND() * (400000 - 1))
    ORDER BY Description COLLATE utf8_general_ci LIMIT 1000;
    SET i = i + 1;
    IF i = 10 THEN
      LEAVE theloop;
    END IF;
  END LOOP theloop;
END;

Dalam prosedur tersimpan utf8_general_cicollation digunakan, tetapi tentu saja selama tes saya menggunakan keduanya utf8_general_cidan utf8_unicode_ci.

Saya memanggil setiap prosedur tersimpan 5 kali untuk setiap pemeriksaan (5 kali untuk utf8_general_cidan 5 kali untuk utf8_unicode_ci) dan kemudian menghitung nilai rata-rata.

Hasil saya adalah:

benchmark_simple_select()

  • dengan utf8_general_ci: 9,957 ms
  • dengan utf8_unicode_ci: 10.271 ms

Dalam benchmark ini menggunakan utf8_unicode_cilebih lambat dibandingkan utf8_general_cidengan 3,2%.

benchmark_select_like()

  • dengan utf8_general_ci: 11.441 ms
  • dengan utf8_unicode_ci: 12.811 ms

Dalam benchmark ini menggunakan utf8_unicode_cilebih lambat dibandingkan utf8_general_cidengan 12%.

benchmark_order_by()

  • dengan utf8_general_ci: 11.944 ms
  • dengan utf8_unicode_ci: 12.887 ms

Dalam benchmark ini, penggunaan utf8_unicode_cilebih lambat dibandingkan utf8_general_cidengan 7,9%.

nightcoder
sumber
16
Tolok ukur yang bagus, terima kasih telah berbagi. Saya mendapatkan angka yang hampir sama (MySQL v5.6.12 di Windows): 10%, 4%, 8%. Saya setuju: perolehan kinerja utf8_general_citerlalu kecil untuk layak digunakan.
RandomSeed
10
1) Tetapi bukankah tolok ukur ini menghasilkan hasil yang serupa untuk kedua pemeriksaan menurut definisi? Maksud saya CONV(FLOOR(RAND() * 99999999999999), 20, 36)hanya menghasilkan ASCII, dan tidak ada karakter Unicode yang akan diproses oleh algoritma dari kumpulan. 2) Description = 'test' COLLATE ...dan Description LIKE 'test%' COLLATE ...hanya memproses string tunggal ("test") saat runtime, bukan? 3) Dalam aplikasi nyata, kolom yang digunakan dalam pemesanan mungkin akan diindeks, dan kecepatan pengindeksan pada berbagai koleksi dengan teks non-ASCII nyata mungkin berbeda.
Halil Özgür
2
@ HalilÖzgür - poin Anda sebagian salah. Saya kira ini bukan tentang nilai codepoint berada di luar ASCII (yang general_ci akan menangani dengan benar), tetapi tentang fitur spesifik, seperti memperlakukan umlaut yang ditulis sebagai "Uml ea ute" atau beberapa seluk-beluk seperti itu.
Tomasz Gandor
38

Posting ini menggambarkannya dengan sangat baik.

Singkatnya: utf8_unicode_ci menggunakan Algoritma Collation Unicode sebagaimana didefinisikan dalam standar Unicode, sedangkan utf8_general_ci adalah urutan penyortiran yang lebih sederhana yang menghasilkan hasil penyortiran "kurang akurat".

Michael Madsen
sumber
1
Terima kasih. itulah kesan saya. Saya akan mengambil hit kinerja :)
onassar
7
Jika Anda tidak peduli tentang kebenaran, maka itu sepele untuk membuat algoritma apa pun dengan sangat cepat. Gunakan utf8_unicode_cidan pura-pura yang lain tidak ada.
tchrist
1
@tchrist tetapi jika Anda peduli tentang keseimbangan antara kebenaran dan kecepatan, utf8_general_cimungkin untuk Anda
Shelvacu
@tchrist Tidak pernah menjadi programmer game;)
Stijn de Witt
1
@onassar - MySQL 8.0 mengklaim telah secara signifikan meningkatkan kinerja semua pemeriksaan.
Rick James
9

Lihat manual mysql, bagian Set Karakter Unicode :

Untuk setiap set karakter Unicode, operasi yang dilakukan menggunakan collation _general_ci lebih cepat daripada yang untuk collation _unicode_ci. Sebagai contoh, perbandingan untuk collation utf8_general_ci lebih cepat, tetapi sedikit kurang benar, dibandingkan dengan perbandingan utf8_unicode_ci. Alasannya adalah utf8_unicode_ci mendukung pemetaan seperti ekspansi; yaitu, ketika satu karakter membandingkan sama dengan kombinasi karakter lain. Misalnya, dalam bahasa Jerman dan beberapa bahasa lainnya "ß" sama dengan "ss". utf8_unicode_ci juga mendukung karakter kontraksi dan diabaikan. utf8_general_ci adalah kumpulan warisan yang tidak mendukung ekspansi, kontraksi, atau karakter yang dapat diabaikan. Itu hanya dapat membuat perbandingan satu-ke-satu antara karakter.

Jadi untuk meringkas, utf_general_ci menggunakan set perbandingan yang lebih kecil dan kurang benar (sesuai dengan standar) daripada utf_unicode_ci yang harus mengimplementasikan seluruh standar. Set general_ci akan lebih cepat karena ada sedikit komputasi yang harus dilakukan.

Dana si Sane
sumber
18
Tidak ada yang namanya "sedikit kurang benar". Kebenaran adalah karakteristik boolean; itu tidak mengakui pengubah derajat. Hanya menggunakan utf8_unicode_cidan berpura-pura versi kereta yang rusak tidak ada.
tchrist
2
Saya punya masalah mendapatkan 5.6.15 untuk mengambil pengaturan collation_connection, dan ternyata Anda harus melewatinya di baris SET seperti 'SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci'. Penghargaan diberikan kepada Mathias Bynens untuk solusinya, inilah panduannya yang sangat berguna: mathiasbynens.be/notes/mysql-utf8mb4
Steve Hibbert
4
@tchrist Masalah dengan mengatakan kebenaran adalah boolean karena tidak memperhitungkan situasi akun yang tidak bergantung pada kebenaran mutlak. Poin yang mendasari Anda tidak valid atau saya mencoba untuk mendukung manfaat dari general_ci, tetapi pernyataan umum Anda tentang kebenaran dengan mudah terbukti tidak benar. Saya melakukannya setiap hari dalam profesi saya. Di samping komedi, Stuart punya poin bagus di sini .
Anthony
5
Dengan geolokasi atau pengembangan game, kami menukar kebenaran dengan kinerja setiap saat. Dan tentu saja kebenaran adalah bilangan real antara 0dan 1, bukan bool. :) EG memilih titik geo dalam kotak pembatas adalah perkiraan 'titik terdekat' yang tidak sebaik menghitung jarak antara titik dan titik referensi dan memfilternya. Tetapi keduanya merupakan perkiraan dan pada kenyataannya, kebenaran lengkap sebagian besar tidak dapat dicapai. Lihat paradoks garis pantai dan IEEE 754
Stijn de Witt
4
TL; DR : Tolong berikan program yang mencetak hasil yang benar untuk1/3
Stijn de Witt
7

Secara singkat:

Jika Anda memerlukan urutan penyortiran yang lebih baik - gunakan utf8_unicode_ci(ini adalah metode yang disukai),

tetapi jika Anda benar-benar tertarik pada kinerja - gunakan utf8_general_ci, tetapi ketahuilah bahwa itu sedikit ketinggalan jaman.

Perbedaan dalam hal kinerja sangat kecil.

simhumileco
sumber
1
Keduanya sudah ketinggalan zaman sekarang - lihat jawaban yang diterima untuk lebih
thomasrutter
OK, terima kasih @thomasrutter
simhumileco
6

Beberapa detail (PL)

Seperti yang dapat kita baca di sini ( Peter Gulutzan ) ada perbedaan dalam menyortir / membandingkan huruf polish "Ł" (L dengan stroke - html esc:) Ł(huruf kecil: "ł" - html esc:) ł- kami memiliki asumsi berikut:

utf8_polish_ci      Ł greater than L and less than M
utf8_unicode_ci     Ł greater than L and less than M
utf8_unicode_520_ci Ł equal to L
utf8_general_ci     Ł greater than Z

Dalam bahasa Polandia, huruf Ładalah setelah huruf Ldan sebelumnya M. Tidak ada satu pun dari pengkodean ini yang lebih baik atau lebih buruk - itu tergantung kebutuhan Anda.

Kamil Kiełczewski
sumber
1

Ada dua perbedaan besar antara penyortiran dan pencocokan karakter:

Menyortir :

  • utf8mb4_general_ci menghapus semua aksen dan pengurutan satu per satu yang dapat membuat hasil pengurutan yang salah.
  • utf8mb4_unicode_ci macam akurat.

Pencocokan Karakter

Mereka mencocokkan karakter secara berbeda.

Misalnya, di utf8mb4_unicode_ciAnda miliki i != ı, tetapi di utf8mb4_general_cidalamnya berlaku ı=i.

Misalnya, bayangkan Anda memiliki pertengkaran name="Yılmaz". Kemudian

select id from users where name='Yilmaz';

akan mengembalikan baris jika kolokasi adalah utf8mb4_general_ci, tetapi jika collocated dengan utf8mb4_unicode_ciitu tidak akan mengembalikan baris!

Di sisi lain kita memiliki a=ªdan ß=ssdi utf8mb4_unicode_cimana tidak terjadi di utf8mb4_general_ci. Jadi bayangkan Anda memiliki pertengkaran name="ªßi", lalu

select id from users where name='assi';

akan mengembalikan baris jika kolokasi utf8mb4_unicode_ci, tetapi tidak akan mengembalikan baris jika kolokasi diatur ke utf8mb4_general_ci.

Daftar lengkap kecocokan untuk setiap kolokasi dapat ditemukan di sini .

Adam
sumber