久久精品人人爽,华人av在线,亚洲性视频网站,欧美专区一二三

如何解決mysql深分頁問題

144次閱讀
沒有評論

共計(jì) 3758 個(gè)字符,預(yù)計(jì)需要花費(fèi) 10 分鐘才能閱讀完成。

今天丸趣 TV 小編給大家分享一下如何解決 mysql 深分頁問題的相關(guān)知識點(diǎn),內(nèi)容詳細(xì),邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。

日常需求開發(fā)過程中,相信大家對于 limit 一定不會(huì)陌生,但是使用 limit 時(shí),當(dāng)偏移量(offset)非常大時(shí),會(huì)發(fā)現(xiàn)查詢效率越來越慢。一開始 limit 2000 時(shí),可能 200ms, 就能查詢出需要的到數(shù)據(jù),但是當(dāng) limit 4000 offset 100000 時(shí),會(huì)發(fā)現(xiàn)它的查詢效率已經(jīng)需要 1S 左右,那要是更大的時(shí)候呢,只會(huì)越來越慢。

概括

本文將會(huì)討論當(dāng) mysql 表大數(shù)據(jù)量的情況,如何優(yōu)化深分頁問題,并附上最近的優(yōu)化慢 sql 問題的案例偽代碼。

1、limit 深分頁問題描述

先看看表結(jié)構(gòu)(隨便舉了個(gè)例子,表結(jié)構(gòu)不全,無用字段就不進(jìn)行展示了)

CREATE TABLE `p2p_detail_record` ( `id` varchar(32) COLLATE utf8mb4_bin NOT NULL DEFAULT   COMMENT  主鍵 ,
 `batch_num` int NOT NULL DEFAULT  0  COMMENT  上報(bào)數(shù)量 ,
 `uptime` bigint NOT NULL DEFAULT  0  COMMENT  上報(bào)時(shí)間 ,
 `uuid` varchar(64) COLLATE utf8mb4_bin NOT NULL DEFAULT   COMMENT  會(huì)議 id ,
 `start_time_stamp` bigint NOT NULL DEFAULT  0  COMMENT  開始時(shí)間 ,
 `answer_time_stamp` bigint NOT NULL DEFAULT  0  COMMENT  應(yīng)答時(shí)間 ,
 `end_time_stamp` bigint NOT NULL DEFAULT  0  COMMENT  結(jié)束時(shí)間 ,
 `duration` int NOT NULL DEFAULT  0  COMMENT  持續(xù)時(shí)間 ,
 PRIMARY KEY (`id`),
 KEY `idx_uuid` (`uuid`),
 KEY `idx_start_time_stamp` (`start_time_stamp`) // 索引,
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT= p2p 通話記錄詳情表 

假設(shè)我們要查詢的深分頁 SQL 長這樣

select * 
from p2p_detail_record ppdr 
where ppdr .start_time_stamp  1656666798000 
limit 0,2000

查詢效率是 94ms,是不是很快?那如果我們 limit 100000,2000 呢,查詢效率是 1.5S,已經(jīng)非常慢,那如果更多呢?

2、sql 慢原因分析

讓我們來看看這條 sql 的執(zhí)行計(jì)劃

也走到了索引,那為什么還是慢呢?我們先來回顧一下 mysql 的相關(guān)知識點(diǎn)。

聚簇索引和非聚簇索引

聚簇索引:葉子節(jié)點(diǎn)儲存的是整行的數(shù)據(jù)。

非聚簇索引:葉子節(jié)點(diǎn)儲存的是整行的數(shù)據(jù)對應(yīng)的主鍵值。

使用非聚簇索引查詢的流程

通過非聚簇索引樹,找到對應(yīng)的葉子節(jié)點(diǎn),獲取到主鍵的值。

再通過取到主鍵的值,回到聚簇索引樹,找到對應(yīng)的整行數(shù)據(jù)。(整個(gè)過程稱為回表)

回到這條 sql 為什么慢的問題上,原因如下

1、limit 語句會(huì)先掃描 offset+ n 行,然后再丟棄掉前 offset 行,返回后 n 行數(shù)據(jù)。也就是說 limit 100000,10,就會(huì)掃描 100010 行,而 limit 0,10,只掃描 10 行。這里需要回表 100010 次,大量的時(shí)間都在回表這個(gè)上面。

方案核心思路:能不能事先知道要從哪個(gè)主鍵 ID 開始,減少回表的次數(shù)

常見解決方案通過子查詢優(yōu)化

select * 
from p2p_detail_record ppdr 
where id  = (select id from p2p_detail_record ppdr2 where ppdr2 .start_time_stamp  1656666798000 limit 100000,1) 
limit 2000

相同的查詢結(jié)果,也是 10W 條開始的第 2000 條,查詢效率為 200ms,是不是快了不少。

標(biāo)簽記錄法

標(biāo)簽記錄法:其實(shí)標(biāo)記一下上次查詢到哪一條了,下次再來查的時(shí)候,從該條開始往下掃描。類似書簽的作用

select * from p2p_detail_record ppdr
where ppdr.id    bb9d67ee6eac4cab9909bad7c98f54d4 
order by id 
limit 2000
備注:bb9d67ee6eac4cab9909bad7c98f54d4 是上次查詢結(jié)果的最后一條 ID

使用標(biāo)簽記錄法,性能都會(huì)不錯(cuò)的,因?yàn)槊辛?id 索引。但是這種方式有幾個(gè)缺點(diǎn)。

