Was ist der Unterschied zwischen utf8_general_ci und utf8_unicode_ci?


1063

Gibt es zwischen utf8_general_ciund utf8_unicode_ciLeistungsunterschiede?



6
Wenn Sie möchten utf8[mb4]_unicode_ci, Sie können gerne utf8[mb4]_unicode_520_cinoch mehr.
Rick James

8
Ich weiß nicht, wie ich das finde - anstatt ihre Implementierung so zu korrigieren, dass sie dem neuesten Unicode-Standard entspricht, behalten sie die veraltete Version als Standard bei, und die Leute müssen "520" hinzufügen, um jetzt die richtige zu verwenden. Und es ist nicht vorwärts und rückwärts kompatibel, da Sie die "520" -Version nicht auf älteren MySQL-Versionen verwenden können. Warum konnten sie ihre vorhandene Sortierung nicht einfach aktualisieren? Das gleiche gilt für "mb4". Welcher Code hing wirklich vom alten, eingeschränkten / veralteten Verhalten ab, um zu rechtfertigen, dass dies als Standard beibehalten wurde?
Thomasrutter

7
Noch besser ist die Standardeinstellung von 8.0 utf8mb4_0900_ai_ci.
Rick James

Antworten:


1591

Diese beiden Kollatierungen gelten beide für die UTF-8-Zeichencodierung. Die Unterschiede bestehen darin, wie Text sortiert und verglichen wird.

Hinweis: In MySQL müssen Sie utf8mb4eher als verwenden utf8. Verwirrenderweise utf8handelt es sich um eine fehlerhafte UTF-8-Implementierung aus frühen MySQL-Versionen, die nur aus Gründen der Abwärtskompatibilität erhalten bleibt. Die feste Version erhielt den Namen utf8mb4.

Hinweis: Neuere Versionen von MySQL haben die Unicode-Sortierregeln aktualisiert, die unter Namen verfügbar sind, z. B. utf8mb4_0900_ai_ci für äquivalente Regeln, die auf Unicode 9.0 basieren - und ohne äquivalente _general Variante. Leute, die dies jetzt lesen, sollten wahrscheinlich eine dieser neueren Kollatierungen anstelle von entweder _unicode oder verwenden _general . Vieles von dem, was unten geschrieben steht, ist nicht mehr von großem Interesse, wenn Sie stattdessen eine der neueren Kollatierungen verwenden können.

Hauptunterschiede

  • utf8mb4_unicode_ci basiert auf den offiziellen Unicode-Regeln für das universelle Sortieren und Vergleichen, die in einer Vielzahl von Sprachen genau sortiert werden.

  • utf8mb4_general_ciist ein vereinfachter Satz von Sortierregeln, der so gut wie möglich funktioniert und gleichzeitig viele Abkürzungen zur Verbesserung der Geschwindigkeit verwendet. Es folgt nicht den Unicode-Regeln und führt in einigen Situationen zu unerwünschten Sortierungen oder Vergleichen, z. B. bei der Verwendung bestimmter Sprachen oder Zeichen.

    Auf modernen Servern ist diese Leistungssteigerung nahezu vernachlässigbar. Es wurde in einer Zeit entwickelt, in der Server einen winzigen Bruchteil der CPU-Leistung heutiger Computer hatten.

Vorteile von utf8mb4_unicode_ciüberutf8mb4_general_ci

utf8mb4_unicode_ci, das die Unicode-Regeln zum Sortieren und Vergleichen verwendet, verwendet einen ziemlich komplexen Algorithmus zum korrekten Sortieren in einer Vielzahl von Sprachen und bei Verwendung einer Vielzahl von Sonderzeichen. Diese Regeln müssen sprachspezifische Konventionen berücksichtigen. Nicht jeder sortiert seine Zeichen in der von uns als "alphabetisch" bezeichneten Reihenfolge.

In Bezug auf lateinische (dh "europäische") Sprachen gibt es keinen großen Unterschied zwischen der Unicode-Sortierung und der vereinfachten utf8mb4_general_ciSortierung in MySQL, aber es gibt immer noch einige Unterschiede:

  • Beispielsweise sortiert die Unicode-Kollatierung "ß" wie "ss" und "Œ" wie "OE", wie Personen, die diese Zeichen verwenden, normalerweise möchten, während utf8mb4_general_cisie als einzelne Zeichen sortiert werden (vermutlich wie "s" bzw. "e"). .

  • Einige Unicode-Zeichen werden als ignorierbar definiert. Dies bedeutet, dass sie nicht für die Sortierreihenfolge berücksichtigt werden sollten und der Vergleich stattdessen zum nächsten Zeichen übergehen sollte. utf8mb4_unicode_cibehandelt diese richtig.

