web-dev-qa-db-ja.com

Utf8_general_ciとutf8_unicode_ciの違いは何ですか

utf8_general_ciutf8_unicode_ciの間で、パフォーマンスに関して何か違いはありますか?

924
KahWee Teng

これらの2つの照合は、両方ともUTF-8文字エンコード用です。違いは、テキストのソート方法と比較方法です。

注:MySQL 5.5.3以降では、utf8mb4ではなく utf8 を使用する必要があります。どちらもUTF-8エンコーディングを参照しますが、古いutf8には、MySQL固有の制限があり、0xFFFDより大きい番号の文字を使用できません。

重要な違い

  • utf8mb4_unicode_ciは、広範囲の言語で正確にソートする、ユニバーサルソートと比較のための公式Unicodeルールに基づいています。

  • utf8mb4_general_ciは、速度を向上させるために設計された多くのショートカットを使用しながら、できる限り実行することを目的とした単純化された並べ替えルールのセットです。 Unicodeルールに従っていないため、特定の言語や文字を使用する場合など、状況によっては望ましくない並べ替えや比較が行われます。

    最新のサーバーでは、このパフォーマンスの向上はほとんど無視できます。これは、サーバーが今日のコンピューターのCPUパフォーマンスのごく一部を占める時代に考案されました。

注:現在、utf8mb4_unicode_ciと呼ばれるutf8mb4_0900_ai_ciの更新バージョンが存在します-これはUnicodeバージョン9.0の変更に基づいており、明らかに高速です。 0900はUnicodeバージョンであり、aiはアクセントを区別しないことを意味する新しい命名スキームを採用します-前のutf8mb4_unicode_ciと同様に、文字のアクセントは重要ではありません。

utf8mb4_unicode_ciutf8mb4_general_ciの利点

utf8mb4_unicode_ciは、Unicodeルールを使用して並べ替えと比較を行いますが、さまざまな言語で正しく並べ替えるために、またさまざまな特殊文字を使用するために、かなり複雑なアルゴリズムを採用しています。これらの規則では、言語固有の規則を考慮する必要があります。すべての人が「アルファベット順」と呼ぶものでキャラクターを並べ替えるわけではありません。

ラテン語(つまり「ヨーロッパ」)言語に関しては、Unicodeの並べ替えとMySQLの簡略化されたutf8mb4_general_ci並べ替えには大きな違いはありませんが、いくつかの違いがあります。

  • たとえば、Unicode照合は、「ß」を「ss」のようにソートし、「Œ」を「OE」のようにそれらの文字を使用するユーザーが通常望むようにソートしますが、utf8mb4_general_ciはそれらを単一の文字としてソートします(おそらく「s」および「e」のように) )。

  • 一部のUnicode文字は無視可能と定義されています。つまり、並べ替え順序にカウントされるべきではなく、比較は次の文字に移動する必要があります。 utf8mb4_unicode_ciはこれらを適切に処理します。

アジア言語やアルファベットの異なる言語などの非ラテン言語では、Unicodeソートと簡略化されたutf8mb4_general_ciソートの間に多くのmore違いがある場合があります。 utf8mb4_general_ciの適合性は、使用する言語に大きく依存します。一部の言語では、まったく不十分です。

何を使うべきですか?

CPUの速度がパフォーマンスの違いが重要になるほど十分に遅いという点を残しているので、utf8mb4_general_ciを使用する理由はほぼ確実にありません。データベースは、これ以外のボトルネックによってほぼ確実に制限されます。

過去には、正確なソートがパフォーマンスコストを正当化するのに十分重要である場合を除いて、utf8mb4_general_ciを使用することを推奨していました。今日、そのパフォーマンスコストはほとんどなくなり、開発者は国際化をより真剣に扱っています。

正確さよりも速度が重要な場合は、ソートをまったく行わないこともできるという議論があります。正確である必要がない場合、アルゴリズムを高速化するのは簡単です。したがって、utf8mb4_general_ciは妥協であり、おそらく速度上の理由から必要ではなく、おそらく精度上の理由からも適切ではありません。

私が追加するもう1つのことは、アプリケーションが英語のみをサポートしていることを知っていても、正しくソートすることが重要である他の言語で使用される文字を含むことが多い人の名前に対処する必要がある場合があることです。すべてにUnicodeルールを使用すると、非常に賢いUnicodeの人々がソートを適切に機能させるために一生懸命働いているという安心感が得られます。

各部の意味

