数百万のデータに対して MySQL クエリを最適化する 4 つの方法

数百万のデータに対して MySQL クエリを最適化する 4 つの方法

1. 時間が経つにつれて限界が遅くなる理由

ページング データに制限を使用すると、最初の数ページを表示するときに速度が非常に速くなることがわかります。たとえば、制限を 200,25 にすると、瞬時に表示されます。しかし、時間が経つにつれて速度がどんどん遅くなり、特に 100 万件を超えると、非常に停止してしまいます。この背後にある原理は何ですか?まず、ページを戻したときにクエリ SQL がどのようになるかを見てみましょう。

c_name1='xxx' の場合、t_name から * を選択し、c_name2 で順序付けし、制限 2000000,25 にします。

このクエリの速度低下は、実際には制限後のオフセットが大きいことが原因です。たとえば、上記の制限 2000000,25 は、データベースが 2000025 個のデータをスキャンし、最初の 20000000 個のデータを破棄し、残りの 25 個のデータをユーザーに返すことに相当します。このアプローチは明らかに不合理です。

2. 百万データシミュレーション

1. 従業員テーブルと部門テーブルを作成し、データを挿入するためのストアドプロシージャを作成します。

/*Department テーブル、存在する場合は削除*/
存在する場合はテーブルを削除します。
テーブル dep を作成します(
    id int unsigned 主キー auto_increment,
    depno mediumint unsigned not null デフォルト 0,
    depname varchar(20) NULLでないデフォルト "",
    メモ varchar(200) null ではない デフォルト ""
);

/*従業員テーブル、存在する場合は削除*/
emp が存在する場合はテーブルを削除します。
テーブルempを作成します(
    id int unsigned 主キー auto_increment,
    empno mediumint unsigned not null デフォルト 0,
    empname varchar(20) NULLでないデフォルト "",
    ジョブvarchar(9) NULLでないデフォルト "",
    mgr mediumint unsigned not null デフォルト 0,
    hiredate datetime が null ではありません。
    sal 小数点(7,2) は null ではありません。
    comn 10進数(7,2) は null ではありません。
    depno mediumint unsigned not null デフォルト 0
);
/* ランダムな文字列を生成する関数 */
区切り文字 $
rand_stringが存在する場合はFUNCTIONを削除します。
CREATE FUNCTION rand_string(n INT) は VARCHAR(255) を返します。
始める
    chars_str VARCHAR(100) をデフォルト 'abcdefghijklmlopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' として宣言します。
    return_str VARCHAR(255) DEFAULT '' を宣言します。
    i INT DEFAULT 0 を宣言します。
    i < n ながら
    return_str = CONCAT(return_str,SUBSTRING(chars_str,FLOOR(1+RAND()*52),1)) を設定します。
    i = i+1 を設定します。
    終了しながら;
    戻り値 return_str;
終了 $
デリミタ;


/*ランダムな部門番号を生成する関数*/
区切り文字 $
rand_numが存在する場合はFUNCTIONを削除します。
CREATE FUNCTION rand_num() は INT(5) を返します。
始める
    i INT DEFAULT 0 を宣言します。
    i = FLOOR(100+RAND()*10) を設定します。
    i を返します。
終了 $
デリミタ;
/*ストアド プロシージャを作成: emp テーブルにデータを挿入します*/
区切り文字 $
存在する場合は PROCEDURE を削除します insert_emp;
CREATE PROCEDURE insert_emp(IN START INT(10),IN max_num INT(10))
始める
    i INT DEFAULT 0 を宣言します。
    /*set autocommit =0 自動コミットを 0 に設定し、デフォルトのコミットをオフにします*/
    自動コミットを 0 に設定します。
    繰り返す
    i = i + 1 を設定します。
    emp(empno,empname,job,mgr,hiredate,sal,comn,depno) に INSERT INTO VALUES ((START+i),rand_string(6),'SALEMAN',0001,now(),2000,400,rand_num());
    i = max_numになるまで
    繰り返し終了;
    専念;
終了 $
デリミタ;

