MySQL で制限を使用するとパフォーマンスに影響するのはなぜですか?

MySQL で制限を使用するとパフォーマンスに影響するのはなぜですか?

まず、MySQL のバージョンについて説明します。

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

テーブル構造:

mysql> desc テスト;
+--------+----------------------+------+-----+---------+----------------+
| フィールド | タイプ | Null | キー | デフォルト | 追加 |
+--------+----------------------+------+-----+---------+----------------+
| id | bigint(20) unsigned | NO | PRI | NULL | auto_increment |
| val | int(10) 符号なし | NO | MUL | 0 | |
| ソース | int(10) 符号なし | NO | | 0 | |
+--------+----------------------+------+-----+---------+----------------+
セット内の 3 行 (0.00 秒)

id は自動インクリメントの主キーであり、val は一意でないインデックスです。

合計500万件の大量のデータを投入します。

mysql> テストから count(*) を選択します。
+----------+
| カウント(*) |
+----------+
|5242882|
+----------+
セット1列(4.25秒)

limit offset rowsoffsetが大きい場合、効率の問題が発生することが分かっています。

mysql> select * from test where val=4 limit 300000,5;
+---------+-----+--------+
| id | 値 | ソース |
+---------+-----+--------+
| 3327622 | 4 | 4 |
| 3327632 | 4 | 4 |
| 3327642 | 4 | 4 |
| 3327652 | 4 | 4 |
| 3327662 | 4 | 4 |
+---------+-----+--------+
5 列セット (15.98 秒)

同じ目的を達成するために、通常は次のように書き直します。

mysql> select * from test a 内部結合 (select id from test where val=4 limit 300000,5) b on a.id=b.id;
+---------+-----+--------+---------+
| id | val | ソース | id |
+---------+-----+--------+---------+
| 3327622 | 4 | 4 | 3327622 |
| 3327632 | 4 | 4 | 3327632 |
| 3327642 | 4 | 4 | 3327642 |
| 3327652 | 4 | 4 | 3327652 |
| 3327662 | 4 | 4 | 3327662 |
+---------+-----+--------+---------+
セット5行(0.38秒)

時間の違いは明らかです。

なぜ上記のような結果が表示されるのでしょうか? select * from test where val=4 limit 300000,5;のクエリ プロセスを見てみましょう。

インデックス リーフ ノード データが照会されます。
リーフ ノードの主キー値に基づいて、クラスター化インデックス上のすべての必須フィールド値をクエリします。

次の図のようになります。

上記のように、インデックス ノードを300005回クエリし、クラスター化インデックス データを300005回クエリし、最後に最初の300000結果をフィルターして最後の 5 件を取り出す必要があります。 MySQL 、クラスター化インデックスのデータをクエリするために大量のランダム I/O を費やし、 300000のランダムI/Oによってクエリされたデータは結果セットに表示されません。

誰かが必ずこう尋ねるでしょう: インデックスは最初に使用されるので、最初にインデックス リーフ ノードに沿って必要な最後の 5 つのノードまでクエリを実行し、次にクラスター化インデックス内の実際のデータをクエリするのはなぜですか。これには、次の図のプロセスと同様に、5 つのランダム I/O のみが必要です。

確認済み:

上記の推論を確認するために実際にいくつかの操作を実行してみましょう。

select * from test where val=4 limit 300000 , 5 がクラスター化インデックス上で300005インデックス ノードと300005データ ノードをスキャンすることを証明するには、MySQL に 1 つの SQL でインデックス ノードを介してデータ ノードがクエリされる回数をカウントする方法があるかどうかを知る必要があります。まずHandler_read_*シリーズを試してみましたが、残念ながらどの変数も条件を満たしませんでした。

私はこれを間接的にしか確認できません:

InnoDBにはbuffer poolがあります。データ ページやインデックス ページなど、最近アクセスされたデータ ページが含まれます。したがって、 buffer pool内のデータ ページの数を比較するには、 2 つの SQL ステートメントを実行する必要があります。予測結果では、 select * from test a inner join (select id from test where val=4 limit 300000,5) b>実行した後、 buffer pool内のデータ ページ数はselect * from test where val=4 limit 300000, 5; の対応する数よりもはるかに少なくなります。これは、前者の SQL がデータ ページに 5 回しかアクセスしないのに対し、後者の SQL がデータ ページに300005回アクセスするためです。

val=4 の制限 300000,5 のテストから * を選択

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

現在、 buffer pool内にテスト テーブルに関するデータ ページが存在しないことがわかります。

mysql> select * from test where val=4 limit 300000,5;
+---------+-----+--------+
| id | 値 | ソース |
+---------+-----+--------+
| 3327622 | 4 | 4 |
| 3327632 | 4 | 4 |
| 3327642 | 4 | 4 |
| 3327652 | 4 | 4 |
| 3327662 | 4 | 4 |
+---------+-----+--------+
セット5列(26.19秒)

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

この時点で、 buffer poolにはテスト テーブルのデータ ページが4098ページ、インデックス ページが 208 ページあることがわかります。

select * from test a inner join (select id from test where val=4 limit 300000,5) b>buffer poolをクリアしてmysql。

mysqladmin シャットダウン
/usr/local/bin/mysqld_safe &
mysql> select index_name,count(*) from information_schema.INNODB_BUFFER_PAGE where INDEX_NAME in('val','primary') and TABLE_NAME like '%test%' group by index_name;
空セット (0.03 秒)

SQL を実行します:

