<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
日常開發中經常會涉及到資料查詢分頁的問題,一般情況下都是根據前端傳入頁數與頁碼通過mysql的limit方式實現分頁,對於資料量較小的情況下沒有問題,但是如果資料量很大,深分頁可能導致查詢效率低下,介面超時的情況。
其實對於我們的 MySQL 查詢語句來說,整體效率還是可以的,該有的聯表查詢優化都有,該簡略的查詢內容也有,關鍵條件欄位和排序欄位該有的索引也都在,問題在於他一頁一頁的分頁去查詢,查到越後面的頁數,掃描到的資料越多,也就越慢。
我們在檢視前幾頁的時候,發現速度非常快,比如 limit 200,25,瞬間就出來了。但是越往後,速度就越慢,特別是百萬條之後,卡到不行,那這個是什麼原理呢。先看一下我們翻頁翻到後面時,查詢的 sql 是怎樣的:
select * from t_name where c_name1='xxx' order by c_name2 limit 2000000,25;
這種查詢的慢,其實是因為 limit 後面的偏移量太大導致的。
比如像上面的 limit 2000000,25,這個等同於資料庫要掃描出 2000025 條資料,然後再丟棄前面的 20000000 條資料,返回剩下 25 條資料給使用者,這種取法明顯不合理。
-- 建立兩個表:員工表和部門表 -- 部門表,存在則進行刪除 drop table if EXISTS dep; create table dep( id int unsigned primary key auto_increment, depno mediumint unsigned not null default 0, depname varchar(20) not null default "", memo varchar(200) not null default "" ); -- 員工表,存在則進行刪除 drop table if EXISTS emp; create table emp( id int unsigned primary key auto_increment, empno mediumint unsigned not null default 0, empname varchar(20) not null default "", job varchar(9) not null default "", mgr mediumint unsigned not null default 0, hiredate datetime not null, sal decimal(7,2) not null, comn decimal(7,2) not null, depno mediumint unsigned not null default 0 );
注意說明
-- 建立兩個函數:生成隨機字串和隨機編號 -- 產生隨機字串的函數 delimiter $ drop FUNCTION if EXISTS rand_string; CREATE FUNCTION rand_string(n INT) RETURNS VARCHAR(255) BEGIN DECLARE chars_str VARCHAR(100) DEFAULT 'abcdefghijklmlopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; DECLARE return_str VARCHAR(255) DEFAULT ''; DECLARE i INT DEFAULT 0; WHILE i < n DO SET return_str = CONCAT(return_str,SUBSTRING(chars_str,FLOOR(1+RAND()*52),1)); SET i = i+1; END WHILE; RETURN return_str; END $ delimiter; -- 產生隨機部門編號的函數 delimiter $ drop FUNCTION if EXISTS rand_num; CREATE FUNCTION rand_num() RETURNS INT(5) BEGIN DECLARE i INT DEFAULT 0; SET i = FLOOR(100+RAND()*10); RETURN i; END $ delimiter; 注意說明 -- 執行函數問題,This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its de -- 這是我們開啟了bin-log, 我們就必須指定我們的函數是否是,DETERMINISTIC 不確定的, NO SQL 沒有SQl語句,當然也不會修改資料 -- 在MySQL中建立函數時出現這種錯誤的解決方法:set global log_bin_trust_function_creators=TRUE; set global log_bin_trust_function_creators=TRUE;
-- 編寫儲存過程,模擬 100W 的員工資料。 -- 建立儲存過程:往emp表中插入資料 DELIMITER $ drop PROCEDURE if EXISTS insert_emp; CREATE PROCEDURE insert_emp(IN START INT(10),IN max_num INT(10)) BEGIN DECLARE i INT DEFAULT 0; /*set autocommit =0 把autocommit設定成0,把預設提交關閉*/ SET autocommit = 0; REPEAT SET i = i + 1; INSERT INTO emp(empno,empname,job,mgr,hiredate,sal,comn,depno) VALUES ((START+i),rand_string(6),'SALEMAN',0001,now(),2000,400,rand_num()); UNTIL i = max_num END REPEAT; COMMIT; END $ DELIMITER; -- 插入500W條資料,時間有點久,耐心等待,1409s call insert_emp(0,5000000); -- 查詢部門員工表 select * from emp LIMIT 1,10;
-- 編寫儲存過程,模擬 120 的部門資料 -- 建立儲存過程:往dep表中插入資料 DELIMITER $ drop PROCEDURE if EXISTS insert_dept; CREATE PROCEDURE insert_dept(IN START INT(10),IN max_num INT(10)) BEGIN DECLARE i INT DEFAULT 0; SET autocommit = 0; REPEAT SET i = i+1; INSERT INTO dep( depno,depname,memo) VALUES((START+i),rand_string(10),rand_string(8)); UNTIL i = max_num END REPEAT; COMMIT; END $ DELIMITER; -- 插入120條資料 call insert_dept(1,120); -- 查詢部門員工表 select * from dep;
-- 建立關鍵欄位的索引,這邊是跑完資料之後再建索引,會導致建索引耗時長,但是跑資料就會快一些。 -- 建立關鍵欄位的索引:排序、條件 CREATE INDEX idx_emp_id ON emp(id); CREATE INDEX idx_emp_depno ON emp(depno); CREATE INDEX idx_dep_depno ON dep(depno);
-- 驗證測試 -- 偏移量為100,取25,Time: 0.011s SELECT a.empno,a.empname,a.job,a.sal,b.depno,b.depname from emp a left join dep b on a.depno = b.depno order by a.id desc limit 100,25; -- 偏移量為4800000,取25,Time: 10.242s SELECT a.empno,a.empname,a.job,a.sal,b.depno,b.depname from emp a left join dep b on a.depno = b.depno order by a.id desc limit 4800000,25;
因為我們有主鍵 id,並且在上面建了索引,所以可以先在索引樹中找到開始位置的 id 值,再根據找到的 id 值查詢行資料。
-- 子查詢獲取偏移100條的位置的id,在這個位置上往後取25,Time: 0.04s SELECT a.empno,a.empname,a.job,a.sal,b.depno,b.depname from emp a left join dep b on a.depno = b.depno where a.id >= (select id from emp order by id limit 100,1) order by a.id limit 25; -- 子查詢獲取偏移4800000條的位置的id,在這個位置上往後取25,Time: 1.549s SELECT a.empno,a.empname,a.job,a.sal,b.depno,b.depname from emp a left join dep b on a.depno = b.depno where a.id >= (select id from emp order by id limit 4800000,1) order by a.id limit 25;
記住上次查詢結果的主鍵位置,避免使用偏移量 offset。
這個效率是最好的,無論怎麼分頁,耗時基本都是一致的,因為他執行完條件之後,都只掃描了 25 條資料。
但是有個問題,只適合一頁一頁的分頁,這樣才能記住前一個分頁的最後 id。如果使用者跳著分頁就有問題了,比如剛剛刷完第 25 頁,馬上跳到 35 頁,資料就會不對。這種的適合場景是類似百度搜尋或者騰訊新聞那種滾輪往下拉,不斷拉取不斷載入的情況。這種延遲載入會保證資料不會跳躍著獲取。
-- 記住了上次的分頁的最後一條資料的id是100,這邊就直接跳過100,從101開始掃描表,Time: 0.006s SELECT a.id,a.empno,a.empname,a.job,a.sal,b.depno,b.depname from emp a left join dep b on a.depno = b.depno where a.id > 100 order by a.id limit 25; -- 記住了上次的分頁的最後一條資料的id是4800000,這邊就直接跳過4800000,從4800001開始掃描表,Time: 0.046s SELECT a.id,a.empno,a.empname,a.job,a.sal,b.depno,b.depname from emp a left join dep b on a.depno = b.depno where a.id > 4800000 order by a.id limit 25;
看了網上一個阿里的 DBA 同學分享的方案:設定 limit 的偏移量和獲取數一個最大值,超過這個最大值,就返回空資料。
因為他覺得超過這個值你已經不是在分頁了,而是在刷資料了,如果確認要找資料,應該輸入合適條件來縮小範圍,而不是一頁一頁分頁。
深分頁問題從理論上來說是存在的場景,但是從實際的業務場景考慮,深分頁很多情況下缺少具體的業務場景做支撐,試想哪個業務會從480W頁面,查詢25條資料,如果需要搜尋某條資料,使用最多的應該根據條件型別過濾吧。
每種方案各有優缺點,具體採用那種解決方案需要結合具體的業務場景,如果根據實際業務場景不需要深分頁,可以採用降級策略,設定分頁引數閾值。如果確實需要深分頁問題可以覆蓋子+子查詢優化或者通過偏移量查詢,如果能獲取到偏移量的前提下優先選擇偏移量的方案,否則採用覆蓋索引+子查詢。
無論是否深分頁都應該考慮限流降級的問題,而且要考慮短時間內重複呼叫的問題,可以限制每秒執行次數,避免使用者誤點以及呼叫頻繁帶來的資料安全問題。
到此這篇關於MySql深分頁問題解決的文章就介紹到這了,更多相關MySql深分頁內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45