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 サーバーでセカンダリドメイン名を設定する方法の詳細な説明

推薦する

HTMLページがincludeを使用してphpファイルをインポートした後に余分な空白行があります

インターネットで見つけた方法は効果的ですinclude によって導入されたフッター ファイルとヘッダ...

JS Canvas インターフェースとアニメーション効果

目次概要Canvas API: グラフィックスの描画パス線種矩形アーク文章グラデーションと画像の塗り...

React 国際化 react-i18next の詳細な説明

導入react-i18next は、 i18nextをベースにした強力な国際化フレームワークです。 ...

HTML は、Web ページの作成者が学習して習得しなければならないものです。

HTML を学ぶメリットは何ですか? 1: ウェブサイトやブログのウェブ構造を簡単に変更できます。...

Vueのスロットの詳細な説明

Vue でのコードの再利用により、mixnis が提供されます。テンプレートの再利用により、スロット...

MySQL 5.7 インストール MySQL サービスを開始できませんが、サービスはエラーを報告しません

MySQL 5.7 をインストールするには 2 つの方法があります。1 つはインストーラをダウンロー...

MySQL InnoDBエンジンのインデックスとストレージ構造の詳細な説明

序文Oracle や SQL Server などのデータベースには、ストレージ エンジンが 1 つだ...

MySQL クエリ フィールド タイプが json の場合の 2 つのクエリ メソッド

テーブル構造は次のとおりです。 id varchar(32) 情報JSONデータ: id = 1 i...

Tomcat ソースコード起動コンソールの中国語文字化けのデバッグプロセス記録

問題を見つける今日はTomcatのソースコードを勉強するつもりなので、公式サイトからTomcatのソ...

CSS3 ベジェ曲線の例: リンクホバーアニメーション効果の作成

CSS3 アニメーション トランジションを使用して、リンクの上にマウスを移動すると小さなポップアップ...

柔軟で安定した高品質の HTML および CSS コード標準を作成するためのガイド

黄金律常に同じコーディング標準セットに従ってください。同じプロジェクトに何人の人が関わっているかに関...

MySql でメモリ使用量を削減する方法の詳細な説明

序文デフォルトでは、MySQL はデータベース クエリ データをキャッシュするために大きなメモリ ブ...

vagrant+virtualBoxで仮想マシンを構築する方法

1. はじめにVagrant は、仮想マシン (VirtualBox) を構築および管理するためのツ...

Docker ベースの Redis クラスターの構築方法

Redisイメージをダウンロードする docker pull yyyyttttwww/redis を...