MySQL のユニークインデックスと通常のインデックスのどちらを選択すればよいでしょうか?

MySQL のユニークインデックスと通常のインデックスのどちらを選択すればよいでしょうか?

ユーザー テーブルを設計するときに、各人の ID 番号が一意であり、検索する必要があるシナリオを想像してください。ただし、ID番号フィールドは大きいため、主キーとして使用するには適していません。ビジネス コードによって挿入された ID カードが一意であることが保証されている場合、一意のインデックスと通常のインデックスを作成するように選択できます。どのように選択すればよいでしょうか?次に、クエリと更新の実行プロセスを分析します。

クエリプロセス

k はテーブル t のインデックスであると仮定します。select select id from t where k=5検索する場合、B+ ツリー k のルートから開始し、リーフ ノードをレイヤーごとに検索し、k=5 のデータ ページを見つけて、データ ページの内容に対してバイナリ ポジショニングを実行します。

通常のインデックスの場合、k=5 のレコードを見つけた後、5 以外の最初のレコードに遭遇するまで、もう一度検索を続けます。

一意のインデックスの場合、値は一意であるため、見つかったら検索を停止します。

InnoDB はデータ ページ単位で読み取りと書き込みを行うため (データ ページはデフォルトで 16 KB)、データが読み取られるときには、データ ページ全体がまとめてメモリに読み込まれます。 メモリに読み込まれたデータページ内に、k=5 のレコードが含まれている場合、クエリの場合、ユニークインデックスは通常のインデックスよりも検索と判断のプロセスが 1 つ多くなりますが、これは無視できます。

k=5 が現在のデータ ページの最後のエントリである場合、次のデータ ページを読み取る必要があります。しかし、これが起こる可能性は低いので無視できます。

したがって、一般的に、クエリ プロセス中、通常のインデックスと一意のインデックスの間に大きな違いはありません。

バッファを変更する

ユニークインデックスと共通インデックスの影響を分析する前に、まず変更バッファ構造を理解しましょう。

変更バッファとは何ですか?

更新操作を実行する際、更新対象のデータ ページがメモリ内にある場合は、直接更新されます。そうでない場合は、データの一貫性に影響を与えることなく、InnoDB は更新操作を変更バッファにキャッシュし、ディスクからデータ ページを読み取るプロセスを排除します。次のクエリ操作で更新が必要なデータ ページが読み取られると、変更バッファー内の更新ステートメントが実行され、データ ページに書き込まれます。ハードディスクに操作を適用するプロセスはマージと呼ばれます。バックグラウンド スレッドは定期的にマージするか、データベースが正常に閉じられるときにもマージ操作が実行されます。

マージの実行プロセスは次のとおりです。

  1. ディスクからデータ ページの古いバージョンを読み取ります。
  2. 変更バッファからデータ ページに関連付けられたレコードを検索し、それらを 1 つずつ適用して、データ ページの新しいバージョンを取得します。
  3. データの変更と変更バッファの変更を記録するために、REDO ログを書き込みます。

変更バッファは実際にはハードディスクに保存できるデータです。つまり、変更バッファはメモリとハードディスクの両方に存在します。変更バッファは、以前は挿入バッファと呼ばれていました。当初は挿入バッファのみが最適化されていましたが、後に削除と更新のサポートが追加され、名前が変更バッファに変更されました。

最初に更新操作を変更バッファに記録することで、ディスク データ ページをメモリに読み込むプロセスが削減され、ステートメントの実行速度が大幅に向上することがわかります。同時に、メモリへのデータの読み取りはバッファ プール メモリを占有するため、読み取り操作を減らすことでメモリ使用率も向上します。

バッファ プールは、InnoDB がアクセスするときにテーブルとインデックスのデータをキャッシュするメモリ内の領域です。頻繁に使用されるデータをメモリ内で直接更新できるため、処理が高速化されます。一部の専用サーバーでは、物理メモリの 80% がバッファ プールに分割されます。

innodb_change_buffer_max_size を使用して、変更バッファが占有するバッファ プールのサイズを設定できます。

バッファ適用シナリオを変更しますか?

前述のように、変更バッファは更新レコードを事前に保存し、データ ページの読み取りプロセスを削減してパフォーマンスを向上させます。つまり、変更バッファーにさまざまなデータ ページの更新レコードがより多く含まれている場合、メリットは大きくなります。

したがって、書き込みが多く読み取りが少ないビジネス(更新後の即時クエリ)では、変更バッファがより大きな役割を果たします。共通の課金システムやログ記録システムなど。

ビジネスが更新直後にクエリを実行する場合、更新レコードを変更バッファに配置できますが、直後にデータ ページをクエリする必要があるため、マージ プロセスがすぐにトリガーされます。これにより、ランダム アクセス IO の数は減りませんが、変更バッファーのメンテナンス コストが増加し、逆の効果が生じます。

