web-dev-qa-db-ja.com

多くの重複値を持つMySQL InnoDB B + Treeインデックスのパフォーマンス

私のデータベースサーバーのランダムなパフォーマンスの問題を診断しようとしています。以下は簡略化されたシナリオですが、うまくいけば、同じ答えを探している人のための将来の参考として役立つほど一般的です。

(MySQL 5.6 w/InnoDB)テーブルがあるとします

CREATE TABLE Example (
    id INT NOT NULL AUTO_INCREMENT,
    secondary_id INT DEFAULT NULL,
    some_data TEXT NOT NULL,
    PRIMARY KEY (id),
    KEY (secondary_id)
) ENGINE=InnoDB;

約1,500万行あります。ただし、secondary_id列はほとんどすべての行でNULLであるため、secondary_idのインデックスのカーディナリティは非常に低くなっています(この場合は約30k)。私たちのケースでは、調査しているパフォーマンスの問題が発生すると、サーバーのプロセスリストに次の形式のクエリが多数(100以上)表示されます。

UPDATE Example SET secondary_id = NULL, some_data = '...' WHERE id = 123;

完了までに90秒以上かかり、その間は "updating"状態 になります。 (これらのクエリは個別のトランザクションで実行されます。)

Nullではないsecondary_idからnull secondary_idへの移行が、上記のUPDATEでパフォーマンスの低下を引き起こしているのかどうか、特に疑問に思っています。つまり、この列の同じ値(NULL)を持つ行(〜15mil)が非常に多いため、この場合のインデックスの更新にかなりの時間がかかる可能性がありますか?

この質問は、Bツリーインデックスが重複するインデックス値を持つ行の行ポインタをどのように格納するかを理解していないことに起因すると思います。ノードにはかなり高速な挿入時間でリンクされたリスト(または類似のもの)があると思うので、私の質問への答えは「いいえ」だと思います。しかし、私はそれを専門家、つまり皆さん全員に確認したいと思います。

私はここでかなりの量の調査を試みましたが、私はかなり手ぶらで思いつきました。おそらく最も包括的な投稿は this one で、重複キーを処理するためのいくつかの異なる手法を説明していますが、私は特にInnoDB/MySQLのアプローチを探しています。

5
Reid

単一のUPDATEの90秒が多すぎます。おそらくいくつかのブロッキングが関係しており、調査する必要があります。

それとは別に、98%の同じ(NULL)値を持つ列を使用することも適切ではありません。その列を別のテーブル(30K行しかない)に置くことを検討する必要があります。これはINSERT/DELETE/UPDATEプロシージャを少し複雑にしますが、おそらく小さいインデックスから利益を得るでしょう。推奨デザイン:

CREATE TABLE Example (
    id INT NOT NULL AUTO_INCREMENT,
    some_data TEXT NOT NULL,
    PRIMARY KEY (id)
) ENGINE = InnoDB ;

CREATE TABLE Example_secondary (
    id INT NOT NULL,
    secondary_id INT NOT NULL,
    PRIMARY KEY (id),
    INDEX (secondary_id),
    FOREIGN KEY (id)
      REFERENCES Example (id)
) ENGINE = InnoDB ;

次に、UPDATE

UPDATE Example 
SET secondary_id = NULL, 
    some_data = '...' 
WHERE id = 123 ;

になるだろう:

BEGIN ;
    UPDATE Example 
    SET some_data = '...' 
    WHERE id = 123 ;

    DELETE FROM Example_secondary 
    WHERE id = 123 ;
COMMIT ;
4
ypercubeᵀᴹ

これにより何が得られますか:

_EXPLAIN UPDATE Example 
    SET secondary_id = NULL, 
        some_data = '...' 
    WHERE id = 123 ;
_

多分それはいくつかのより多くの手がかりを与えます。

別のアイデア:INDEX(secondary_id)INDEX(secondary_id, id)に変更します。それがBTreeに格納されているものであるとしても、明示的であるとそれをだましてより効率的になるのではないかと思います。おそらくあなたのインデックスはランダムな順序で保存されたIDを持っていますが、私は挿入/検索/などに効率的な順序でそれらを持っているでしょう。

0
Rick James