In nicht-lateinischen Sprachen wie asiatischen Sprachen oder Sprachen mit unterschiedlichen Alphabeten kann es viel größere Unterschiede zwischen der Unicode-Sortierung und der vereinfachten utf8mb4_general_ciSortierung geben. Die Eignung von utf8mb4_general_ciwird stark von der verwendeten Sprache abhängen. Für einige Sprachen ist dies völlig unzureichend.

Was solltest du verwenden?

Es gibt mit ziemlicher Sicherheit keinen Grund utf8mb4_general_cimehr, diese zu verwenden , da wir den Punkt hinter uns gelassen haben, an dem die CPU-Geschwindigkeit so niedrig ist, dass der Leistungsunterschied wichtig wäre. Ihre Datenbank wird mit ziemlicher Sicherheit durch andere Engpässe als diese eingeschränkt.

In der Vergangenheit wurde von einigen Personen empfohlen, diese zu verwenden, es sei utf8mb4_general_cidenn, eine genaue Sortierung wäre wichtig genug, um die Leistungskosten zu rechtfertigen. Heute sind diese Leistungskosten so gut wie verschwunden, und Entwickler behandeln die Internationalisierung ernsthafter.

Es muss argumentiert werden, dass Sie, wenn Geschwindigkeit für Sie wichtiger ist als Genauigkeit, auch überhaupt keine Sortierung durchführen dürfen. Es ist trivial, einen Algorithmus schneller zu machen, wenn Sie ihn nicht benötigen, um genau zu sein. Es handelt sich also utf8mb4_general_cium einen Kompromiss, der aus Geschwindigkeitsgründen wahrscheinlich nicht benötigt wird und aus Genauigkeitsgründen wahrscheinlich auch nicht geeignet ist.

Eine andere Sache, die ich hinzufügen möchte, ist, dass Ihre Anwendung, selbst wenn Sie wissen, dass sie nur die englische Sprache unterstützt, möglicherweise immer noch mit den Namen von Personen umgehen muss, die häufig Zeichen enthalten, die in anderen Sprachen verwendet werden, in denen es genauso wichtig ist, richtig zu sortieren . Die Verwendung der Unicode-Regeln für alles trägt dazu bei, dass die sehr intelligenten Unicode-Mitarbeiter sehr hart daran gearbeitet haben, dass das Sortieren ordnungsgemäß funktioniert.

Was die Teile bedeuten

Erstens cidient das Sortieren und Vergleichen ohne Berücksichtigung der Groß- und Kleinschreibung . Dies bedeutet, dass es für Textdaten geeignet ist und die Groß- und Kleinschreibung nicht wichtig ist. Die anderen Arten der Sortierung sind cs(Groß- und Kleinschreibung beachten) für Textdaten, bei denen die Groß- und Kleinschreibung wichtig ist, und binfür die, bei denen die Codierung übereinstimmen muss, Bit für Bit, was für Felder geeignet ist, bei denen es sich tatsächlich um codierte Binärdaten handelt (einschließlich beispielsweise Base64). Die Sortierung nach Groß- und Kleinschreibung führt zu seltsamen Ergebnissen, und der Vergleich zwischen Groß- und Kleinschreibung kann dazu führen, dass doppelte Werte nur in Groß- und Kleinschreibung voneinander abweichen. Daher werden Sortierungen, bei denen zwischen Groß- und Kleinschreibung unterschieden wird, für Textdaten in Ungnade fallen und so weiter ist wahrscheinlich auch von Bedeutung, und eine binäre Kollatierung könnte geeigneter sein.

Weiter unicodeoder generalbezieht sich auf die spezifischen Sortier- und Vergleichsregeln - insbesondere auf die Art und Weise, wie Text normalisiert oder verglichen wird. Es gibt viele verschiedene Arten von Regeln für die utf8mb4 Zeichencodierung, mit unicodeund generalsind zwei dieser Versuch zu funktionieren gut in allen möglichen Sprachen statt einem spezifischen. Die Unterschiede zwischen diesen beiden Regelwerken sind Gegenstand dieser Antwort. Beachten Sie, dass unicodeRegeln aus Unicode 4.0 verwendet werden. Neuere Versionen von MySQL fügen die Regelsätze unicode_520mithilfe von Regeln aus Unicode 5.2 und 0900(Löschen des Teils "unicode_") mithilfe von Regeln aus Unicode 9.0 hinzu.

Und schließlich utf8mb4wird natürlich die Zeichencodierung intern verwendet. In dieser Antwort spreche ich nur von Unicode-basierten Codierungen.


