一次線上MySQL分頁事故,搞了半夜...
點(diǎn)擊上方“碼農(nóng)突圍”,馬上關(guān)注 這里是碼農(nóng)充電第一站,回復(fù)“666”,獲取一份專屬大禮包 真愛,請(qǐng)?jiān)O(shè)置“星標(biāo)”或點(diǎn)個(gè)“在看”

背景
分析
數(shù)據(jù)模擬
測(cè)試
解決方案
小結(jié)
背景
1?POST??domain/v1.0/module/method?order=condition&orderType=desc&offset=1800000&limit=500
分析
1?select?*?from?t_name?where?c_name1='xxx'?order?by?c_name2?limit?2000000,25;

但這會(huì)出現(xiàn)一個(gè)常見問題:當(dāng)偏移量非常大的時(shí)候,它會(huì)導(dǎo)致 MySQL 掃描大量不需要的行然后再拋棄掉。
數(shù)據(jù)模擬
/*部門表,存在則進(jìn)行刪除?*/
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?""
);
/*員工表,存在則進(jìn)行刪除*/
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
);
/*?產(chǎn)生隨機(jī)字符串的函數(shù)*/
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?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;
/*產(chǎn)生隨機(jī)部門編號(hào)的函數(shù)*/
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;
?/*建立存儲(chǔ)過程:往emp表中插入數(shù)據(jù)*/
?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?把a(bǔ)utocommit設(shè)置成0,把默認(rèn)提交關(guān)閉*/
?????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條數(shù)據(jù)*/
?call?insert_emp(0,5000000);
?/*建立存儲(chǔ)過程:往dep表中插入數(shù)據(jù)*/
?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條數(shù)據(jù)*/
?call?insert_dept(1,120);
/*建立關(guān)鍵字段的索引:排序、條件*/
CREATE?INDEX?idx_emp_id?ON?emp(id);
CREATE?INDEX?idx_emp_depno?ON?emp(depno);
CREATE?INDEX?idx_dep_depno?ON?dep(depno);?
測(cè)試
/*偏移量為100,取25*/
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*/
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;?
[SQL]
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;
受影響的行:?0
時(shí)間:?0.001s
[SQL]
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;
受影響的行:?0
時(shí)間:?12.275s
解決方案
①使用索引覆蓋+子查詢優(yōu)化
?/*子查詢獲取偏移100條的位置的id,在這個(gè)位置上往后取25*/
?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,在這個(gè)位置上往后取25*/
?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;
?[SQL]
?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;
?受影響的行:?0
?時(shí)間:?0.106s
?[SQL]
?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;
?受影響的行:?0
?時(shí)間:?1.541s??
②起始位置重定義
?/*記住了上次的分頁的最后一條數(shù)據(jù)的id是100,這邊就直接跳過100,從101開始掃描表*/
?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;
?/*記住了上次的分頁的最后一條數(shù)據(jù)的id是4800000,這邊就直接跳過4800000,從4800001開始掃描表*/
?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;
[SQL]
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;
受影響的行:?0
時(shí)間:?0.001s
[SQL]
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;
受影響的行:?0
時(shí)間:?0.000s?
③降級(jí)策略
小結(jié)
(完)
碼農(nóng)突圍資料鏈接
1、臥槽!字節(jié)跳動(dòng)《算法中文手冊(cè)》火了,完整版 PDF 開放下載!
2、計(jì)算機(jī)基礎(chǔ)知識(shí)總結(jié)與操作系統(tǒng) PDF 下載
3、艾瑪,終于來了!《LeetCode Java版題解》.PDF
4、Github 10K+,《LeetCode刷題C/C++版答案》出爐.PDF歡迎添加魚哥個(gè)人微信:smartfish2020,進(jìn)粉絲群或圍觀朋友圈
評(píng)論
圖片
表情