更新プロセス

一意のインデックスの場合、すべての更新操作は一意性制約に違反しているかどうかを判断する必要があります。したがって、必要なデータ ページをメモリに読み込み、変更バッファを使用せずに直接更新する必要があります。したがって、変更バッファは通常のインデックスにのみ役立ちます。

特定の分析を行うには、テーブルに新しいレコードを挿入します。

新しいレコードによって更新されるデータ ページがメモリ内にある場合:

一意のインデックスの場合、適切な位置を見つけ、競合があるかどうかを判断し、値を挿入すると、ステートメントが終了します。

通常のインデックスの場合: 位置を見つけて値を挿入すると、ステートメントが終了します。

したがって、データ ページがメモリ内にある場合、ユニーク インデックスと通常のインデックスの唯一の違いは、判断プロセスです。無視できます。

新しいレコードによって更新されるデータ ページがメモリ内にない場合:

一意のインデックスの場合、データ ページがメモリに読み込まれ、競合が判断され、データが挿入されて、ステートメントが終了します。

共通インデックスの場合、ステートメントは変更バッファに記録され、ステートメントは終了します。

ディスクからメモリへのランダム IO アクセスが関係するため、これはデータベース内で最もコストのかかる操作の 1 つです。通常のインデックスでは、一意のインデックスと比較して読み取り操作が削減されるため、パフォーマンスが大幅に向上します。

ユニークまたは通常のインデックスの選択

クエリと更新の観点から 2 つを比較します。クエリ プロセス中、非常に特殊な状況を除いて、2 つの違いは実際にはそれほど大きくないことがわかっています。

主な違いは、更新中に、更新されるデータ ページがコンテンツ内にない場合です。このとき、ユニークインデックスはユニーク性チェックが必要なため、変更バッファを使用できません。ディスクからコンテンツにデータを読み込むという追加のプロセスがあり、ランダム IO アクセスが伴い、比較的非効率的です。

したがって、ビジネスで良好なパフォーマンスを更新する必要がある場合は、通常のインデックスを選択できます。もちろん、すべてはデータの正確性を確保するという前提に基づいています。

更新の後にクエリが続く場合は、変更バッファをオフにすることを検討してください。その他の場合は、変更バッファによって大幅な改善が得られる可能性があります。

特に機械式ハードドライブの場合、変更バッファの影響は非常に重要です。

REDOログと変更バッファの比較

InnoDB に REDO ログが導入されたことで、最初にログを書き込み、次に WAL を介してディスクに書き込むことでクラッシュセーフとなり、効率が向上しました。

変更バッファは、ディスクからメモリにデータ ページを読み取るランダム IO プロセスを保存します。

挿入ステートメントを通じて、2 つの関係を分析してみましょう。

mysql> t(id,k) に値 (id1,k1),(id2,k2) を挿入します。

k が通常のインデックスであると仮定すると、k1 によって挿入されたデータ ページはメモリ内にありますが、k2 はメモリ内にありません。

挿入操作を実行する場合、主に次の 4 つの部分が関係します。

InnoDB バッファプール: メモリ領域

再実行ログ: ログ

システム表スペース (ibdata1): システム表スペース

data(t.idb): データテーブルスペース

innodb_file_per_table がオンの場合、テーブルは別のテーブルスペースに作成されます。それ以外の場合は、システム テーブルスペースに作成されます。

実行プロセスは次のとおりです。

  1. k1が配置されているページ1はメモリ内にあるため、メモリを直接更新します。
  2. k2 が配置されているページ 2 はメモリ内にはありませんが、変更バッファーに記録されています。
  3. k1 と k2 の操作を REDO ログに記録します。
  4. トランザクションをコミットします。

この更新ステートメント (挿入、削除、更新操作を含む) の実行コストは、メモリへの書き込みが 2 回、ディスクへの順次書き込みが1 回と非常に低いことがわかります。点線でマークされた操作はバックグラウンド操作であり、応答時間には影響しません。

別のクエリステートメントを見てみましょう。

(k1, k2) 内の k が t から * を選択する

読み取りステートメントが更新ステートメントの直後に実行され、メモリ内のデータがまだそこにあると仮定すると、読み取り操作はシステム テーブルスペースおよび REDO ログとは関係ありません。

実行プロセス:

  1. メモリ内の k1 が配置されているページ 1 を読み取り、直接返します。ディスク上のデータは読み取られず、ディスク上のデータは以前のバージョンのままである可​​能性があることに注意してください。
  2. k2 が配置されているページ 2 を読み取ります。このとき、ページ 2 をディスクからメモリにロードし、変更バッファの内容を適用してから、正しい結果を返す必要があります。ここでも、変更バッファは更新直後の読み取りには適していないことがわかります。

REDO ログと変更バッファの関係をまとめると次のようになります。