218
@KahWeeTeng Sie sollten nie, nie verwenden utf8_general_ci: es funktioniert einfach nicht. Es ist ein Rückfall in die schlechten alten Zeiten der ASCII-Stooopeeedität vor fünfzig Jahren. Unicode-Matching ohne Berücksichtigung der Groß- und Kleinschreibung kann nicht ohne die Foldcase-Map von der UCD durchgeführt werden. Zum Beispiel hat "Σίσυφος" drei verschiedene Sigmen; oder wie der Kleinbuchstabe von "TSCHüẞ" "tschüβ" ist, aber der Großbuchstabe von "tschüβ" ist "TSCHÜSS". Sie können Recht haben oder Sie können schnell sein. Daher müssen Sie verwenden utf8_unicode_ci, denn wenn Sie sich nicht um Korrektheit kümmern, ist es trivial, es unendlich schnell zu machen.
Tchrist

7
Nachdem ich dies gelesen hatte, stellte ich auch fest, dass utf8_unicode_ci alle Zeichen mit dem gleichen Kollatierungsgewicht zum Zwecke des Gleichheitsvergleichs als gleich betrachtet. Dies führt zu Fällen, in denen "か" == "が"oder "ǽ" == "æ". Für die Sortierung ist dies sinnvoll, könnte aber überraschend sein, wenn Sie über Gleichungen auswählen oder mit eindeutigen Indizes arbeiten - bugs.mysql.com/bug.php?id=16526
Mat Schaffer

4
@DanHorvat Der einzige praktische Grund, sich auf die ältere, eingeschränktere Teilmenge von Unicode von MySQL zu beschränken, ist, wenn Sie eine alte Version von MySQL haben, die das vollständigere utf8mb4 nicht unterstützt. 5.5.3 ist über 5 Jahre alt. Ich schätze , dass Plesk auf einem anderen MySQL Zeitplan läuft, aber die meisten Distributionen sind auf MySQL 5.5 jetzt und Plesk 11.x tut Unterstützung MySQL 5.5 , wenn Sie seine Komponenten aktualisieren.
Thomasrutter

22
Ich würde nicht zustimmen, dass die Verwendung der neueren Variante mit mehr Standardbeschwerden eine schlechte Praxis ist, und ich denke, es ist entzündlich, Leute wegen so etwas als schlechte Entwickler zu bezeichnen. Vielleicht möchten Sie auch beachten, dass meine Antwort in der jetzigen Form lautet: " Verwenden Sie in neuen Versionen von MySQL utf8mb4 anstelle von utf8".
Thomasrutter

24
@ DanHorvat utf8mb4ist die einzig richtige Wahl . Mit utf8Ihnen in einiger MySQL-only feststecken, 3-Byte - Variante von UTF - 8 , dass nur MySQL (und MariaDB) wissen , was mit zu tun. Der Rest der Welt verwendet UTF8, das bis zu 4 Bytes pro Zeichen enthalten kann . Die MySQL-Entwickler haben ihre Homebrew-Codierung falsch benannt. Um die utf8Abwärtskompatibilität nicht zu beeinträchtigen , müssen sie sich jetzt auf die echte UTF8 als beziehen utf8mb4.
Stijn de Witt

162

Ich wollte wissen, was der Leistungsunterschied zwischen der Verwendung von utf8_general_ciund utf8_unicode_ciist, fand jedoch keine im Internet aufgelisteten Benchmarks und habe mich daher entschlossen, selbst Benchmarks zu erstellen.

Ich habe eine sehr einfache Tabelle mit 500.000 Zeilen erstellt:

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

Dann habe ich es mit zufälligen Daten gefüllt, indem ich diese gespeicherte Prozedur ausgeführt habe:

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

Dann habe ich die folgenden gespeicherten Prozeduren erstellt, um einfach SELECT, SELECTmit LIKEund sortieren ( SELECTmit ORDER BY) zu vergleichen:

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;

In den oben gespeicherten Prozeduren wird die utf8_general_ciKollatierung verwendet, aber natürlich habe ich während der Tests sowohl utf8_general_cials auch verwendet utf8_unicode_ci.

Ich habe jede gespeicherte Prozedur 5 Mal für jede Kollatierung aufgerufen (5 Mal für utf8_general_ciund 5 Mal für utf8_unicode_ci) und dann die Durchschnittswerte berechnet.

Meine Ergebnisse sind:

benchmark_simple_select()

  • mit utf8_general_ci: 9.957 ms
  • mit utf8_unicode_ci: 10.271 ms

