I. はじめにまず、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 | | | | ソース | int(10) 符号なし | NO | | | | +--------+----------------------+------+-----+---------+----------------+ セット内の 3 行 (.00 秒) id は自動インクリメントの主キーであり、val は一意でないインデックスです。 合計500万件の大量のデータを投入します。 mysql> テストから count(*) を選択します。 +----------+ | カウント(*) | +----------+ |5242882| +----------+ セット1列(4.25秒) 制限オフセット行のオフセットが大きい場合、効率の問題が発生することが分かっています。 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; のクエリ プロセスを見てみましょう。 インデックス リーフ ノード データが照会されます。 リーフ ノードの主キー値に基づいて、クラスター化インデックス上のすべての必須フィールド値をクエリします。 次の図のようになります。 上記のように、インデックス ノードを 300,005 回クエリし、クラスター化インデックス データを 300,005 回クエリし、最後に最初の 300,000 件の結果をフィルターして最後の 5 件を取り出す必要があります。 MySQL は、クラスター化インデックスのデータをクエリするために大量のランダム I/O を費やし、300,000 回のランダム I/O によってクエリされたデータは結果セットに表示されません。 誰かが必ずこう尋ねるでしょう: インデックスは最初に使用されるので、最初にインデックス リーフ ノードに沿って必要な最後の 5 つのノードまでクエリを実行し、次にクラスター化インデックス内の実際のデータをクエリするのはなぜですか。これには、次の図のプロセスと同様に、5 つのランダム I/O のみが必要です。 実は私もこの質問をしたいんです。 確認上記の推論を確認するために実際にいくつかの操作を実行してみましょう。 私はこれを間接的にしか確認できません: InnoDB にはバッファプールがあります。データ ページやインデックス ページなど、最近アクセスされたデータ ページが含まれます。したがって、バッファー プール内のデータ ページの数を比較するには、2 つの SQL ステートメントを実行する必要があります。予測結果では、 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 秒) 現在、バッファー プール内にテスト テーブルに関するデータ ページが存在しないことがわかります。 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秒) この時点で、バッファー プールにはテスト テーブルのデータ ページが 4098 ページ、インデックス ページが 208 ページあることがわかります。 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 のデータ ページをバッファー プールにロードしますが、2 番目の SQL は 5 つのデータ ページのみをバッファー プールにロードします。私たちの予測通りです。これにより、最初の SQL ステートメントが遅い理由も確認できます。大量の役に立たないデータ行 (300,000) を読み取ってから破棄します。 そして、これは問題を引き起こします。あまりホットではないデータ ページを大量にバッファー プールにロードすると、バッファー プールの汚染が発生し、バッファー プールのスペースが占有されます。 発生した問題再起動のたびにバッファー プールがクリアされるようにするには、innodb_buffer_pool_dump_at_shutdown と innodb_buffer_pool_load_at_startup をオフにする必要があります。これら 2 つのオプションは、データベースのシャットダウン時にバッファー プール データをダンプすることと、データベースの起動時にディスクにバックアップ バッファー プール データをロードすることを制御します。 参考文献: 1. https://explainextended.com/2009/10/23/mysql-order-by-limit-performance-late-row-lookups/ 2. https://dev.mysql.com/doc/refman/5.7/en/innodb-information-schema-buffer-pool-tables.html SQL実行効率の詳細については、以下の関連記事をご覧ください。 以下もご興味があるかもしれません:
|
<<: JS の FileReader を介して .txt ファイルの内容を取得する方法
>>: HTML で dl(dt,dd)、ul(li)、ol(li) を使用する方法
序文これは、オンライン コンテナーの拡張によって発生した調査です。最終的には、実際の OOM が原因...
シナリオ 1: HTML: <div class="outer"> ...
一般的な CSS コードでは、UI レイアウトや互換性に関して軽微な問題が発生するだけです。しかし、...
目次1. Angular 2 アプリケーションのライフサイクル フックとは何ですか? 2. Angu...
この記事では、カードフリップ効果を実現するためのVueの具体的なコードを例として紹介します。具体的な...
目次背景問題の説明原因分析CPUクエリが遅い接続数分析する拡大する総括する背景新年を迎える前は、一年...
inline-block について学習しているときに、境界線と inline-block を持つ複数...
この記事の例では、すべてのチェックボックスの選択を実現するためのJavaScriptの具体的なコード...
ウェブサイトを最適化するときは、エラー ページの使い方を学ぶ必要があります。たとえば、ウェブサイトに...
ソフィー・ハルダッククライドキー埠頭 37 東ソープボックス Rxバランス EEハーバー対比 SVN...
この記事では、ウィンドウ表示効果を実現するためのJavaScriptの具体的なコードを参考までに紹介...
MySQL パーティションテーブルの概要MySQL の人気が高まるにつれて、MySQL に保存される...
序文プロジェクト内のパズルゲーム(デジタル華容路とも呼ばれる)を再構築するのに 2 日かかりました。...
ドッカーの作成 バージョン: '2' サービス: fastdfsトラッカー: ホスト...
コードをコピーコードは次のとおりです。 <hr style="width:490px...