11

この例の構造/データがあるとします。

@ http://sqlfiddle.com/#!8/1f85e/1のフィドルを参照

-- SET GLOBAL innodb_file_per_table=1;
DROP TABLE IF EXISTS mysql_index_reading_myisam;
CREATE TABLE IF NOT EXISTS mysql_index_reading_myisam (
    id INT NOT NULL AUTO_INCREMENT
  , str VARCHAR(50) NOT NULL
  , enm ENUM('thatis', 'thequestion') NOT NULL
  , cnt TINYINT NOT NULL

  , PRIMARY KEY (id)
  , INDEX str_cnt (str, cnt)
  , INDEX enm_cnt (enm, cnt)

) ENGINE=MyISAM CHARSET=Latin1;
INSERT INTO mysql_index_reading_myisam (str, enm, cnt) VALUES
    ('Tobeornottobe', 'Thatis', 1)
  , ('toBeornottobe', 'thatIs', 2)
  , ('tobeOrnottobe', 'ThatIs', 3)
  , ('tobeorNottobe', 'thatis', 4)
  , ('tobeornotTobe', 'THATIS', 5)
;
DROP TABLE IF EXISTS mysql_index_reading_innodb;
CREATE TABLE mysql_index_reading_innodb LIKE mysql_index_reading_myisam;
ALTER TABLE mysql_index_reading_innodb ENGINE InnoDB;
INSERT INTO mysql_index_reading_innodb SELECT * FROM mysql_index_reading_myisam;

EXPLAIN SELECT cnt FROM mysql_index_reading_myisam WHERE str = 'tobeornottobe';
EXPLAIN SELECT cnt FROM mysql_index_reading_innodb WHERE str = 'tobeornottobe';
EXPLAIN SELECT cnt FROM mysql_index_reading_myisam WHERE enm = 'thatis';
EXPLAIN SELECT cnt FROM mysql_index_reading_innodb WHERE enm = 'thatis';

内部にどのように格納されているか確認してみましょう

# egrep --ignore-case --only-matching --text '(tobeornottobe|thatis)' *
mysql_index_reading_innodb.frm:thatis
mysql_index_reading_innodb.ibd:Tobeornottobe
mysql_index_reading_innodb.ibd:toBeornottobe
mysql_index_reading_innodb.ibd:tobeOrnottobe
mysql_index_reading_innodb.ibd:tobeorNottobe
mysql_index_reading_innodb.ibd:tobeornotTobe
mysql_index_reading_innodb.ibd:Tobeornottobe
mysql_index_reading_innodb.ibd:toBeornottobe
mysql_index_reading_innodb.ibd:tobeOrnottobe
mysql_index_reading_innodb.ibd:tobeorNottobe
mysql_index_reading_innodb.ibd:tobeornotTobe
mysql_index_reading_myisam.frm:thatis
mysql_index_reading_myisam.MYD:Tobeornottobe
mysql_index_reading_myisam.MYD:toBeornottobe
mysql_index_reading_myisam.MYD:tobeOrnottobe
mysql_index_reading_myisam.MYD:tobeorNottobe
mysql_index_reading_myisam.MYD:tobeornotTobe
mysql_index_reading_myisam.MYI:Tobeornottobe
mysql_index_reading_myisam.MYI:toBeornottobe
  • 両方のエンジンで、enum は本来あるべき *.frm に格納されます。Ok。
  • どちらのエンジンでも、データはデータ ファイルとデータ/インデックス ファイルに格納されます。Ok。
  • MyISAM のインデックスには 2 つのレコードがあります。
  • InnoDB インデックスでは、5 つのレコードすべてが正しい大文字と小文字で区別されます。

私がすでに見つけたもの

http://dev.mysql.com/doc/refman/5.1/en/mysql-indexes.html

場合によっては、データ行を参照せずに値を取得するようにクエリを最適化できます。クエリが数値であり、いくつかのキーの左端のプレフィックスを形成するテーブルの列のみを使用する場合、選択された値をインデックス ツリーから取得して高速化することができます。

SELECT key_part3 FROM tbl_name WHERE key_part1=1

http://www.mysqlperformanceblog.com/2009/09/12/3-ways-mysql-uses-indexes/