In dieser Benchmark ist die Verwendung utf8_unicode_cilangsamer als utf8_general_cium 3,2%.

benchmark_select_like()

  • mit utf8_general_ci: 11.441 ms
  • mit utf8_unicode_ci: 12.811 ms

In dieser Benchmark ist die Verwendung utf8_unicode_cilangsamer als utf8_general_cium 12%.

benchmark_order_by()

  • mit utf8_general_ci: 11.944 ms
  • mit utf8_unicode_ci: 12.887 ms

In dieser Benchmark ist die Verwendung utf8_unicode_cilangsamer als utf8_general_cium 7,9%.


16
Netter Benchmark, danke fürs Teilen. Ich bekomme vernünftigerweise ähnliche Zahlen (MySQL v5.6.12 unter Windows): 10%, 4%, 8%. Ich stimme zu: Der Leistungsgewinn von utf8_general_ciist einfach zu gering, um ihn zu nutzen.
RandomSeed

10
1) Aber sollte dieser Benchmark nicht per Definition ähnliche Ergebnisse für die beiden Kollatierungen liefern? Ich meine, CONV(FLOOR(RAND() * 99999999999999), 20, 36)generiert nur ASCII und keine Unicode-Zeichen, die von den Algorithmen der Kollatierungen verarbeitet werden sollen. 2) Description = 'test' COLLATE ...und verarbeite Description LIKE 'test%' COLLATE ...nur einen einzigen String ("Test") zur Laufzeit, nicht wahr? 3) In realen Apps werden die bei der Bestellung verwendeten Spalten wahrscheinlich indiziert, und die Indizierungsgeschwindigkeit für verschiedene Kollatierungen mit echtem Nicht-ASCII-Text kann unterschiedlich sein.
Halil Özgür

2
@ HalilÖzgür - dein Punkt ist teilweise falsch. Ich denke, es geht nicht darum, dass der Codepunktwert außerhalb von ASCII liegt (was general_ci korrekt handhaben würde), sondern um bestimmte Funktionen, wie die Behandlung von Umlauten, die als "Uml ea ute" geschrieben wurden, oder einige solche Feinheiten.
Tomasz Gandor

38

Dieser Beitrag beschreibt es sehr schön.

Kurz gesagt: utf8_unicode_ci verwendet den in den Unicode-Standards definierten Unicode-Kollatierungsalgorithmus, während utf8_general_ci eine einfachere Sortierreihenfolge ist, die zu "weniger genauen" Sortierergebnissen führt.


1
Vielen Dank. Das war mein Eindruck. Ich werde den Performance-Hit nehmen :)
Onassar

7
Wenn Sie sich nicht um Korrektheit kümmern, ist es trivial, einen Algorithmus unendlich schnell zu machen. Verwenden Sie einfach utf8_unicode_ciund tun Sie so, als ob der andere nicht existiert.
Tchrist

1
@tchrist aber wenn Sie sich für ein gewisses Gleichgewicht zwischen Korrektheit und Geschwindigkeit utf8_general_ci
interessieren

@tchrist Werden Sie nie ein Spielprogrammierer;)
Stijn de Witt

1
@onassar - MySQL 8.0 soll die Leistung aller Kollatierungen erheblich verbessert haben .
Rick James

9

Siehe das MySQL-Handbuch, Abschnitt Unicode-Zeichensätze :

Für jeden Unicode-Zeichensatz sind Operationen, die mit der _general_ci-Kollatierung ausgeführt werden, schneller als die für die _unicode_ci-Kollatierung. Beispielsweise sind Vergleiche für die Kollatierung utf8_general_ci schneller, aber etwas weniger korrekt als Vergleiche für utf8_unicode_ci. Der Grund dafür ist, dass utf8_unicode_ci Zuordnungen wie Erweiterungen unterstützt. Das heißt, wenn ein Zeichen mit Kombinationen anderer Zeichen verglichen wird. In Deutsch und einigen anderen Sprachen ist „ß“ beispielsweise gleich „ss“. utf8_unicode_ci unterstützt auch Kontraktionen und ignorierbare Zeichen. utf8_general_ci ist eine Legacy-Sortierung, die keine Erweiterungen, Kontraktionen oder ignorierbaren Zeichen unterstützt. Es können nur Eins-zu-Eins-Vergleiche zwischen Zeichen durchgeführt werden.

Zusammenfassend verwendet utf_general_ci einen kleineren und weniger korrekten (gemäß dem Standard) Satz von Vergleichen als utf_unicode_ci, der den gesamten Standard implementieren sollte . Der Satz general_ci ist schneller, da weniger Berechnungen erforderlich sind.


