MySQL ディープ ページング (数千万のデータを素早くページ分割する方法)

MySQL ディープ ページング (数千万のデータを素早くページ分割する方法)

序文

バックエンド開発では、一度に大量のデータがロードされ、メモリやディスク IO のオーバーヘッドが過剰になることを防ぐために、ページング表示が必要になることがよくあります。このとき、MySQL の LIMIT キーワードが必要になります。しかし、LIMIT ページングですべてがうまくいくと思いますか? まだ若すぎて単純すぎます。データ量が多い場合、LIMIT が引き起こす可能性のある問題の 1 つは、ディープ ページングです。

場合

ここでは、電子商取引の注文詳細の表示を例にとります。新しいテーブルは次のようになります。

テーブル `cps_user_order_detail` を作成します (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主キー',
  `user_id` varchar(32) NOT NULL DEFAULT '' COMMENT 'ユーザーID',
  `order_id` bigint(20) デフォルト NULL コメント '注文ID',
  `sku_id` bigint(20) unsigned NOT NULL COMMENT '製品ID',
  `order_time` datetime DEFAULT NULL COMMENT '注文時間、形式 yyyy-MM-dd HH:mm:ss',
   主キー (`id`)、
   キー `idx_time_user` (`order_time`,`user_id`) BTREE の使用
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='ユーザー注文詳細';

次に、120 万件のレコードを手動でテーブルに挿入します。
ここで、ユーザーの注文詳細を注文時間の逆順にページに表示するという要件があります。
テーブル構造は合理化されており、要件はシンプルです。そこで、すぐにコードを書き終えて、テスト用にオンラインに公開しました。当初はすべて正常に動作していましたが、注文量が増え続けるにつれて、システムは次第に遅くなり、いくつかの慢查詢時々報告されました。
この時点で、これは LIMIT オフセットの問題であると考えるべきです。そうです、SQL が十分に美しくないのではなく、MySQL 自体のメカニズムの問題なのです。
ここでは、次の図に示すように、それぞれ 100 と 100 万の位置オフセットからページングする 2 つの SQL ステートメントを例として取り上げます。時間差が非常に大きいことがわかります。これには、他のデータの計算や処理にかかる時間は含まれません。1 回の SQL クエリには 1 秒以上かかるため、ユーザーに提供される機能では許容できません (電子商取引では、インターフェイスの RT が 200 ミリ秒を超えないことが求められることがよくあります)。

ここでは、以下に示すように実行プランを見てみましょう。

ここではまず、実行プランの Extra 列の可能な値と意味を紹介します。

  1. where: を使用すると、オプティマイザーはインデックスを介してテーブルにデータをクエリする必要があることを示します。
  2. インデックスの使用: カバーリング インデックスとは、インデックスを介してテーブルに戻らなくても、インデックスに直接アクセスするだけで必要なデータを取得できることを意味します。これは通常、クエリ対象のフィールドの結合インデックスを作成することで実現されます。
  3. インデックス条件の使用: バージョン 5.6 以降に追加された新しい機能である有名なインデックス プッシュダウンは、減少回表次數ための MySQL の主要な最適化です。
  4. filesort の使用: ファイルのソート。これは通常、ORDER BY 中に行われます。データの量が多すぎる場合、MySQL はソートのためにすべてのデータをメモリに呼び戻すため、より多くのリソースが消費されます。

上の図を見ると、オフセットが異なるだけで、同じステートメントでも実行プランが大きく異なっていることがわかります (少し誇張して表現します)。最初のステートメントLIMIT 100,6 type 列の値がrangeであり、これは範囲スキャンを示しています。 refもパフォーマンスが 1 レベル低くなりますが、インデックスを使用することも考慮されており、インデックス プッシュダウンも適用されます。つまり、WHERE 後の ORDER BY 時にインデックスが削除および選択され、後続の ORDER BY もインデックス プッシュダウンに基づいて最適化され、WHERE 条件がフィルタリングされるときに同期的に実行されます (テーブルに戻らずに)。
2 番目のステートメントLIMIT 1000000,6インデックスをまったく使用せず、 type 列の値はALLであり、明らかに完全なテーブルスキャンです。 Extra 列の「Using where」はテーブルの戻りが発生することを示し、「Using filesort」は ORDER BY 中にファイルの並べ替えが発生することを示します。ここで速度が遅くなる理由は 2 つあります。1 つ目は、ファイルのソートに時間がかかりすぎること、2 つ目は、条件に従って関連データをフィルタリングした後、オフセットに基づいてテーブルに戻り、すべての値を取得する必要があることです。上記のいずれの点においても、LIMIT オフセットが大きすぎることが原因であるため、実際の開発環境では、非統計テーブルレベルが 100 万を超えてはならないという要件に遭遇することがよくあります。

最適化

原因が分析されたので、実際の開発で LIMIT ディープ ページングを最適化するにはどうすればよいでしょうか。ここで 2 つの解決策を紹介します。
1 つは主キー インデックスの最適化です。それはどういう意味ですか?上記の文を次のように変更します。

SELECT * FROM cps_user_order_detail d WHERE d.id > #{maxId} AND d.order_time>'2020-8-5 00:00:00' ORDER BY d.order_time LIMIT 6;

上記のコードに示されているように、これもページ分割されていますが、maxId 制限があります。これは何を意味するのでしょうか。maxId は、前のページの最大の主キー ID です。したがって、この方法を使用する前提は次のとおりです。1) 主キーは自動増分である必要があり、UUID にすることはできません。また、基本的なページング パラメータ pageNo、pageSize を渡すだけでなく、フロント エンドは前の各ページの最大 ID も取得する必要があります。2) この方法はランダムなページ ジャンプをサポートしていません。つまり、ページを上下に移動することしかできません。次の図は、有名な電子商取引会社の実際のページを示しています。