インデックスを使用してデータを読み取る 一部のストレージ エンジン (MyISAM および Innodb を含む) は、インデックスを使用してデータを読み取ることもできるため、行データ自体の読み取りを回避できます。これは、インデックス エントリごとに 1 回ではなく 2 回の読み取りを行うことによる単純な節約ではありませんが、場合によっては IO を大幅に節約できます。同じページですが、行自体が多くのページに分散している可能性があり、多くの IO が必要になる可能性があります。それに加えて、いくつかの列にアクセスする必要があるだけの場合、インデックスはデータよりもはるかに小さい可能性があります。これは、データがメモリ内にある場合でも、インデックスをカバーすることでクエリを高速化するのに役立つ理由の 1 つです。MySQL がインデックスを読み取るだけで行にアクセスしない場合、EXPLAIN 出力に「using index」と表示されます。

次に、sql_select.cc のソース: http://bazaar.launchpad.net/~mysql/mysql-server/5.1/view/head:/sql/sql_select.cc#L12834

/*
  We can remove binary fields and numerical fields except float,
  as float comparison isn't 100 % secure
  We have to keep normal strings to be able to check for end spaces
*/
if (field->binary() &&
    field->real_type() != MYSQL_TYPE_STRING &&
    field->real_type() != MYSQL_TYPE_VARCHAR &&
    (field->type() != MYSQL_TYPE_FLOAT || field->decimals() == 0))
{
  return !store_val_in_field(field, right_item, CHECK_FIELD_WARN);
}

だから私の質問は

  1. データとしてのみ必要なインデックス文字列列に格納することは実用的ですか? たとえば、20 列のテーブルで、多くの場合、intcolumn で検索される strcolumn が必要です。(intcolumn,strcolumn) のようなインデックスを作成するのは良いことですか、それとも本当に必要なのは (intcolumn) だけですか?

  2. innodb エンジンの mysql は、データを取得するための特別なアクションを実際に実行しますか ("Using where; Using index" が表示された場合)。

  3. ENUMでも同じことが起こります。これは、Enum_field の real_type が MYSQL_TYPE_STRING を返すために発生します。列挙型でも同じですか?

  4. それでは、列挙型は非常に悪であり、代わりに単純な参照テーブルを常に使用する必要があると想定できますか?

  5. MyISAM の場合、すべての値がインデックスに保存されるわけではないため、理解できます。しかし、なぜ 1 つではなく 2 つの値を格納するのでしょうか。

  6. これがすべて実際に発生する場合、具体的なハンドラーの実装に依存しないのは、mysql カーネルの現在の制限だけですか?

ps: この質問は非常に大きなものだと思います。誰かがそれを再構築/破るのを手伝ってくれるなら、それは素晴らしいことです。


Update1:「インデックスの使用」と「インデックスの使用」に関する別の SQL を追加する; where を使用する

@ http://sqlfiddle.com/#!8/3f287/2のフィドルを参照

DROP TABLE IF EXISTS tab;
CREATE TABLE IF NOT EXISTS tab (
    id INT NOT NULL AUTO_INCREMENT
  , num1 TINYINT NOT NULL
  , num2 TINYINT
  , str3 CHAR(1) NOT NULL

  , PRIMARY KEY (id)
  , INDEX num1_num2 (num1, num2)
  , INDEX num1_str3 (num1, str3)
  , INDEX num2_num1 (num2, num1)
  , INDEX str3_num1 (str3, num1)

) ENGINE=InnoDB;
INSERT INTO tab (num1, num2, str3) VALUES
    (1, 1, '1')
  , (2, 2, '2')
  , (3, 3, '3')
  , (4, 4, '4')
  , (5, 5, '5')
  , (6, 6, '6')
  , (7, 7, '7')
  , (8, 8, '8')
  , (9, 9, '9')
  , (0, 0, '0')
;
INSERT INTO tab (num1, num2, str3) SELECT num1, num2, str3 FROM tab;

-- Using index
EXPLAIN SELECT num2 FROM tab WHERE num1 =  5;
EXPLAIN SELECT str3 FROM tab WHERE num1 =  5;
-- Using where; Using index
EXPLAIN SELECT num1 FROM tab WHERE num2 =  5;
EXPLAIN SELECT num1 FROM tab WHERE str3 = '5';

質問#2

  1. 非 null int による検索の場合、「インデックスを使用しています」と表示されるのはなぜですか?

  2. しかし、null 許容の int OR string の場合、"Using where" も表示されますか?

  3. mysql はそこでどのような追加アクションを行いますか?

4

1 に答える 1

