日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

MySQL - 分页查询优化的两个案例解析

發布時間:2025/3/21 数据库 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 MySQL - 分页查询优化的两个案例解析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • 生猛干貨
  • Table
  • 日常場景
    • Case1 根據自增且連續的主鍵排序的分頁查詢
      • 優化
      • 數據可刪除的場景
      • 適用條件
    • Case2 根據非主鍵字段排序的分頁查詢
  • 搞定MySQL

生猛干貨

帶你搞定MySQL實戰,輕松對應海量業務處理及高并發需求,從容應對大場面試


Table

還是我們那個老表

CREATE TABLE `employees` (`id` int(11) NOT NULL AUTO_INCREMENT,`name` varchar(24) NOT NULL DEFAULT '' COMMENT '姓名',`age` int(11) NOT NULL DEFAULT '0' COMMENT '年齡',`position` varchar(20) NOT NULL DEFAULT '' COMMENT '職位',`hire_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '入職時間',PRIMARY KEY (`id`),KEY `idx_name_age_position` (`name`,`age`,`position`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='員工記錄表';

有個主鍵索引和二級聯合索引 idx_name_age_position


日常場景

任何一個系統,分頁查詢都是必不可少的吧 ,MySQL中的分頁查詢 就是 limit唄 ,你有沒有感覺到 越往后翻頁越慢 ,常見的SQL如下

mysql> select * from employees limit 10000,10;

就是從 employees 中取出從 10001 行開始的 10 行記錄。

MySQL是怎么處理這個SQL的呢?

先讀取 10010 條記錄,然后拋棄前 10000 條記錄,僅保留10 條想要的數據 。 可想而知,如果要查詢一張大表比較靠后的數據,這效率是非常低的。

那有沒有優化的辦法呢?


Case1 根據自增且連續的主鍵排序的分頁查詢

我們先來看一個 【根據自增且連續主鍵排序的分頁查詢】的優化案例

select * from employees limit 10000, 10

從第1萬條數據開始,獲取10條數據

我們來看下執行計劃

mysql> explain select * from employees limit 10000, 10 ;+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+-------+ | 1 | SIMPLE | employees | NULL | ALL | NULL | NULL | NULL | NULL | 100175 | 100 | NULL | +----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+-------+ 1 row in setmysql>

因為沒有添加單獨 order by字段,所以表示通過主鍵排序 。 執行計劃顯示全表掃描


優化

如何優化下呢?

既然是按照id排序,結合B+Tree 的特性 ,如果能從 10000這個數據位置往后掃描,是不是就會比掃描全部理論上更快一些呢?

改造如下

select * from employees where id> 10000 limit 10;

來看下執行計劃

mysql> explain select * from employees where id> 10000 limit 10; +----+-------------+-----------+------------+-------+---------------+---------+---------+------+-------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-----------+------------+-------+---------------+---------+---------+------+-------+----------+-------------+ | 1 | SIMPLE | employees | NULL | range | PRIMARY | PRIMARY | 4 | NULL | 50087 | 100 | Using where | +----+-------------+-----------+------------+-------+---------------+---------+---------+------+-------+----------+-------------+ 1 row in set

比一比這兩個,是不是下面那個更快一些


數據可刪除的場景

還有個問題,我們知道我們業務系統有些數據是可以被刪除的,如果有些數據被刪除了,還是按照id來排序,上面這種優化方式,會存在問題嗎?

假設8888 這條業務數據被刪除了

delete from employees where id = 8888 ;

那我們來看下

如果允許刪除,那這種優化方式是不是就不正確了?

  • limit 10000, 10 : 就是全部數據排好序后 取第10000個開始后的10個,我們剛才刪除了8888, 所以 第一條數據就變成了 10002

  • id> 10000 limit 10 : 這個就很好理解了,刪除了8888 ,不影響 id>10000的排序 ,所以第一條數據還是 10001


適用條件

如果主鍵不連續,不能使用上面描述的優化方法。

如果原 SQL 是 order by 非主鍵的字段,按照上的方法改寫會導致兩條 SQL 的結果不一致。

所以這種優化方式必須同時滿足以下兩個條件:

  • 主鍵自增且連續
  • 結果是按照主鍵排序的

Case2 根據非主鍵字段排序的分頁查詢

來看第二個案例,實際工作中可能比第一種用的比較多

select * from employees ORDER BY name limit 10000, 10 ;

來看下執行計劃

mysql> explain select * from employees ORDER BY name limit 10000, 10 ;+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+----------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+----------------+ | 1 | SIMPLE | employees | NULL | ALL | NULL | NULL | NULL | NULL | 100175 | 100 | Using filesort | +----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+----------------+ 1 row in setmysql>

按照B+Tree的結構,應該會走name字段索引,wtf , 操作的結果集太多,又要回表等等原因 , MySQL可能不選name 字段的索引 , key 字段對應的值為 null ,從而走了全表掃描 。。。。

還有 Using filesort

這部分就屬于MySQL內部的優化了,可以使用Trace來追蹤下MySQL是如何選擇的 ,

MySQL - 使用trace工具來窺探MySQL是如何選擇執行計劃的

MySQL認為掃描整個索引并查找到沒索引的行(可能要遍歷多個索引樹)的成本比掃描全表的成本更高,所以優化器放棄使用索引。

那既然知道不走索引的原因,那么怎么優化呢?

關鍵是讓排序時返回的字段盡可能少,所以可以讓排序和分頁操作先查出主鍵,然后根據主鍵查到對應的記錄.

讓排序時返回的字段盡可能少–》 只返回id , 然后用返回的特定范圍的id ,再和原表關聯,只取特定范圍內的數據 ,肯定比全表掃描要快。

改造如下

select * from employees a inner join (select id from employees order by name limit 10000,10) b on a.id = b.id;

先找到id (select id 使用覆蓋索引),然后用這個結果集 (這個案例中就只有10條結果)去和 employees 關聯

看看執行計劃

原 SQL 使用的是 filesort 排序,優化后的 SQL 使用的是索引排序。

當然了,結果集也是和優化前是一致的


搞定MySQL

總結

以上是生活随笔為你收集整理的MySQL - 分页查询优化的两个案例解析的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。