まず、ciは、大文字と小文字を区別しないソートと比較に使用します。これは、テキストデータに適していることを意味し、大文字と小文字は重要ではありません。他のタイプの照合は、大文字と小文字が重要なテキストデータの場合はcs(大文字と小文字が区別されます)、エンコードが一致する必要がある場合はbin、ビットごとです。実際にエンコードされたバイナリデータ(Base64など)。大文字と小文字を区別する並べ替えは奇妙な結果をもたらし、大文字と小文字を区別する比較では大文字と小文字が異なる重複値が発生する可能性があるため、大文字と小文字を区別する照合はテキストデータでは優先されません-大文字と小文字が区別される場合は無視できる句読点などもおそらく重要であり、バイナリ照合がより適切である可能性があります。

次に、unicodeまたはgeneralは、特定の並べ替えおよび比較ルール、特にテキストの正規化または比較の方法を示します。 utf8mb4文字エンコーディングには多くの異なるルールセットがあり、unicodegeneralは2つであり、1つの特定の言語ではなく、可能なすべての言語でうまく機能しようとします。この2つのルールセットの違いが、この回答の主題です。新しいルールセットには、Unicode 9.0を指す0900とUnicode 5.2を指すunicode_520が含まれていることに注意してください。

そして最後に、utf8mb4はもちろん内部的に使用される文字エンコーディングです。この回答では、Unicodeベースのエンコーディングについてのみ説明しています。

1477
thomasrutter

utf8_general_ciutf8_unicode_ciを使用した場合のパフォーマンスの違いを知りたいのですが、インターネットにベンチマークが表示されていないため、自分でベンチマークを作成することにしました。

500,000行の非常に単純なテーブルを作成しました。

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

それから私はこのストアドプロシージャを実行することによってランダムなデータでそれを埋めました:

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

次に、以下のストアドプロシージャを作成して、単純なSELECTSELECTLIKE、および並べ替え(SELECTORDER 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;

上記のストアドプロシージャではutf8_general_ci照合が使用されていますが、もちろんテスト中はutf8_general_ciutf8_unicode_ciの両方を使用しました。

照合ごとに各ストアドプロシージャを5回(utf8_general_ciに5回、utf8_unicode_ciに5回)呼び出してから、平均値を計算しました。

私の結果は次のとおりです。

benchmark_simple_select()

  • utf8_general_ciの場合:9,957 ms
  • utf8_unicode_ci付き:10,271ミリ秒

このベンチマークでは、utf8_unicode_ciを使用するほうがutf8_general_ciより3.2%遅くなります。

benchmark_select_like()

  • utf8_general_ciの場合:11,441 ms
  • utf8_unicode_ciの場合:12,811 ms

このベンチマークでは、utf8_unicode_ciを使用した方がutf8_general_ciより12%遅くなります。

benchmark_order_by()

  • utf8_general_ciの場合:11,944 ms
  • utf8_unicode_ciの場合:12,887 ms

このベンチマークでは、utf8_unicode_ciを使用するほうがutf8_general_ciより7.9%遅くなります。

145
nightcoder

この記事 はそれを非常にうまく説明しています。

つまり、utf8_unicode_ciはUnicode標準で定義されているUnicode照合アルゴリズムを使用しますが、utf8_general_ciはより単純なソート順であるため、「正確性が低い」ソート結果になります。

36
Michael Madsen

Mysqlマニュアルの nicode Character Sets セクションを参照してください:

Unicode文字セットの場合、_general_ci照合を使用して実行される操作は、_unicode_ci照合の操作よりも高速です。たとえば、utf8_general_ci照合順序の比較は、utf8_unicode_ciの比較よりも高速ですが、わずかに正しくありません。これは、utf8_unicode_ciが展開などのマッピングをサポートしているためです。つまり、1つの文字が他の文字の組み合わせと等しいと比較される場合。たとえば、ドイツ語および他のいくつかの言語では、「ß」は「ss」と同じです。 utf8_unicode_ciは、収縮と無視可能な文字もサポートします。 utf8_general_ciは、拡張、縮小、または無視可能な文字をサポートしないレガシー照合です。文字間で1対1の比較のみを行うことができます。

要約すると、utf_general_ciは、shouldが標準全体を実装するutf_unicode_ciよりも小さく(標準に従って)正しい比較セットを使用します。 general_ciセットは、実行する計算が少ないため、高速になります。

8
Dana the Sane

簡単に言うと:

より良いソート順が必要な場合 - utf8_unicode_ciを使います(これが好ましい方法です)、

しかし、あなたが本当にパフォーマンスに興味があるなら - utf8_general_ciを使ってください、しかしそれが少し時代遅れであることを知っていてください。

パフォーマンスの面での違いは非常にわずかです。

6
simhumileco

いくつかの詳細(PL)

here Peter Gulutzan )を読むことができるので、ポーランド文字 "Ł"のソート/比較に違いがあります(L - ストローク - html esc:Ł)(小文字: "ł" - html esc:ł) - 次のように仮定しています。

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

ポーランド語の文字Łは、文字Lの後、Mの前です。このコーディングのどれもが良くも悪くもない - それはあなたのニーズに依存する。

2