18
Es gibt kein "etwas weniger korrektes". Korrektheit ist ein boolesches Merkmal; Es werden keine Gradmodifikatoren zugelassen. Verwenden Sie einfach utf8_unicode_ciund tun Sie so, als ob die fehlerhafte Version des Buggys nicht existiert.
Tchrist

2
Ich hatte Probleme, 5.6.15 zu bekommen, um die Einstellung collation_connection zu übernehmen, und es stellte sich heraus, dass Sie sie in der SET-Zeile wie 'SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci' übergeben müssen. Dank geht an Mathias Bynens für die Lösung, hier ist sein sehr nützlicher Leitfaden: mathiasbynens.be/notes/mysql-utf8mb4
Steve Hibbert

4
@tchrist Das Problem mit der Aussage, dass Korrektheit boolesch ist, besteht darin, dass Situationen, die nicht auf absoluter Korrektheit beruhen, nicht berücksichtigt werden. Ihr zugrunde liegender Punkt ist nicht ungültig und ich versuche auch nicht, die Vorteile von general_ci zu vertreten, aber Ihre allgemeine Aussage zur Korrektheit ist leicht zu widerlegen. Ich mache es täglich in meinem Beruf. Komödie beiseite, hat Stuart einen guten Punkt hier .
Anthony

5
Bei der Geolokalisierung oder Spieleentwicklung tauschen wir ständig Korrektheit mit Leistung aus. Und natürlich ist Korrektheit eine reelle Zahl zwischen 0und 1kein Bool. :) Das Auswählen von Geopunkten in einem Begrenzungsrahmen ist eine Annäherung an "Punkte in der Nähe", die nicht so gut ist wie das Berechnen des Abstands zwischen dem Punkt und dem Referenzpunkt und das Filtern danach. Aber beide sind eine Annäherung und in der Tat vollständige Richtigkeit ist meist nicht erreichbar. Siehe das Küstenparadoxon und IEEE 754
Stijn de Witt

4
TL; DR : Bitte geben Sie ein Programm an, das das richtige Ergebnis für1/3
Stijn de Witt

7

In kurzen Worten:

Wenn Sie eine bessere Sortierreihenfolge benötigen, verwenden Sie utf8_unicode_ci(dies ist die bevorzugte Methode),

aber wenn Sie ganz an Leistung interessiert sind - verwenden Sie utf8_general_ci, aber wissen Sie, dass es ein wenig veraltet ist.

Die Leistungsunterschiede sind sehr gering.


1
Beide sind jetzt veraltet - siehe akzeptierte Antwort für mehr
thomasrutter

OK, danke @thomasrutter
simhumileco

6

Einige Details (PL)

Wie wir hier lesen können ( Peter Gulutzan ), gibt es Unterschiede beim Sortieren / Vergleichen des polnischen Buchstabens "Ł" (L mit Strich - html esc :) Ł(Kleinbuchstaben: "ł" - html esc :) ł- wir haben folgende Annahme:

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

In polnischer Sprache steht der Buchstabe Łnach dem Buchstaben Lund davor M. Keine dieser Kodierungen ist besser oder schlechter - es hängt von Ihren Bedürfnissen ab.


1

Es gibt zwei große Unterschiede bei der Sortierung und der Zeichenübereinstimmung:

Sortierung :

  • utf8mb4_general_ci Entfernt alle Akzente und sortiert nacheinander, was zu falschen Sortierergebnissen führen kann.
  • utf8mb4_unicode_ci sortiert genau.

Zeichenübereinstimmung

Sie stimmen mit Charakteren unterschiedlich überein.

Zum Beispiel in utf8mb4_unicode_cidir i != ı, aber utf8mb4_general_cidarin gilt ı=i.

Stellen Sie sich zum Beispiel vor, Sie haben eine Reihe mit name="Yılmaz". Dann

select id from users where name='Yilmaz';

würde die Zeile zurückgeben, wenn Kollokation ist utf8mb4_general_ci, aber wenn es mit kollokiert ist , utf8mb4_unicode_ciwürde die Zeile nicht zurückgeben!

Auf der anderen Seite haben wir das a=ªund ß=ssin utf8mb4_unicode_cidem ist das nicht der Fall utf8mb4_general_ci. So stellen Sie sich mit einer Reihe haben name="ªßi", dann

select id from users where name='assi';

würde die Zeile zurückgeben, wenn die Kollokation ist utf8mb4_unicode_ci, würde aber keine Zeile zurückgeben, wenn die Kollokation auf gesetzt ist utf8mb4_general_ci.

Eine vollständige Liste der Übereinstimmungen für jede Kollokation finden Sie hier .


Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.