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

推薦する

js 配列エントリ() 反復メソッドを取得する

目次1.entires() メソッドの詳細な構文2.entires() メソッドの一般的な使用法と注...

MySql データベースのサブクエリと高度なアプリケーションの簡単な分析

MySql データベースのサブクエリ:サブクエリ: 選択クエリ ステートメント内に別の選択ステートメ...

MySQL MGR 構築時の一般的な問題と解決策

目次01 よくある故障 1 02 よくある欠陥 2 03 よくある欠陥 3 04 よくある欠陥 4 ...

時刻を保存するために適切な MySQL の datetime 型を選択する方法

データベースを構築してプログラムを書くとき、日付と時刻の使用は避けられません。データベースには、ti...

Vue で PC アドレスをモバイル アドレスにリダイレクトする方法

要件:PC側とモバイル側は2つの独立したプロジェクトです。2つのプロジェクトの内容は基本的に同じで、...

HTML メタの使用例

使用例コードをコピーコードは次のとおりです。 <!DOCTYPE html> <!...

VMWare Linux MySQL 5.7.13 のインストールと設定のチュートリアル

この記事では、参考までにVMWare LinuxにMySQL 5.7.13をインストールするチュート...

Ubuntu 18.04 システムでの Redis および phpredis 拡張機能のインストールと設定の詳細な説明

この記事では、Ubuntu 18.04 に Redis と phpredis 拡張機能をインストール...

MySQLでトリガーを作成する方法

この記事の例では、参考のためにMySQLトリガーを作成するための具体的なコードを共有しています。具体...

Linux カーネル デバイス ドライバー カーネル デバッグ テクニカル ノート集

/****************** * カーネルデバッグ技術 ****************...

CentOS 6.8 に MySQL 8.0.18 をインストールするチュートリアルの簡単な分析 (RPM 方式)

今日は、CentOS 6.8 サーバーに MySQL 8.0.18 をインストールする方法を記録しま...

CSS3 Flex エラスティックレイアウトのサンプルコードの詳細な説明

1. 基本概念 //任意のコンテナを Flex レイアウトとして指定できます。 。箱{ ディスプレイ...

Linux に nginx をインストールする方法

Nginx は C 言語で開発されており、Linux で実行することをお勧めします。もちろん、Win...

Centos7 システムでの .NET Core 2.0 + Nginx + Supervisor 環境の構築

1. Linux .NET Core の紹介Microsoft は常に自社のプラットフォームに対して...

SQL ROW_NUMBER() および OVER() メソッドのケーススタディ

構文フォーマット: row_number() over(partition by grouping ...