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) を使用する方法
CSS でテキストアイコンを実装する方法 /*アイコンスタイル*/ .nav-icon-norma...
元のコードは次のとおりです。 <div class='コントロールグループ'&...
目次1. Dockerをインストールする2. ソナーイメージをインストールする3. ソナーを使ってコ...
MySQL 5.5 の場合、文字セットが設定されていない場合、MySQL のデフォルトの文字セットは...
目次序文文章1. グローバル登録2. 部分登録3. フック機能とパラメータ設定4. 柔軟な使い方(1...
目次ルートの場所が見つかりませんオフバイスラッシュ安全でない変数の使用スクリプト名$uri を使用す...
この記事では、実際に発生した問題をもとに、git の設定に関する内容を紹介します。コマンド: git...
プロジェクトを開発しているとき、支払い済み、支払済み、クローズ済み、返金済みなどの注文ステータスなど...
最近、社内文書の整理とファイルサーバーの構成を予定しています。以前はサーバー2003を使い慣れていま...
一般的な Dockerfile 命令の紹介命令説明するから新しいイメージが構築される基となるイメージ...
結果:実装コード: <!DOCTYPE html><html class=&quo...
目次概要フロントエンド知識システムフロントエンドの3つの要素プレゼンテーション層 (CSS)動作レイ...
Docker の基本的な操作を学習した後、コンテナにいくつかの基本的なアプリケーションをデプロイして...
目次制御されていないコンポーネント制御コンポーネント知らせ結論は制御されていないコンポーネントフォー...
この記事では、グラフィック認証コードログインを実装するためのVueの具体的なコードを参考までに紹介し...