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 は最適化に役立ち、最良のものを選択しましたディープ
ページング クエリが遅い理由: 部分的 オフセットが小さい場合はテーブルの戻りのコストが小さく、オフセットが大きい場合はテーブルの戻りのコストが高い スコア フィールド インデックスのデータ
構造
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. まとめ
並べ替えによるページング クエリの最適化
- 浅いページングにより、フィールドによる順序にインデックスを追加できる
- ディープ ページングでは、フィールドの並べ替えや選択にジョイント インデックスを追加できます。
- 手動でテーブルを返すことにより、インデックスを強制的に取得できます。
- ビジネス側から開始して、ページング クエリを制限するか、フロントエンドとバックエンドのやり取りを変更します (各ページの最後のデータの ID とスコアを渡します)。