1、只能連續(xù)頁查詢,不能跨頁查詢。

2、需要一種類似連續(xù)自增的字段(可以使用 orber by id 的方式)。

方案對比

使用通過子查詢優(yōu)化的方式

優(yōu)點(diǎn):可跨頁查詢,想查哪一頁的數(shù)據(jù)就查哪一頁的數(shù)據(jù)。

缺點(diǎn):效率不如標(biāo)簽記錄法。原因:比如需要查 10W 條數(shù)據(jù)后,第 1000 條,也需要先查詢出非聚簇索引對應(yīng)的 10W1000 條數(shù)據(jù),在取第 10W 開始的 ID,進(jìn)行查詢。

使用 標(biāo)簽記錄法 的方式

優(yōu)點(diǎn):查詢效率很穩(wěn)定,非常快。

缺點(diǎn):

不跨頁查詢,

需要一種類似連續(xù)自增的字段

關(guān)于第二點(diǎn)的說明:該點(diǎn)一般都好解決,可使用任意不重復(fù)的字段進(jìn)行排序即可。若使用可能重復(fù)的字段進(jìn)行排序的字段,由于 mysql 對于相同值的字段排序是無序,導(dǎo)致如果正好在分頁時(shí),上下頁中可能存在相同的數(shù)據(jù)。

實(shí)戰(zhàn)案例

需求:需要查詢查詢某一時(shí)間段的數(shù)據(jù)量,假設(shè)有幾十萬的數(shù)據(jù)量需要查詢出來,進(jìn)行某些操作。

需求分析 1、分批查詢(分頁查詢),設(shè)計(jì)深分頁問題,導(dǎo)致效率較慢。

CREATE TABLE `p2p_detail_record` ( `id` varchar(32) COLLATE utf8mb4_bin NOT NULL DEFAULT   COMMENT  主鍵 ,
 `batch_num` int NOT NULL DEFAULT  0  COMMENT  上報(bào)數(shù)量 ,
 `uptime` bigint NOT NULL DEFAULT  0  COMMENT  上報(bào)時(shí)間 ,
 `uuid` varchar(64) COLLATE utf8mb4_bin NOT NULL DEFAULT   COMMENT  會(huì)議 id ,
 `start_time_stamp` bigint NOT NULL DEFAULT  0  COMMENT  開始時(shí)間 ,
 `answer_time_stamp` bigint NOT NULL DEFAULT  0  COMMENT  應(yīng)答時(shí)間 ,
 `end_time_stamp` bigint NOT NULL DEFAULT  0  COMMENT  結(jié)束時(shí)間 ,
 `duration` int NOT NULL DEFAULT  0  COMMENT  持續(xù)時(shí)間 ,
 PRIMARY KEY (`id`),
 KEY `idx_uuid` (`uuid`),
 KEY `idx_start_time_stamp` (`start_time_stamp`) // 索引,
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT= p2p 通話記錄詳情表 

偽代碼實(shí)現(xiàn):

// 最小 ID 
String lastId = null; 
// 一頁的條數(shù)  
Integer pageSize = 2000; 
List P2pRecordVo  list ;
do{ 
 list = listP2pRecordByPage(lastId,pageSize); // 標(biāo)簽記錄法,記錄上次查詢過的 Id 
 lastId = list.get(list.size()-1).getId(); // 獲取上一次查詢數(shù)據(jù)最后的 ID,用于記錄
 // 對數(shù)據(jù)的操作邏輯
 XXXXX();
 }while(isNotEmpty(list));
 
 select id = listP2pRecordByPage  
 select * 
 from p2p_detail_record ppdr where 1=1
  if test =  lastId != null 
 and ppdr.id   #{lastId}
  /if 
 order by id asc
 limit #{pageSize}
 /select

這里有個(gè)小優(yōu)化點(diǎn): 可能有的人會(huì)先對所有數(shù)據(jù)排序一遍,拿到最小 ID,但是這樣對所有數(shù)據(jù)排序,然后去 min(id), 耗時(shí)也蠻長的,其實(shí)第一次查詢,可不帶 lastId 進(jìn)行查詢,查詢結(jié)果也是一樣。速度更快。

以上就是“如何解決 mysql 深分頁問題”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,丸趣 TV 小編每天都會(huì)為大家更新不同的知識,如果還想學(xué)習(xí)更多的知識,請關(guān)注丸趣 TV 行業(yè)資訊頻道。

正文完
 
丸趣
版權(quán)聲明:本站原創(chuàng)文章,由 丸趣 2023-07-13發(fā)表,共計(jì)3758字。
轉(zhuǎn)載說明:除特殊說明外本站除技術(shù)相關(guān)以外文章皆由網(wǎng)絡(luò)搜集發(fā)布,轉(zhuǎn)載請注明出處。
評論(沒有評論)
主站蜘蛛池模板: 合江县| 桐乡市| 鹤山市| 南充市| 福安市| 白玉县| 邮箱| 临沂市| 宾川县| 青浦区| 巴彦淖尔市| 彭山县| 石柱| 常熟市| 宽城| 临夏市| 佛冈县| 海丰县| 永善县| 常山县| 安徽省| 易门县| 彰武县| 峨山| 西城区| 林西县| 瑞丽市| 灵宝市| 军事| 台山市| 甘德县| 宕昌县| 南投市| 台山市| 海阳市| 鸡泽县| 革吉县| 随州市| 阳朔县| 清丰县| 景宁|