2 つ目は、Elastic Search 検索エンジン最適化 (転置インデックスに基づく) です。実際、Taobao などの電子商取引企業は、基本的にすべての製品を ES 検索エンジンに入れています (このような膨大なデータを MySQL に入れるのは不可能であり、Redis に入れるのは現実的ではありません)。しかし、ES 検索エンジンを使用しても、ディープ ページングの問題が発生する可能性があります。その場合はどうすればよいでしょうか?答えはカーソルスクロールを通じてです。この点についてはここでは詳しく説明しません。興味のある方は調べてみてください。

まとめ

このブログを書いたのは、以前開発中に実際にそれを経験し、Byte のインタビューで面接官とそれについて話し合ったからです。 LIMIT の制限と最適化について知っていると、面接でそのことを伝えることができればプラスになります。MySQL の最適化はインデックスの構築と SQL の調整だけだと言わないでください (実際、実際の開発では、これら 2 つの最適化ソリューションの効果は最小限です)。結局のところ、MySQL の最適化がそれほど素晴らしいのであれば、ミドルウェアはそれほど多くないはずです。

これで、MySQL ディープ ページング (数千万のデータを素早くページ分割する方法) に関するこの記事は終了です。MySQL ディープ ページングの詳細については、123WORDPRESS.COM の以前の記事を検索するか、以下の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

以下もご興味があるかもしれません:
  • MySQL ディープページング問題の解決の実践記録

<<:  ページのレンダリング時間を短縮してページの実行速度を速めます

>>:  Git サーバーを使用してデバッグ ブランチを表示し、修正する方法を 1 日 1 分で学習します。

推薦する

CSS でよく使用されるフォントサイズ、フォント単位、行の高さの詳細な説明

px(ピクセル)ピクセルという言葉は皆さんもよくご存知だと思います。次に、この単位に関するちょっとし...

js配列の基本的な使い方のまとめ

序文配列は特別な種類のオブジェクトです。 js には実際の配列はなく、オブジェクトを使用して配列をシ...

Vue で Graphql インターフェースを実装する例

注意:この記事は現在取り組んでいる nestjs+graphql+serverless 合宿における...

CentOS7.5 の MySQL8.0.19 のインストールチュートリアルの詳細な手順

1. はじめにこの記事には MySQL インストール部分のスクリーンショットがないので、ある程度の基...

IE ブラウザの HTML ハック タグの概要

コードをコピーコードは次のとおりです。 <!--[if !IE]><!-->...

SQL で行の最大値または最小値を取得する方法

元データと対象データSQL文を実装する(最大) 選択 店、 月、 最大(dz,fz,sp) が最大値...

HTML テーブル タグ チュートリアル (31): セルの幅と高さの属性 WIDTH、HEIGHT

デフォルトでは、セルの幅と高さはコンテンツに応じて自動的に調整されますが、セルの幅と高さを手動で設定...

Vue3はJingdong製品詳細ページの虫眼鏡効果コンポーネントをカプセル化します

この記事では、Jingdong製品詳細ページの虫眼鏡効果コンポーネントに似たvue3カプセル化の具体...

CocosCreator でカメラトラッキングに cc.follow を使用する方法

Cocos Creator バージョン: 2.3.4デモのダウンロード: https://files...

ネイティブ Js で実装されたシンプルなシームレス スクロール カルーセルのサンプル コード

シンプルなシームレススクロールカルーセルには多くの抜け穴があり、後から画像を追加するのは非常に不便で...

Docker Toolboxを完全にアンインストールする方法

Docker Toolbox は、Windows 10 Professional より前のバージョン...

Vue3 非同期データ読み込みコンポーネントサスペンスの使い方

目次序文コンポーネントの作成要約する序文Vue3 には多くの注目すべき機能が追加されましたが、サスペ...

JavaScript実行メカニズムの詳細な紹介

目次1. プロセスとスレッドの概念2. ブラウザの原則3. 同期と非同期4. 実行スタックとタスクキ...

互換性を維持しながら他のウェブページのデータを適用する iframe の使い方

以下は、Shiji Tiancheng が Tencent KartRider ページを呼び出すため...

HTML ベース URL タグ

その機能はグローバル スタイルを設定することです。その後の相対パスはこれに基づきます: <im...