MySQL 最適化チュートリアル: 大規模なページングクエリ

MySQL 最適化チュートリアル: 大規模なページングクエリ

背景

基本的にバックエンド開発をしていれば、ページングの需要や機能に触れることになります。基本的に誰もが MySQL の LIMIT を使用してこれを処理しており、現在私が担当しているプロジェクトもこの方法で記述されています。しかし、データ量が増えると、LIMIT の効率は極端に低下します。この記事では、LIMIT 句の最適化について説明します。

制限の最適化

多くのビジネス シナリオではページング機能が必要であり、これは基本的に LIMIT を使用して実装されます。

テーブルを作成し、200 万件のレコードを挿入します。

# 新しいt5テーブルを作成する CREATE TABLE `t5` (
 `id` int NOT NULL AUTO_INCREMENT、
 `name` varchar(50) NOT NULL,
 `text` varchar(100) NOT NULL,
 主キー (`id`)、
 キー `ix_name` (`name`),
 キー `ix_test` (`text`)
)ENGINE=InnoDB デフォルト文字セット=utf8;

# 200万件のデータを挿入するストアドプロシージャを作成する CREATE PROCEDURE t5_insert_200w()
始める
 i INT を宣言します。
 i=1000000 を設定します。
 i<=3000000の場合
 t5(`name`,text) に挿入します。VALUES('god-jiang666',concat('text', i));
 i=i+1 を設定します。
 終了しながら;
終わり;

# ストアド プロシージャを呼び出して 200 万個のデータを挿入します call t5_insert_200w();

ページめくりが比較的少ない場合、LIMIT によってパフォーマンス上の問題は発生しません。

しかし、ユーザーが最後のページを見つける必要がある場合はどうすればよいでしょうか?

通常、次のページをクエリするために order by xxx desc などの逆 SQL を使用するのではなく、ページング クエリを実行するために順方向の順序を使用するため、すべてのページが正常にジャンプできることを確認する必要があります。

t5 から * を選択し、テキスト制限 100000、10 で並べ替えます。

このような SQL クエリ ページングを使用すると、200 万件のデータからこれらの 10 行のデータを抽出するコストが非常に高くなります。まず最初の 1,000,010 件のレコードをソートして見つけ、次に最初の 1,000,010 件のレコードを破棄する必要があります。私のMacBook Proの実行には5.578秒かかりました。

次に、上記の SQL ステートメントの実行プランを見てみましょう。

説明: select * from t5 order by text limit 1000000, 10;

実行プランから、大規模なページングの場合、テキスト フィールドにインデックスを追加しても、MySQL はインデックス スキャンを実行しないことがわかります。

これはなぜでしょうか?

MySQL インデックス (II) インデックスの設計方法に戻ると、MySQL データベースのクエリ オプティマイザはコストベースのアプローチを採用しており、クエリ コストの見積りはCPU コストIO コストに基づいていることが述べられています。

MySQL は、クエリ コストの見積もりにおいて、フル テーブル スキャンの方がインデックス スキャンよりも効率的であると判断した場合、インデックスを放棄してフル テーブル スキャンを直接実行します。

このため、大きなページを含む SQL クエリでは、フィールドがインデックス化されていても、MySQL は完全なテーブルスキャンを実行します。

次に、上記のクエリ SQL を引き続き使用して、推測を検証します。

説明: select * from t5 order by text limit 7774, 10;

説明: select * from t5 order by text limit 7775, 10;

上記の実験はすべて私の MBP で実行されました。7774 の重要なポイントでは、MySQL はそれぞれインデックス スキャンとフル テーブル スキャンのクエリ最適化方法を使用しました。

したがって、MySQL は独自のコスト クエリ オプティマイザに基づいてインデックスを使用するかどうかを決定すると想定できます。

MySQL のクエリ オプティマイザのコア アルゴリズムに手動で介入することはできないため、最適化戦略では、ページングを最適なページング クリティカル ポイントで維持する方法に重点を置く必要があります。

最適化方法

1. カバーインデックスを使用する

SQL ステートメントがクエリのためにテーブルに戻らずにインデックスを通じてクエリ結果を直接取得できる場合、このインデックスはカバーリング インデックスと呼ばれます。

実行プランを表示するには、MySQL データベースで explain キーワードを使用します。追加の列に「Using index」と表示される場合、この SQL ステートメントはカバーリング インデックスを使用していることを意味します。

カバーリングインデックスを使用することでパフォーマンスがどの程度向上するか比較してみましょう。

# カバーインデックスは使用されません select * from t5 order by text limit 1000000, 10;

このクエリには 3.690 秒かかりました。カバーリング インデックスの最適化を使用することでパフォーマンスがどの程度向上するかを見てみましょう。

# カバーリングインデックスを使用して、 id、`text` を t5 から order by text limit 1000000、10 で選択します。

上記の比較から、超大規模なページングクエリでは、カバーリングインデックスを使用した後は 0.201 秒かかりましたが、カバーリングインデックスを使用しない場合は 3.690 秒かかり、18 倍以上高速化されています。実際の開発では、これは大きなパフォーマンスの最適化です。 (このデータはMBPで実行して取得しました)

2. サブクエリの最適化

実際の開発では、1 つまたは 2 つの列を照会する SELECT 操作は非常にまれであるため、上記のカバー インデックスの適用範囲は比較的限られています。

したがって、ページング SQL ステートメントをサブクエリに書き換えることでパフォーマンスを向上させることができます。

