ソートを使用した MySQL ページング クエリの最適化

1. 脚本

1.1 テーブル作成スクリプト

CREATE TABLE `student` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `user_no` varchar(50) DEFAULT '' COMMENT '学号',
  `user_name` varchar(50) DEFAULT '' COMMENT '姓名',
  `score` decimal(10,2) DEFAULT NULL COMMENT '分数',
  `create_time` date DEFAULT NULL COMMENT '创建时间',
  `update_time` date DEFAULT NULL COMMENT '更新时间',
  `remark` varchar(200) DEFAULT '' COMMENT '备注',
  PRIMARY KEY (`id`),
  KEY `idx_score` (`score`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='学生表';

1.2 要件

結果を降順に並べ替え、フィールド学生番号 (user_no)、名前 (user_name)、およびスコア (score) をクエリして、並べ替えられたページング クエリを作成します。

1.3 ストアド プロシージャ - データを自動的に実行

use test;
DROP PROCEDURE if EXISTS BatchInsert;
delimiter $$
CREATE PROCEDURE BatchInsert(IN initId INT, IN loop_counts INT)
BEGIN
    DECLARE Var INT;
    DECLARE ID INT;
    SET Var = 0;
    SET ID = initId;
    set autocommit=0; -- 关闭自动提交事务,提高插入效率
    WHILE Var < loop_counts DO
        INSERT INTO `student` (`user_no`,`user_name`,`score`,`create_time`,`update_time`,`remark`) 
        VALUES (CONCAT('学号',ID),CONCAT('姓名',ID), floor(1 + rand()*100) , 
				DATE_ADD('2020-1-01 11:29:00', INTERVAL ROUND(RAND() * 1000 + 1) DAY),
				DATE_ADD('2020-1-01 11:29:00', INTERVAL ROUND(RAND() * 1000 + 1) DAY),
				CONCAT('备注',ID));
        SET ID = ID + 1;
        SET Var = Var + 1;
    END WHILE;
    COMMIT;
END$$;

delimiter ;  
CALL BatchInsert(1, 2000000);  

2. 最適化するSQL

	#浅分页
	EXPLAIN
	select user_no, user_name, score from student order by score desc limit 5, 20;
	#深分页
	EXPLAIN
	select user_no, user_name, score from student order by score desc limit 80000, 20;

短所: 深いページングか浅いページングかは関係ありません。全体的なパフォーマンスは比較的悪いです。
分析: 20 個のデータもクエリされます。オフセットが大きいため、深いページング クエリによってスキャンされるレコード行の数が浅いページングのクエリよりも多く、パフォーマンスが比較的悪いです。

3. 最適化計画1

ソートフィールドインデックススクリプトによる順序の追加

#增加order by 排序字段索引脚本
ALTER TABLE student ADD index idx_score (score);

利点: 浅いページング クエリの速度が大幅に向上しました
欠点: 深いページング クエリの速度は大幅に変化していません
テーブルに戻るにはコストがかかり、ソートにもコストがかかります MySQL は最適化に役立ち、最良のものを選択しましたディープ
ページング クエリが遅い理由: 部分的 オフセットが小さい場合はテーブルの戻りのコストが小さく、オフセットが大きい場合はテーブルの戻りのコストが高い スコア フィールド インデックスのデータ
構造
企業 WeChat スクリーンショット_16783468979960.png

4. 最適化計画2

フィールドの並べ替えと選択にジョイント インデックスを追加する

#给我们order by 和 select字段加上联合索引
ALTER TABLE student ADD index idx_score_name_no (score,user_name, user_no);

利点: ディープ ページング クエリの速度が大幅に向上しました
欠点: 要件の変更によりクエリ フィールドが追加された場合、SQL のパフォーマンスに影響します クエリ フィールドの結合インデックスを常に変更できるわけではありません。

5. 最適化計画3

並べ替えフィールドにインデックスを追加し、テーブルを手動で返します (他のインデックスの影響を軽減するために、プラン 2 でインデックスを削除する必要があります)
スクリプトを削除します

#给排序字段增加索引,并手动回表
drop index idx_score_name_no on student;

SQLを実行する

EXPLAIN
select user_no, user_name, score from student t1 join
(select id from student order by score desc limit 80000,20) t2 on t1.id= t2.id;  

利点: ディープ ページング クエリの速度が向上しました
欠点: サブクエリの結果セットが多すぎる場合には推奨されません。

6. 最適化計画4

スコア インデックス構造に従って最適化します。スコア フィールド インデックスは、最初にスコアの昇順で並べ替えられ、次にスコアが同じ場合は ID の昇順で並べ替えられます。ページング クエリのフロント エンドは、最後のクエリの ID とスコアを渡します。前のページのデータ行

EXPLAIN
 SELECT
	id, user_no, user_name, score 
FROM
	student 
WHERE id < 30000000  AND score <= 100.00 
	ORDER BY score DESC  LIMIT 20

7. まとめ

並べ替えによるページング クエリの最適化

  1. 浅いページングにより、フィールドによる順序にインデックスを追加できる
  2. ディープ ページングでは、フィールドの並べ替えや選択にジョイント インデックスを追加できます。
  3. 手動でテーブルを返すことにより、インデックスを強制的に取得できます。
  4. ビジネス側から開始して、ページング クエリを制限するか、フロントエンドとバックエンドのやり取りを変更します (各ページの最後のデータの ID とスコアを渡します)。

おすすめ

転載: blog.csdn.net/weixin_44030143/article/details/130340816