mysql> select * from test a 内部結合 (select id from test where val=4 limit 300000,5) b on a.id=b.id;
+---------+-----+--------+---------+
| id | val | ソース | id |
+---------+-----+--------+---------+
| 3327622 | 4 | 4 | 3327622 |
| 3327632 | 4 | 4 | 3327632 |
| 3327642 | 4 | 4 | 3327642 |
| 3327652 | 4 | 4 | 3327652 |
| 3327662 | 4 | 4 | 3327662 |
+---------+-----+--------+---------+
セットに5行(0.09秒)

mysql> select index_name,count(*) from information_schema.INNODB_BUFFER_PAGE where INDEX_NAME in('val','primary') and TABLE_NAME like '%test%' group by index_name;
+------------+-----------+
| インデックス名 | カウント(*) |
+------------+-----------+
| プライマリ | 5 |
| 値 | 390 |
+------------+-----------+
セットに2行(0.03秒)

2 つの違いは明らかです。最初の SQL は 4098 のデータ ページをbuffer poolにロードしますが、2 番目の SQL は 5 つのデータ ページのみをbuffer pool。私たちの予測通りです。これにより、最初の SQL ステートメントが遅い理由も確認できます。大量の役に立たないデータ行 (300,000) を読み取ってから破棄します。

そして、これは問題を引き起こします。あまりホットではないデータ ページを大量にbuffer poolにロードすると、 buffer pool汚染が発生し、 buffer poolスペースが占有されます。

発生した問題:

再起動のたびにbuffer poolがクリアされるようにするには、 innodb_buffer_pool_dump_at_shutdowninnodb_buffer_pool_load_at_startupオフにする必要があります。これら 2 つのオプションは、データベースのシャットダウン時にバッファー プール データをダンプすることと、データベースの起動時にディスクにバックアップbuffer poolデータをロードすることを制御します。

これで、MySQL で limit を使用するとパフォーマンスに影響する理由に関するこの記事は終了です。MySQL で limit を使用するとパフォーマンスにどのような影響があるかの詳細については、123WORDPRESS.COM の以前の記事を検索するか、次の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

以下もご興味があるかもしれません:
  • MySql ページングで limit+order by を使用する場合のデータ重複の解決策
  • 制限を使用すると、MySQL のページングがどんどん遅くなるのはなぜですか?
  • MySQL 最適化 query_cache_limit パラメータの説明
  • MySQLのorder byとlimitを混在させる際の落とし穴の詳細な説明
  • 大きなオフセットによる MySQL 制限ページングが遅い理由と最適化ソリューション
  • MySQL のソートとページング (order by と limit) と既存の落とし穴
  • MySQL は制限を使用してページング例メソッドを実装します
  • MySQLの制限を使用して大規模なページングの問題を解決する方法
  • MySQLのページング制限のパフォーマンス問題についての簡単な説明
  • MySQL の制限パフォーマンス分析と最適化
  • MySQL クエリにおける LIMIT の大きなオフセットによって引き起こされるパフォーマンス低下の分析

<<:  Zabbix カスタム監視 nginx ステータス実装プロセス

>>:  ブラウザが登録できるイベントの概要

推薦する

Tomcat の静的ページ (html) で中国語の文字化けが発生する問題の究極の解決策

tomcatでは、jspは文字化けしませんが、htmlの中国語は文字化けします理由はいくつかあります...

Navicat Premiumを使用してMySQLデータベースにリモート接続する方法

新しい接続を作成する側がクライアントに相当し、接続される側がサーバーに相当します。手順は次のとおりで...

JavaScript コンソールのその他の機能

目次概要コンソールログコンソール.infoコンソール.警告コンソールエラーコンソールテーブルコンソー...

4つのReactコンポーネントにおけるDOMスタイル設定の詳細な説明

1. インラインスタイル仮想DOMにインラインスタイルを追加するには、式を使用してスタイルオブジェク...

要素に丸い境界線を追加する border-radius メソッド

border-radius:10px; /* すべての角は半径 10px で丸められます*/ bor...

Vite2.x に基づく Vue 3.x プロジェクトの実装

Vue 3.x プロジェクトの作成 npm init @vitejs/app my-vue-app ...

MySQLユーザー権限管理の詳細な説明

目次序文: 1. ユーザー権利の概要2. 実際の権限管理序文:データベースのユーザー権限管理について...

js配列のfind、some、filter、reduceの違いの詳細な説明

Array の filter、find、some、reduce メソッドの違いを区別し、使用シナリオ...

Vue の this.$router と this.$route の違いと push() メソッド

公式文書には次のように記されている。ルーターを挿入することで、どのコンポーネントでも this.$r...

JS+Canvas でダイナミックな時計効果を実現

参考までに、Canvas をベースにしたダイナミッククロックのデモを用意しました。具体的な内容は次の...

Linux を使用して時間指定ファイルが占有するディスク容量を計算する方法

スケジュールされたタスク エディターを開きます。Cent は、デフォルトで vim を使用して直接開...

nginx をベースにした Web クラスター プロジェクトをすばやく構築する方法を説明します。

目次1. プロジェクト環境2. プロジェクトの説明3. プロジェクトの手順1. インストール2. 構...

Dockerイメージをプライベートリポジトリにアップロードする方法の例

イメージは、GitHub と同様に Docker パブリック リポジトリに直接簡単にプッシュできます...

CSS での配置の使用方法の詳細な研究 (要約)

CSS における位置指定の概要position属性は英語で位置を意味し、 CSSでの主な機能は要素...

Linux インストール Apache サーバー構成プロセス

袋を用意するインストールApacheがすでにインストールされているかどうかを確認するrpm -qa ...