t5 から * を選択します。id>=(t5 から id を選択、テキストで順序を指定、制限 1000000、1)、制限 10。

実際、この方法を使用することによって達成される効率改善は、上記のカバーリング インデックスを使用することによって達成される効率改善と基本的に同じです。

ただし、この最適化方法にも制限があります。

  • この書き込み方法では、主キーIDが連続している必要があります。
  • Where句では他の条件を追加することはできません

3. 遅延連合

上記のサブクエリのアプローチと同様に、JOIN を使用して最初にインデックス列のページング操作を完了し、次にテーブルに戻って必要な列を取得できます。

t5 から a.* を選択し、内部結合します (t5 から id を選択し、テキスト制限 1000000、10 で順序付けします)。b は a.id=b.id になります。

実験から、JOIN を使用して書き換えた後は、上記 2 つの制限が解除され、SQL 実行効率が失われていないことがわかります。

4. 最後のクエリが終了した場所を記録する

上記で使用した方法とは異なり、最後の終了位置を記録する最適化の考え方は、変数を使用して最後のデータの位置を記録し、次のページング中にこの変数の位置から直接スキャンを開始することです。これにより、MySQL が大量のデータをスキャンしてから破棄することを回避できます。

id>=1000000 の t5 から * を選択し、制限を 10 にします。

上記の実験に基づくと、ページング操作に主キー インデックスを使用しているため、SQL のパフォーマンスが最も高速であると結論付けるのは難しくありません。

要約する

  • 大規模なページングクエリのパフォーマンスが低下する理由を紹介し、いくつかの最適化のアイデアを共有しました。
  • 超大規模ページングの最適化の考え方は、完全なテーブルスキャンをトリガーせずに、ページング SQL を可能な限り最高のパフォーマンス範囲で実行することです。
  • 上記の共有が、MySQL の道で迂回を避けるのに役立つことを願っています~~~

参考文献

  • 「MySQL パフォーマンス最適化」の第 6 章 - クエリ最適化パフォーマンス
  • データベースクエリオプティマイザーの技術

これで、大規模ページング クエリに関する MySQL 最適化チュートリアルに関するこの記事は終了です。MySQL 大規模ページング クエリに関する関連コンテンツについては、123WORDPRESS.COM で以前の記事を検索するか、次の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

以下もご興味があるかもしれません:
  • 数百万のデータに対するMySQLラージページクエリ最適化の実装
  • MySQLでページングクエリを実装する方法
  • 複数の無関係なテーブルからデータをクエリし、MySQL でページングする方法
  • MySQL ページングクエリ最適化テクニック
  • MySQLクエリのソートとページング関連
  • MySQLを使用してページングクエリを実装する方法

<<:  TeamCenter12 にログインする際の 404/503 問題の解決方法

>>:  Vueはビデオ再生を実装するためにビデオタグを使用します

推薦する

CentOS8 で Docker を使用してオープンソース プロジェクト Tcloud をデプロイするチュートリアル

1. Dockerをインストールする1. 仮想マシンに Centos7 をインストールしました。Li...

CSSは複数の要素をボックスの両端に揃える効果を実現します

要素の両端を揃える配置レイアウトは、実際の開発のいたるところで見られます。これは、フレックスレイアウ...

計算機機能を実装するミニプログラム

この記事の例では、計算機機能を実装するためのミニプログラムの具体的なコードを参考までに共有しています...

CSS3は赤い封筒を振る効果を実現します

赤い封筒の揺れ効果を実現するには要件があります。これまでやったことがないので、記録しておきます。ヘヘ...

Vue3 の使用 (パート 1) Vue CLI プロジェクトの作成

目次1. 公式ドキュメント2. Vue CLIプロジェクトを作成する1. Vue CLIをインストー...

Typescriptを使用してWeChatミニプログラムでプロジェクトを作成する方法

プロジェクトを作成するWeChat開発者ツールでプロジェクトを作成し、言語でTypeScriptを選...

Dockerはnginxをデプロイし、フォルダとファイル操作をマウントします

この間、私は docker を勉強していたのですが、nginx をデプロイするときに行き詰まりました...

IE 環境での css-vars-ponyfill の使用に関する詳細な説明 (nextjs ビルド)

css-vars-ポニーフィルCSS 変数を使用して Web ページのスキニングを実現すると、互換...

MySQLはカスタム関数を使用して親IDまたは子IDを再帰的に照会します

背景: MySQL では、レベルに制限がある場合、たとえば、ツリーの最大深度を事前に決定できる場合、...

IE6かどうかを判定する最短JS(IEの書き方)

ブラウザが IE のどのバージョンであるかを検出するためによく使用される JavaScript コー...

HTML テーブルタグチュートリアル (45): テーブル本体タグ

<tbody> タグは、テーブル本体のスタイルを定義するために使用されます。基本構文 &...

Linux ディスクのマウント、パーティション分割、容量拡張操作を実装する方法

基本概念操作の前に、まずいくつかの基本的な概念を理解する必要がありますディスクLinux システムで...

MySQL Order By 複数フィールドのソートルールのコード例

事前に言っておく気まぐれですが、MySQL の order by sorting にどのようなルール...

Vue のスロットとフィルターの詳細な説明

目次スロットスロットとは何ですか?スロットの内容コンパイルスコープフォールバックコンテンツ名前付きス...

docker を使用して minio と java sdk を構築するプロセスの詳細な説明

目次1minioはシンプル2 Dockerビルド minio 2.1 単一ノード2.2 マルチノード...