数百万のデータに対して 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関数導入の詳しい説明

推薦する

vsftp を使用して Linux で FTP サーバーを構築する (パラメータの説明付き)

導入この章では、主に Linux で FTP サーバーを構築するプロセスを紹介します。習得すべき重要...

WeChatアプレットがユーザーの移動軌跡を記録

目次設定を追加json 構成レイヤー構成の表示論理層の構成位置追跡をオンにする録音を開始開始座標を決...

Nginx に lua-nginx-module モジュールをインストールする方法

ngx_lua_module は、lua パーサーを nginx に埋め込み、lua 言語で記述され...

Vueのフィルターについて知っておくべきこと

目次序文フィルターとは何かフィルターの使い方グローバルフィルターローカルフィルターフィルターは直列に...

ユーザーエクスペリエンスの76の経験ポイントの要約

ウェブサイト体験の分類1. 感覚体験:快適性を重視した視聴覚体験をユーザーに提供します。 2. イン...

Mysql の mysql.user ユーザー テーブルの詳細な説明

MySQL は、異なるユーザーに異なる権限を割り当てることができるマルチユーザー管理データベースであ...

高性能な HTML アプリケーションを作成するためのヒント

Web ページのパフォーマンスを向上させるにはどうすればよいでしょうか?ほとんどの開発者は、Java...

ウェブデザインと制作に関する科学的原則と提案の要約

<br />ネットワーク設計の分野では、アイトラッキングに関する研究が非常に盛んに行われ...

CSS3 で transform を使用した場合のフォントぼかしの解決方法の詳細な説明

この質問は非常に奇妙なので、あまり多くを語らずにコードに直接進みます。 .g-ダイアログラッパー{ ...

Vueが学生管理機能を実装

この記事では、学生管理を実装するためのVueの具体的なコードを例として紹介します。具体的な内容は次の...

Vueはランニングライトのシンプルな効果を実現

この記事では、マーキーのシンプルな効果を実現するためのVueの具体的なコードを参考までに共有します。...

HTML の隠しフィールドの紹介と例

基本的な構文: <input type="hidden" name=&qu...

MySQLのマスタースレーブレプリケーションと読み取り書き込み分離を理解するための記事

目次導入1. MySQL マスタースレーブレプリケーション1. MySQLレプリケーションタイプ2....

開発効率の向上に役立つ 56 個の実用的な JavaScript ツール関数

目次1. デジタルオペレーション(1)指定された範囲内で乱数を生成する2. 配列操作(1)配列の順序...

CSS エラスティック ボックス flex-grow、flex-shrink、flex-basis の詳細な説明

3 つの属性 flex-grow、flex-shrink、flex-basis の機能は次のとおりで...