MySQLクエリのパフォーマンスに影響を与える大きなオフセットの理由と最適化の詳細な説明

MySQLクエリのパフォーマンスに影響を与える大きなオフセットの理由と最適化の詳細な説明

序文

MySQL クエリは select コマンドを使用し、limit および offset パラメータを使用して、指定された範囲のレコードを読み取ることができます。この記事では、オフセットが大きすぎて MySQL クエリのパフォーマンスに影響する理由と、それを最適化する方法について紹介します。

テストデータシートとデータを準備する

1. テーブルを作成する

テーブル「メンバー」を作成(
 `id` int(10) 符号なし NOT NULL AUTO_INCREMENT,
 `name` varchar(10) NOT NULL COMMENT '名前',
 `gender` tinyint(3) unsigned NOT NULL COMMENT '性別',
 主キー (`id`)、
 キー `gender` (`gender`)
)ENGINE=InnoDB デフォルト文字セット=utf8;

2. 1,000,000件のレコードを挿入する

<?php
$pdo = 新しい PDO ("mysql:host=localhost;dbname=user","root",'');

($i=0; $i<1000000; $i++){
 $name = substr(md5(time().mt_rand(000,999)),0,10);
 性別 = mt_rand(1,2);
 $sqlstr = "メンバー(名前、性別)に値('".$name."','".$gender."')を挿入します";
 $stmt = $pdo->prepare($sqlstr);
 $stmt->execute();
}
?>

mysql> メンバーから count(*) を選択します。
+----------+
| カウント(*) |
+----------+
| 1000000 |
+----------+
セット内1列(0.23秒)

3. 現在のデータベースバージョン

mysql> バージョンを選択します();
+-----------+
| バージョン() |
+-----------+
| 5.6.24 |
+-----------+
セット内の1行(0.01秒)

大きなオフセットがパフォーマンスに影響を与える理由を分析する

1. オフセットが小さい場合

mysql> メンバーから * を選択し、性別 = 1 を制限 10,1 にします。
+----+------------+--------+
| ID | 名前 | 性別 |
+----+------------+--------+
| 26 | 509e279687 | 1 |
+----+------------+--------+
セット内の 1 行 (0.00 秒)

mysql> メンバーから * を選択して、性別 = 1 の制限を 100,1 にします。
+-----+------------+--------+
| ID | 名前 | 性別 |
+-----+------------+--------+
| 211 | 07c4cbca3a | 1 |
+-----+------------+--------+
セット内の 1 行 (0.00 秒)

mysql> メンバーから * を選択して、性別 = 1 の制限を 1000,1 にします。
+------+------------+---------+
| ID | 名前 | 性別 |
+------+------------+---------+
| 1975 | e95b8b6ca1 | 1 |
+------+------------+---------+
セット内の 1 行 (0.00 秒)

オフセットが小さいとクエリ速度が速くなり、効率が高くなります。

2. 大きなオフセット

mysql> メンバーから * を選択して、性別 = 1 の制限を 100000,1 にします。
+--------+------------+---------+
| ID | 名前 | 性別 |
+--------+------------+---------+
| 199798 | 540db8c5bc | 1 |
+--------+------------+---------+
セット内1列(0.12秒)

mysql> メンバーから * を選択し、性別 = 1 を制限 200000,1 にします。
+--------+------------+---------+
| ID | 名前 | 性別 |
+--------+------------+---------+
| 399649 | 0b21fec4c6 | 1 |
+--------+------------+---------+
セット内1列(0.23秒)

mysql> メンバーから * を選択して、性別 = 1 の制限を 300000,1 にします。
+--------+------------+---------+
| ID | 名前 | 性別 |
+--------+------------+---------+
| 599465 | f48375bdb8 | 1 |
+--------+------------+---------+
セット内1列(0.31秒)

オフセットが大きい場合、効率の問題が発生し、オフセットが増加するにつれて、実行効率が低下します。

パフォーマンスに影響を与える理由を分析する

性別=1 の制限 300000,1 のメンバーから * を選択します。

データ テーブルは InnoDB であるため、InnoDB インデックスの構造に応じて、クエリ プロセスは次のようになります。

  • セカンダリ インデックスを通じて主キーの値を検索します (性別 = 1 のすべての ID を検索します)。
  • 次に、見つかった主キー値に基づいて、主キー インデックスを通じて対応するデータ ブロックを検索します (ID に基づいて対応するデータ ブロックの内容を検索します)。
  • オフセットの値に従って、300001 番目の主キー インデックスのデータを照会し、前の 300000 件のレコードを破棄し、最後のレコードを取り出します。