/*ストアド プロシージャを作成: dep テーブルにデータを挿入します*/
区切り文字 $
存在する場合は PROCEDURE を削除します insert_dept;
CREATE PROCEDURE insert_dept(IN START INT(10), IN max_num INT(10))
始める
    i INT DEFAULT 0 を宣言します。
    自動コミットを 0 に設定します。
    繰り返す
    i = i+1 を設定します。
    dep( depno, depname, memo) に挿入 VALUES((START+i),rand_string(10),rand_string(8));
    i = max_numになるまで
    繰り返し終了;
    専念;
終了 $
デリミタ;

2. ストアドプロシージャを実行する

/*120件のレコードを挿入*/
insert_dept(1,120)を呼び出します。
/*500万個のデータを挿入*/
insert_emp(0,5000000)を呼び出します。

500万件のレコードを挿入すると遅くなる可能性がある

3.4 クエリメソッド

1. 通常の制限ページング

/*オフセットは100なので、25を取る*/
a.empno、a.empname、a.job、a.sal、b.depno、b.depname を選択
emp a から左結合 dep b を a.depno = b.depno で、order by a.id desc limit 100,25 で結合します。
/*オフセットは 4800000 なので 25 を取る*/
a.empno、a.empname、a.job、a.sal、b.depno、b.depname を選択
emp a から、左結合 dep b を a.depno = b.depno で、order by a.id desc limit 4800000,25 で結合します。

実行結果

[SQL]
a.empno、a.empname、a.job、a.sal、b.depno、b.depname を選択
emp a から左結合 dep b を a.depno = b.depno で、order by a.id desc limit 100,25 で結合します。
影響を受ける行: 0
時間: 0.001秒
[SQL]
a.empno、a.empname、a.job、a.sal、b.depno、b.depname を選択
emp a から、左結合 dep b を a.depno = b.depno で、order by a.id desc limit 4800000,25 で結合します。
影響を受ける行: 0
時間: 12.275秒

先に進むほど、クエリの効率は低下します。

2. インデックスカバレッジ+サブクエリ最適化を使用する

主キー ID があり、それに基づいてインデックスを構築しているため、最初にインデックス ツリー内の開始位置の ID 値を見つけ、次に見つかった ID 値に基づいて行データをクエリできます。

/*サブクエリは 100 オフセットされた位置の ID を取得し、この位置の後の 25 を取得します*/
a.empno、a.empname、a.job、a.sal、b.depno、b.depname を選択
emp a から、a.depno = b.depno で dep b を左結合します。
ここで、a.id >= (emp から id を選択し、id 制限で 100,1 を注文)
a.id 制限 25 で順序付けします。

/*サブクエリは 4800000 オフセットの位置の ID を取得し、この位置の 25 を取得します*/
a.empno、a.empname、a.job、a.sal、b.depno、b.depname を選択
emp a から、a.depno = b.depno で dep b を左結合します。
ここで、a.id >= (emp から id を選択し、id 制限で 4800000,1 を注文)
a.id 制限 25 で順序付けします。

実行結果

[SQL]
a.empno、a.empname、a.job、a.sal、b.depno、b.depname を選択
emp a から、a.depno = b.depno で dep b を左結合します。
ここで、a.id >= (emp から id を選択し、id 制限で 100,1 を注文)
a.id 制限 25 で順序付けします。
影響を受ける行: 0
時間: 0.106秒

[SQL]
a.empno、a.empname、a.job、a.sal、b.depno、b.depname を選択
emp a から、a.depno = b.depno で dep b を左結合します。
ここで、a.id >= (emp から id を選択し、id 制限で 4800000,1 を注文)
a.id 制限 25 で順序付けします。
影響を受ける行: 0
時間: 1.541秒

3. 開始位置を再定義する

主キーが自動増分であるテーブルに適用可能

/*前回のページングの最後のデータの ID は 100 なので、100 をスキップして 101 からテーブルをスキャンします */
a.id、a.empno、a.empname、a.job、a.sal、b.depno、b.depname を選択
emp a から、a.depno = b.depno で dep b を左結合します。
a.id > 100 の場合、a.id で順序付けし、制限を 25 にします。

/*前回のページングの最後のデータの ID は 4800000 なので、4800000 をスキップして 4800001 からテーブルをスキャンします */
a.id、a.empno、a.empname、a.job、a.sal、b.depno、b.depname を選択
emp a から、a.depno = b.depno で dep b を左結合します。
a.id > 4800000の場合
a.id 制限 25 で順序付けします。
[SQL]
a.id、a.empno、a.empname、a.job、a.sal、b.depno、b.depname を選択
emp a から、a.depno = b.depno で dep b を左結合します。
a.id > 100 の場合、a.id で順序付けし、制限を 25 にします。
影響を受ける行: 0
時間: 0.001秒