保存場所: 変更バッファもハードディスク上に保存されますが、システム テーブルスペース ibdata1 に保存されます。 REDO ログは別のファイルです。

記録内容: 変更バッファには更新操作の内容が記録され、REDO ログには通常のデータ ページの変更と変更バッファ内の変更が記録されます。

ディスク同期プロセス: メモリ内のデータ ページへの変更の同期は、REDO ログに基づくのではなく、マージ操作によって実行されます。

更新プロセスの観点から見ると、REDO ログはランダム ディスク書き込み IO をシーケンシャル書き込みに変換し、変更バッファはランダム ディスク読み取り IO の消費を節約します。

サーバーの電源が予期せず失われた場合、変更バッファは失われますか?

いいえ、変更バッファ内のデータはREDOログに記録されているため、失われることはありません。

変更バッファ データの一部はディスク上にあり、一部はメモリ内にあるためです。ディスク上のデータは結合されているため、失われることはありません。
メモリ内のデータの場合:

  1. 変更バッファが書き込まれても、REDO ログと binlog がコミットされていない場合、トランザクションはロールバックされ、この部分のデータは存在しなくなります。
  2. 変更バッファ、REDO ログ、および binlog が書き込まれてコミットされた場合、それらは失われません。 REDO ログから直接復元します。
  3. 変更バッファが書き込まれ、REDO ログが書き込まれたがコミットされておらず、バイナリログが書き込まれている場合は、バイナリログから REDO ログを復元してから、変更バッファを復元します。

参考文献

バッファプール

上記は、MySQL のユニーク インデックスと通常のインデックスのどちらを選択すればよいかということです。 MySQL のユニークインデックスと共通インデックスの詳細については、123WORDPRESS.COM の他の関連記事に注目してください。

以下もご興味があるかもしれません:
  • MySQL共通インデックスとユニークインデックスの選択に関する詳細な分析
  • MySQL共通インデックスとユニークインデックスの詳細な説明
  • MySQL の共通インデックスとユニークインデックスの効率比較
  • MySQLの通常インデックスとユニークインデックスの違いの詳しい説明

<<:  Vue ページをリフレッシュするために provide と injection を適用する

>>:  Vueのログインとログアウトの詳細な説明

推薦する

Vue ベースの要素ボタン権限実装ソリューション

背景要件: ERP システムに「ボタン権限制御」機能を追加する必要があり、権限の制御粒度をボタン レ...

DIV+CSS命名規則の詳細な説明はSEO最適化に役立ちます

1. CSSファイルの命名規則提案: 文字、_、-、数字を使用します。文字で始まる必要があり、純粋な...

MySQL での find_in_set() 関数の使用に関する詳細な説明

まず、例を見てみましょう。記事テーブルにはタイプフィールドがあり、1 見出し、2 おすすめ、3 ホッ...

Nginx で limit_req_zone を使用して同じ IP へのアクセスを制限する方法

Nginx は、ngx_http_limit_req_module モジュールの limit_req...

SQL 文を使用してデータを収集する場合の sum 関数と count 関数の if 判定条件の使用法の説明

まず、例を挙げてみましょう(読みたくない場合は、以下の要約だけ読んでください)。 order_typ...

Nginx 経由で Tomcat9 クラスターを構築し、セッション共有を実現する

Nginx を使用して Tomcat9 クラスターを構築し、Redis を使用してセッション共有を実...

HTML テーブルの使い方 (Web ページの視覚効果を表示する)

NetEase Blog で HTML を使用する場合、テンプレートに直接コードを追加できることは...

Nginx アップロードファイルのサイズを変更する簡単な方法

オリジナルリンク: https://vien.tech/article/138序文私は、マークダウン...

Dockerでコンテナを作成するときにコンテナIPを指定する実装例

Docker はコンテナを作成するときに、デフォルトでブリッジ ネットワークを使用し、IP アドレス...

Ubuntu環境でのSSHの詳細なインストールと使用

SSH は Secure Shell の略で、安全な伝送プロトコルです。Ubuntu クライアントは...

docker --privileged=true パラメータの役割についての簡単な説明

バージョン 0.6 あたりで、Docker に privileged が導入されました。このパラメー...

ピクセルを包括的なブランド体験に変えるヒント

編集者:この記事では、インタラクティブデザインがブランドコミュニケーションチェーン全体で果たすべき役...

ウェブタイポグラフィにおける致命的な意味的ミス 10 選

<br />これは、Steven D が書いた Web フロントエンド開発デザインの基本...

Vue ベースの Excel 解析とエクスポートの詳細な説明

目次序文基本的な紹介コードの実装基本構造アップロード分析Excel にエクスポート基本構造Excel...

Linuxシステムのログの詳細な紹介

目次1. ログ関連サービス2. システム内の共通ログファイル1. ログ関連サービスCentOS 6....