成人在线亚洲_国产日韩视频一区二区三区_久久久国产精品_99国内精品久久久久久久

您的位置:首頁技術文章
文章詳情頁

MySql深分頁問題解決

瀏覽:322日期:2023-02-18 16:43:11
目錄
  • 1. 問題描述
  • 2. 問題分析
  • 3. 驗證測試
    • 3.1 創建兩個表
    • 3.2 創建兩個函數
    • 3.3 編寫存儲過程
    • 3.4 編寫存儲過程
    • 3.5 創建索引
    • 3.6 驗證測試
  • 4. 解決方案
    • 4.1 使用索引覆蓋+子查詢優化
    • 4.2 起始位置重定義
    • 4.3 降級策略
  • 5. 梳理總結

    1. 問題描述

    日常開發中經常會涉及到數據查詢分頁的問題,一般情況下都是根據前端傳入頁數與頁碼通過mysql的limit方式實現分頁,對于數據量較小的情況下沒有問題,但是如果數據量很大,深分頁可能導致查詢效率低下,接口超時的情況。

    2. 問題分析

    其實對于我們的 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 條數據給用戶,這種取法明顯不合理。

    3. 驗證測試

    3.1 創建兩個表

    -- 創建兩個表:員工表和部門表
    -- 部門表,存在則進行刪除
    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
    );

    注意說明

    • mediumint是MySQL數據庫中的一種整型,比INT小,比SMALLINT大,
    • 取值范圍為:-8388608到8388607,無符號的范圍是0到16777215。
    • 中等大小的整數,一位大小為3個字節。

    3.2 創建兩個函數

    -- 創建兩個函數:生成隨機字符串和隨機編號
    -- 產生隨機字符串的函數
    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;

    3.3 編寫存儲過程

    -- 編寫存儲過程,模擬 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;

    3.4 編寫存儲過程

    -- 編寫存儲過程,模擬 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;

    3.5 創建索引

    -- 建立關鍵字段的索引,這邊是跑完數據之后再建索引,會導致建索引耗時長,但是跑數據就會快一些。
    -- 建立關鍵字段的索引:排序、條件
    CREATE INDEX idx_emp_id ON emp(id);
    CREATE INDEX idx_emp_depno ON emp(depno);
    CREATE INDEX idx_dep_depno ON dep(depno); 
    

    3.6 驗證測試

    -- 驗證測試
    -- 偏移量為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;

    4. 解決方案

    4.1 使用索引覆蓋+子查詢優化

    因為我們有主鍵 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;

    4.2 起始位置重定義

    記住上次查找結果的主鍵位置,避免使用偏移量 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;

    4.3 降級策略

    看了網上一個阿里的 DBA 同學分享的方案:配置 limit 的偏移量和獲取數一個最大值,超過這個最大值,就返回空數據。
    因為他覺得超過這個值你已經不是在分頁了,而是在刷數據了,如果確認要找數據,應該輸入合適條件來縮小范圍,而不是一頁一頁分頁。

    5. 梳理總結

    深分頁問題從理論上來說是存在的場景,但是從實際的業務場景考慮,深分頁很多情況下缺少具體的業務場景做支撐,試想哪個業務會從480W頁面,查詢25條數據,如果需要搜索某條數據,使用最多的應該根據條件類型過濾吧。

    每種方案各有優缺點,具體采用那種解決方案需要結合具體的業務場景,如果根據實際業務場景不需要深分頁,可以采用降級策略,設置分頁參數閾值。如果確實需要深分頁問題可以覆蓋子+子查詢優化或者通過偏移量查詢,如果能獲取到偏移量的前提下優先選擇偏移量的方案,否則采用覆蓋索引+子查詢。

    無論是否深分頁都應該考慮限流降級的問題,而且要考慮短時間內重復調用的問題,可以限制每秒執行次數,避免用戶誤點以及調用頻繁帶來的數據安全問題。

    到此這篇關于MySql深分頁問題解決的文章就介紹到這了,更多相關MySql深分頁內容請搜索以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持!

    標簽: MySQL
    相關文章:
    成人在线亚洲_国产日韩视频一区二区三区_久久久国产精品_99国内精品久久久久久久
    午夜精品久久久久久久99樱桃| 亚洲视屏一区| |精品福利一区二区三区| 日韩1区2区日韩1区2区| 成人免费va视频| 欧美日韩伊人| 欧美丰满少妇xxxbbb| 亚洲精品一二三四区| 国产成人午夜电影网| 亚洲经典在线| 欧美一区二区视频在线观看2020| 亚洲丝袜另类动漫二区| 国产精品18久久久久久久久| 亚洲激情午夜| 久久综合网色—综合色88| 日韩电影在线观看电影| 亚洲黑丝在线| 欧美性猛交一区二区三区精品| 中文一区二区在线观看| 国产精品亚洲成人| 亚洲欧美日韩另类精品一区二区三区| 久久综合色鬼综合色| 亚洲成人免费电影| 欧美日韩亚洲免费| 4438x亚洲最大成人网| 亚洲第一激情av| 国产精品国产一区二区| 日韩精品一区二区在线| 免播放器亚洲一区| 日韩视频三区| 欧美精品一区二区三区很污很色的| 麻豆视频一区二区| 亚洲区第一页| 久久精品这里都是精品| 韩国精品一区二区| 日韩午夜一区| 国产日韩精品一区| 岛国精品一区二区| 欧美视频在线不卡| 亚洲福利视频一区二区| 精品不卡一区二区三区| 久久综合九色综合97_久久久| 久久国产日韩欧美精品| 国产精品视频福利| 成人欧美一区二区三区1314| 成人av免费在线| 欧美一区二区高清| 强制捆绑调教一区二区| 国产私拍一区| 亚洲精品中文在线| 国内外成人免费视频| 精品福利在线导航| 国产成人综合亚洲网站| 欧美日韩精品欧美日韩精品一综合| 午夜视频在线观看一区二区三区| 国产一级一区二区| 亚洲日本青草视频在线怡红院| 亚洲欧美一区二区原创| 精品av久久707| 国产成人超碰人人澡人人澡| 欧美日韩午夜在线视频| 玖玖九九国产精品| 色呦呦一区二区三区| 五月天久久比比资源色| 国产一区白浆| 亚洲一区二区三区自拍| 国产亚洲一区在线播放| 亚洲综合激情小说| 一区二区日韩免费看| 伊人夜夜躁av伊人久久| 99国产精品| 一区二区三区免费观看| 亚洲一级电影| 亚洲美腿欧美偷拍| 中文一区二区| 亚洲成人高清在线| 在线亚洲一区观看| 美女被吸乳得到大胸91| 欧美亚洲另类激情小说| 日韩av在线发布| 色94色欧美sute亚洲线路一久| 日本午夜一本久久久综合| 在线视频欧美区| 精品一区二区三区免费播放| 国产精品情趣视频| 韩国理伦片一区二区三区在线播放 | 国产精品一区二区免费不卡| 亚洲国产精品二十页| 色婷婷综合激情| 91在线视频网址| 国产精品久久久久aaaa樱花| 一道本成人在线| 99视频精品免费视频| 亚洲国产一区二区视频| 精品国产一区二区三区久久久蜜月| 一区二区三区四区五区视频| 国产河南妇女毛片精品久久久| 亚洲人xxxx| 欧美一区二区三区视频在线观看| 黄色欧美成人| 狠狠网亚洲精品| 亚洲黄色免费网站| 精品乱人伦一区二区三区| 一本色道久久99精品综合| 国产成人自拍在线| 亚洲精品一二三| 欧美群妇大交群中文字幕| 在线看片日韩| 成人精品视频一区二区三区尤物| 天堂久久久久va久久久久| 国产精品国产三级国产普通话三级| 欧美日韩国产精品自在自线| 在线日韩视频| 成人高清视频在线| 久久国产视频网| 亚洲高清中文字幕| 日本一区二区免费在线观看视频 | 国内视频一区| 国产.欧美.日韩| 亚洲高清一区二区三区| 国产欧美精品区一区二区三区| 欧美性猛片xxxx免费看久爱| 亚洲经典三级| 91网址在线看| 国产盗摄女厕一区二区三区| 手机精品视频在线观看| 中文字幕在线不卡| 久久免费视频色| 欧美日精品一区视频| 国产亚洲一区二区三区在线播放| www.亚洲色图| 国产一区二区三区高清播放| 午夜精品在线看| 亚洲欧美激情小说另类| 久久影院午夜论| 欧美一区二区三区播放老司机| 久久精品男女| 亚洲小说欧美另类婷婷| 成人性生交大片免费看中文网站| 午夜精品一区二区三区免费视频| 国产精品国产三级国产aⅴ入口| 精品卡一卡二卡三卡四在线| 欧美三级三级三级| 色伊人久久综合中文字幕| 国产精品mm| 高清av一区二区| 久久精品国产999大香线蕉| 亚洲国产中文字幕在线视频综合| 成人欧美一区二区三区1314| 久久一日本道色综合| 欧美精品乱码久久久久久按摩 | 欧美中文字幕| 亚洲精品1区2区| 94-欧美-setu| 国产不卡在线视频| 国产一区二区在线视频| 人人狠狠综合久久亚洲| 亚洲一区二区三区影院| 中文字幕乱码亚洲精品一区| 亚洲精品一区二区三区在线观看| 欧美精品在线观看播放| 欧美日韩专区在线| 在线免费亚洲电影| 色综合久久久久综合体桃花网| 亚洲综合99| 每日更新成人在线视频| 国产亚洲一区在线| 中文亚洲字幕| 在线亚洲观看| 国产亚洲欧美另类一区二区三区| 亚洲精品黄色| 亚洲人成网站在线观看播放| 亚洲福利久久| 国产一区二区中文| 欧美日韩在线精品| 欧美系列一区| 国产一区视频在线观看免费| 欧美日韩精品伦理作品在线免费观看| 91麻豆文化传媒在线观看| 99视频一区二区| 91麻豆国产福利在线观看| 99久久精品免费精品国产| 波多野结衣亚洲一区| 成人激情电影免费在线观看| 91视视频在线观看入口直接观看www | 亚洲精品国产第一综合99久久| 亚洲人xxxx| 亚洲1区2区3区4区| 蜜臀av性久久久久av蜜臀妖精| 蜜桃一区二区三区在线| 激情综合色综合久久| 国产在线麻豆精品观看| 国产精品18久久久久久久久| caoporn国产一区二区| 欧美日韩免费高清| 国产日韩欧美一区二区三区四区 | 亚洲欧洲日韩综合二区| 免费视频一区二区三区在线观看| 久久久久久久久久久久久久一区| 在线免费观看不卡av|