しかし、セカンダリ インデックスですでにプライマリ キー値が検索されているのに、なぜプライマリ キー インデックスを使用して最初にデータ ブロックを検索し、次にオフセット値に基づいてオフセット処理を実行する必要があるのでしょうか。

主キーインデックスを見つけた後、まずオフセット処理を実行し、300,000 をスキップし、300,001 番目のレコードの主キーインデックスを通じてデータブロックを読み取ると、効率が向上します。

主キーのみをクエリした場合、何が違うのか確認する

mysql> メンバーから ID を選択し、性別を 1 に制限します (300000,1)。
+--------+
|id|
+--------+
|599465|
+--------+
セット内の1行(0.09秒)

明らかに、主キーのみをクエリすると、すべてのフィールドをクエリする場合に比べて実行効率が大幅に向上します。

投機

主キーのみをクエリする

セカンダリ インデックスが主キー値を見つけ、クエリは主キーのみを読み取る必要があるため、MySQL は最初にオフセット操作を実行し、その後、後続の主キー インデックスに基づいてデータ ブロックを読み取ります。

すべてのフィールドをクエリする必要がある場合

セカンダリインデックスは主キーの値のみを検索しますが、他のフィールドの値を取得するにはデータブロックを読み取る必要があるためです。したがって、MySQL は最初にデータ ブロックの内容を読み取り、次にオフセット操作を実行し、最後にスキップする必要がある前のデータを破棄して、後続のデータを返します。

確認

InnoDB には、データ ページやインデックス ページなど、最近アクセスされたデータ ページを格納するバッファー プールがあります。

テスト目的で、まず MySQL を再起動してから、バッファ プールの内容を確認します。

mysql> select index_name,count(*) from information_schema.INNODB_BUFFER_PAGE where INDEX_NAME in('primary','gender') and TABLE_NAME like '%member%' group by index_name;
空セット (0.04 秒)

再起動後にデータ ページにアクセスされていないことがわかります。

すべてのフィールドをクエリし、バッファプールの内容を表示します。

mysql> メンバーから * を選択して、性別 = 1 の制限を 300000,1 にします。
+--------+------------+---------+
| ID | 名前 | 性別 |
+--------+------------+---------+
| 599465 | f48375bdb8 | 1 |
+--------+------------+---------+
セット内1列(0.38秒)

mysql> select index_name,count(*) from information_schema.INNODB_BUFFER_PAGE where INDEX_NAME in('primary','gender') and TABLE_NAME like '%member%' group by index_name;
+------------+-----------+
| インデックス名 | カウント(*) |
+------------+-----------+
| 性別 | 261 |
| プライマリ | 1385 |
+------------+-----------+
セットに2行(0.06秒)

この時点で、バッファー プールにはメンバー テーブルのデータ ページが 1385 個、インデックス ページが 261 個あることがわかります。

MySQLを再起動してバッファプールをクリアし、主キーのみをクエリするテストを続行します。

mysql> メンバーから ID を選択し、性別を 1 に制限します (300000,1)。
+--------+
|id|
+--------+
|599465|
+--------+
セット内の1行(0.08秒)

mysql> select index_name,count(*) from information_schema.INNODB_BUFFER_PAGE where INDEX_NAME in('primary','gender') and TABLE_NAME like '%member%' group by index_name;
+------------+-----------+
| インデックス名 | カウント(*) |
+------------+-----------+
| 性別 | 263 |
| プライマリー | 13 |
+------------+-----------+
セットに2行(0.04秒)

この時点で、バッファー プールにはメンバー テーブルのデータ ページが 13 個、インデックス ページが 263 個しかないことがわかります。したがって、主キー インデックスを介してデータ ブロックにアクセスする複数の I/O 操作が削減され、実行効率が向上します。

したがって、オフセットが大きすぎて MySQL クエリ時のパフォーマンスに影響を与える原因は、主キーインデックスを介してデータブロックに複数回アクセスする I/O 操作であることが確認できます。 (この問題が発生するのは InnoDB のみであり、MYISAM インデックス構造は InnoDB とは異なることに注意してください。セカンダリ インデックスはすべてデータ ブロックを直接ポイントするため、このような問題は発生しません)。

InnoDB と MyISAM エンジンのインデックス構造の比較

ここに写真の説明を記入してください

最適化手法

上記の分析に基づいて、すべてのフィールドをクエリすると、主キー インデックスによるデータ ブロックへの複数回のアクセスによって I/O 操作が発生することがわかります。

したがって、最初にオフセット主キーを見つけ、次に主キー インデックスに基づいてデータ ブロックのすべての内容を照会して最適化を実現します。