7
  1. データとしてのみ必要なインデックス文字列列に格納することは実用的ですか? たとえば、20 列のテーブルで、多くの場合、intcolumn で検索される strcolumn が必要です。(intcolumn,strcolumn) のようなインデックスを作成するのは良いことですか、それとも本当に必要なのは (intcolumn) だけですか?

    これはカバリング インデックスと呼ばれます。テーブル データ内のレコードの中から値を検索することなく、選択した列をインデックス ファイルから取得できるというパフォーマンス上の利点があります。

    すべての場合と同様に、その使用はトレードオフであり、状況によっては適切である場合とそうでない場合があります。

  2. innodb エンジンの mysql は、データを取得するための特別なアクションを実際に実行しますか ("Using where; Using index" が表示された場合)。

    質問のリンク先の sqlfiddle はUsing where; Using index、4 つのクエリすべてについて表示されます。EXPLAIN追加情報の下に記載されているとおり:

    出力のExtra列にはEXPLAIN、MySQL がクエリを解決する方法に関する追加情報が含まれています。次のリストは、この列に表示される値について説明しています。

    デレティア
    • Using index

      列情報は、実際の行を読み取るために追加のシークを行うことなく、インデックス ツリー内の情報のみを使用してテーブルから取得されます。この戦略は、クエリが単一のインデックスの一部である列のみを使用する場合に使用できます。

      Extra列に も表示されている場合はUsing where、キー値の検索を実行するためにインデックスが使用されていることを意味します。がないUsing whereと、オプティマイザはインデックスを読み取ってデータ行の読み取りを回避する可能性がありますが、ルックアップには使用しません。たとえば、インデックスがクエリのカバリング インデックスである場合、オプティマイザはルックアップに使用せずにスキャンすることがあります。

    したがって、すべてのクエリは、使用されているストレージ エンジンに関係なく、ルックアップとデータ取得の両方にカバリング インデックスを使用しています。

    「 innodbエンジンは実際にデータを取得するためにいくつかの追加のアクションを実行します」と言ったときに、あなたが何を参照しているのかはっきりしません。EXPLAIN私が見ることができる出力の唯一の違いは、InnoDB クエリが列に低い値を示していることです。Rowsただし、文書化されているように

    このrows列は、MySQL がクエリを実行するために調べる必要があると考える行の数を示します。

    テーブルの場合InnoDB、この数値は推定値であり、常に正確であるとは限りません。

  3. ENUMでも同じことが起こります。これは、Enum_field の real_type が MYSQL_TYPE_STRING を返すために発生します。列挙型でも同じですか?

    繰り返しますが、「同じことが起こる」と言うとき、あなたが何を指しているのかはっきりしません。ただし、上で説明したように、Using where; Using indexは、検索とデータ検索の両方にカバリング インデックスが使用されたことを示しているにすぎません。

    さらに、ENUMフィールドにreal_typeMYSQL_TYPE_ENUMではなくの がありMYSQL_TYPE_STRINGます。参照sql/field.h:1873:

      enum_field_types real_type() const { return MYSQL_TYPE_ENUM; }
    
  4. それでは、列挙型は非常に悪であり、代わりに単純な参照テーブルを常に使用する必要があると想定できますか?

    避けるべき理由はたくさんありますがENUM、あなたの質問はそれらのどれにも触れていないと思います。

  5. MyISAM の場合、すべての値がインデックスに保存されるわけではないため、理解できます。しかし、なぜ 1 つではなく 2 つの値を格納するのでしょうか。

    そのegrep結果は、誤った結論を導き出しています。パターンの大文字と小文字を区別しない検索でファイル"tobeornottobe"内に 2 つの一致する文字列が見つかったからといって、MyISAM インデックスに 2 つのレコードがあるとは限りません。データ構造は次のようなツリーです。.myi

                  /\
                 / \
    とべーのとべーのとべーのとべーとべーのとべーとべーのとべー
                       /\
                      / \
         tobeornottobe tobeorNottobe
                           \
                            \
                             tobeornotTobe
    

    .myiすべての文字列のインデックス ファイルを表示すると、このヒントが得られます。

    $ 文字列 mysql_index_reading_myisam.MYI
    とべーのとべー
    とべかのとべ
    beOrnottobe
    またはのとべ
    とべない
    

    したがって、 pattern に対して (大文字と小文字を区別しない) 検索を実行する"nottobe"と、2 つではなく 5 つの一致が検出されます。

    The .MYIFileで、MyISAM のインデックス構造のストレージ形式について詳しく読むことができます。

  6. これがすべて実際に発生する場合、具体的なハンドラーの実装に依存しないのは、mysql カーネルの現在の制限だけですか?

    ここで何を聞かれているのかわからないのが残念です。

于 2013-05-31T11:14:39.347 に答える