[SQL]
a.id、a.empno、a.empname、a.job、a.sal、b.depno、b.depname を選択
emp a から、a.depno = b.depno で dep b を左結合します。
a.id > 4800000の場合
a.id 制限 25 で順序付けします。
影響を受ける行: 0
時間: 0.000秒

これが最も効率的です。条件を実行した後、25 個のデータのみがスキャンされるため、ページがどのように分割されても、消費される時間は基本的に同じです。

4. ダウングレード戦略(百度のアプローチ)

この戦略は最もシンプルで効果的です。一般的なビッグデータクエリには検索条件があり、100 ページ以降のコンテンツには誰も注目しないからです。ユーザーがクエリするページ数が多すぎる場合は、エラーを返すだけです。たとえば、Baidu は 76 ページまでしか検索できません。

上記は、MySQL の何百万ものデータに対する 4 つのクエリ最適化方法の詳細です。何百万ものデータに対する MySQL クエリ最適化の詳細については、123WORDPRESS.COM の他の関連記事に注目してください。

以下もご興味があるかもしれません:
  • MySQLは数百万のシミュレーションデータ操作コードを自動的に挿入します
  • MySQL 単一テーブル 100 万データ レコード ページング パフォーマンス最適化スキル
  • MySQLに何百万ものテストデータを素早く挿入する方法
  • 10秒以内にMySQLデータベースに数百万件のレコードを挿入する実装

<<:  ウェブページの背景色を制御する CSS コード

>>:  JavaScript関数導入の詳しい説明

推薦する

HTML テーブル マークアップ チュートリアル (15): テーブル タイトル

<br />このタグを使用すると、表のタイトルを直接追加し、タイトル テキストの配置プロ...

Ubuntu 16.04 サーバーで MySQL を設定し、リモート接続を有効にする方法

背景最近、Node.js を勉強しているのですが、クラウド サーバーがあることを思い出しました。しか...

jQuery プロジェクトで重複送信を防ぐ方法

新しいプロジェクトでは、axios によって重複した送信を防ぐことができますが、古いプロジェクト (...

CSSのさまざまな背景、使用シナリオ、テクニックの詳細な分析

この記事はGitHub https://github.com/qq449245884/xiaozhi...

Node.jsで子プロセスを作成する方法

目次導入子プロセスプロセスを非同期的に作成する同期作成プロセス導入Node.js のメイン イベント...

LinuxにMySQLをインストールするための詳細なチュートリアル

すべてのプラットフォーム用の MySQL ダウンロードは、MySQL ダウンロードから入手できます。...

CentOS 7.2 に SuPHP をインストールするための詳細な手順

デフォルトでは、CentOS 7 上の PHP は apache または nobody として実行さ...

MySQLインデックスを正しく作成する方法

インデックス作成は大学図書館の書誌インデックスの構築に似ており、データ検索の効率を向上させ、データベ...

Dockerは起動されていないコンテナの設定情報を変更します

私が初めてdockerを使用したときは、dockerfileやdocker-composeを使用して...

MySQLの関連ロックについての簡単な理解

この記事は主にInnoDBのロックに関する知識を素早く理解してもらうことを目的としています。 Roc...

MySQL DISTINCTの基本実装原理の詳細な説明

序文DISTINCT は、GROUP BY 操作の実装と非常によく似ていますが、GROUP BY の...

KTLツールはMySQLからMySQLへのデータの同期方法を実現します

ktl ツールを使用して、mysql から mysql にデータを同期します。 1. 新しいジョブス...

Linux の PHP に XML 拡張機能をインストールする詳細な手順

PHP Linux に XML 拡張機能をインストールする1. PHPインストールソースパッケージを...

HTML フレームセットのサンプルコード

この記事では、Frameset が作成した、できるだけシンプルなフレームワークを紹介します。さて、ま...

JavaScript で実装された 7 つのソート アルゴリズムの概要 (推奨!)

目次序文バブルソート基本アルゴリズム2 番目の書き方は、基本的なアルゴリズムに基づいて改良されていま...