mysql> メンバーから a.* を内部結合として選択します (メンバーから id を選択し、性別 = 1、制限 300000,1)、b として a.id = b.id で選択します。
+--------+------------+---------+
| ID | 名前 | 性別 |
+--------+------------+---------+
| 599465 | f48375bdb8 | 1 |
+--------+------------+---------+
セット内の1行(0.08秒)

付録: MYSQL の制限、オフセットの違い

選択
  キーワード
から
  キーワードランク
どこ
  広告id='59'
並び替え
  キーワード
制限 2 オフセット 1;

たとえば、この SQL ステートメントでは、制限の後に 2 つのレコードが続き、オフセットの後に最初のレコードが続きます。

選択
  キーワード
から
  キーワードランク
どこ
  広告id='59'
注文する
  キーワード
制限 2,1;

この SQL では、制限に従って 2 番目の項目から読み取り、1 つの情報を読み取ります。

これら2つを混同しないでください。

要約する

上記はこの記事の全内容です。この記事の内容が皆さんの勉強や仕事に一定の参考学習価値を持つことを願っています。ご質問があれば、メッセージを残してコミュニケーションしてください。123WORDPRESS.COM を応援していただきありがとうございます。

以下もご興味があるかもしれません:
  • MySQL パフォーマンスの最適化: インデックスを効率的かつ正しく使用する方法
  • MySQL データベース クエリ パフォーマンス最適化戦略
  • MySQLの高性能最適化スキルの概要
  • MySQL パフォーマンス最適化のための魔法のツール、Explain の基本的な使用分析
  • MySQL スロークエリを通じて MySQL のパフォーマンスを最適化する方法
  • MySQL Limitパフォーマンス最適化とページングデータパフォーマンス最適化の詳細な説明
  • MySQL の集計関数 count の使用法とパフォーマンスの最適化テクニック
  • 数千万のデータを扱うMySQLのページングクエリのパフォーマンスを最適化する
  • MYSQL開発パフォーマンス調査:バッチデータ挿入の最適化方法
  • MySQL パフォーマンス最適化のヒント

<<:  JSはreduce()メソッドを使用してツリー構造データを処理します

>>:  Linux 環境の Apache サーバーでセカンダリドメイン名を設定する方法の詳細な説明

推薦する

CentOS8.0ネットワーク設定の実装

1. CentOS 7 と CentOS 8 のネットワーク構成の違い: VMware Workst...

IE8はマルチ互換モードを使用してWebページを正常に表示します

IE8には複数の互換モードがあります。 IE プラットフォームの設計者である Chris Wilso...

react+antd.3x は IP 入力ボックスを実装します

この記事では、IP入力ボックスを実装するための react+antd.3x の具体的なコードを参考ま...

nginx をシャットダウン/再起動/起動する方法

閉鎖サービス nginx 停止systemctl 停止 nginx起動するサービス nginx 開始...

CentOS MySQLデータベースのスケジュールバックアップを実装する方法

次のスクリプトは、MySQLデータベース全体のスケジュールされたバックアップに使用されます。 mys...

nginxディレクトリパスをリダイレクトする方法

ドメイン名に続くパスがデフォルトの Web ディレクトリではなく、ローカル ディスク上の他のディレク...

WebWorkerはJavaScriptサンドボックスの詳細をカプセル化します

目次1. シナリオ2. IJavaScriptShadowboxを実装する2.1 メインスレッドの実...

Nginx を使用して rtmp ライブ サーバーを実行する方法

今回は、コンピューターや携帯電話用の rtmp ライブ ブロードキャスト サーバーを設定し、ライブ ...

HTTP および HTTP コラボレーション Web サーバー アクセス フロー図

Web サーバーは、独立したドメイン名を持つ複数の Web サイトを構築できるほか、通信経路上のトラ...

一般的なMysql DDL操作の概要

図書館管理ライブラリを作成する データベースを作成します [存在しない場合] ライブラリ名;ライブラ...

K8Sの5つのコントローラーの紹介と使用

目次k8sのコントローラータイプポッドとコントローラの関係デプロイメント(ステートレスアプリケーショ...

Linux で pip 操作中にタイムアウトが発生する問題を解決する方法

Linuxインスタンスでpipを使用する際のタイムアウト問題を解決する方法pip は最も人気のある ...

Linux xargsコマンドの使用

1. 機能: xargs は、stdin 内のスペースまたは改行で区切られたデータをスペースで区切ら...

Vue で SuperMap を使用する練習

目次序文関連資料Vue プロジェクトが 2 次元ハイパーグラフを導入ハイパーグラフ 2D ケース引用...

Dockerはブリッジを追加し、IPアドレスの範囲を設定します

バイナリ docker 19.03 バージョンがインストール後に docker0 ブリッジを自動的に...