mysql 大分页查询优化_Mysql骚操作:优化大分页查询
背景
系統(tǒng)結(jié)構(gòu)如上圖。經(jīng)過排查是因?yàn)橄到y(tǒng)B拉取數(shù)據(jù)時(shí)間太長導(dǎo)致的推送超時(shí)。
系統(tǒng)B拉取數(shù)據(jù)的方法是根據(jù)_tiemstamp(數(shù)據(jù)操作時(shí)間)分頁查詢系統(tǒng)A的接口,即:
1SELECT?字段名2FROM?表名3WHERE?_timestamp?>=?beginTime?AND?_timestamp?<=?endTime?4LIMIT?n,?m;
由于該數(shù)據(jù)是從其他數(shù)據(jù)源中導(dǎo)入的,所以_timestamp這個(gè)字段值幾乎相同,這就導(dǎo)致了在我們的查詢范圍內(nèi)存在大約150萬的數(shù)據(jù)。一般遇到這種情況,首先想到的就是是否需要給_timestamp添加索引,這張表上是存在_timestamp索引的。那么為什么還會(huì)出現(xiàn)這個(gè)問題呢?這就要從分頁查詢本身說起了。
分頁查詢的性能瓶頸
B+樹簡述
首先我們要了解InnoDB存儲引擎中的B+數(shù)索引。這里我簡單總結(jié)一下:
上圖是一顆B+樹,通過觀察我們可以發(fā)現(xiàn)它的一些特點(diǎn):
1.每個(gè)節(jié)點(diǎn)中子節(jié)點(diǎn)個(gè)個(gè)數(shù)不能少于m/2個(gè),不能大于m個(gè)(B+樹是一顆m叉樹,圖中m=3)
2.根節(jié)點(diǎn)的節(jié)點(diǎn)個(gè)數(shù)可以超過m/2個(gè),這是一個(gè)例外
上述兩點(diǎn)特性是為了保證B+樹的查詢效率。
節(jié)點(diǎn)數(shù)超過m越多,在總節(jié)點(diǎn)數(shù)相同的情況下,樹的高度h就越小,此時(shí)m叉數(shù)就會(huì)向鏈表退化(O(logn)->O(n))。 ??節(jié)點(diǎn)數(shù)小于m/2越多,在總節(jié)點(diǎn)數(shù)相同的情況下,樹的高度h就越高,此時(shí)查詢數(shù)據(jù),就需要經(jīng)歷更多次的IO
3.m叉樹非葉子節(jié)點(diǎn)只存儲索引,不存儲數(shù)據(jù)
4.通過鏈表將葉子節(jié)點(diǎn)串聯(lián)在一起,這樣可以方便按區(qū)間查找。
B+比起二叉查找樹,有什么優(yōu)勢?
更矮,這就減少了IO次數(shù)。
由于非葉子節(jié)點(diǎn)不存儲數(shù)據(jù),上圖查詢?nèi)魏螖?shù)據(jù),都需要3次IO,查詢性能更穩(wěn)定
由于葉子節(jié)點(diǎn)使用了鏈表連接,范圍查詢更簡便。
分頁查詢過程
1.首先通過非主鍵索引查詢出所有條件的主鍵
2.通過主鍵索引,定位到數(shù)據(jù)
3.不斷重復(fù)上述操作
4.根據(jù)分頁條件,確定返回?cái)?shù)據(jù)的啟始位置以及數(shù)據(jù)量
5.返回?cái)?shù)據(jù)
可以看出,初始位置值越大,定位時(shí)需要查詢的數(shù)據(jù)就越多,查詢效率也會(huì)越低
測試集
為了測試優(yōu)化效果,我準(zhǔn)備了150萬測試數(shù)據(jù)(需要跑幾分鐘)。
1# 建表語句
2CREATE TABLE `test`(
3 `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主鍵',
4 `name` varchar(512) NOT NULL DEFAULT '無' COMMENT '創(chuàng)建人',
5 `_timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新時(shí)間',
6 PRIMARY KEY (`id`),
7 KEY `ix_timestamp` (`_timestamp`)
8) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='測試表';
9
10
11# 通過存儲過程導(dǎo)入數(shù)據(jù)
12drop procedure idata;
13delimiter ;;
14create procedure idata()
15begin
16 declare i int;
17 set i=1;
18 while(i<=1500000)do
19 insert into test values(i, i, now());
20 set i=i+1;
21 end while;
22end;;
23delimiter ;
24
25call idata();
接著,我們看一下使用索引的情況下,分頁查詢語句的耗時(shí)情況。
可以看出,在使用索引的情況下,無論初始位置是0,還是145萬,Mysql都會(huì)掃描所有符合條件的數(shù)據(jù),然后找到初始位置的數(shù)據(jù),向后查偏移量個(gè)數(shù)據(jù),最后返回。
這兩條語句的執(zhí)行速度差距非常大,大約3個(gè)數(shù)量級(0.00sec,10 sec)
解決方法
針對于limit,有很多優(yōu)化的方法,比如前端加緩存、或者使用分頁加載的方式展示數(shù)據(jù)。(大部分用戶請求數(shù)據(jù)的初始開始都不會(huì)很大)。在我們的使用場景中,調(diào)大超時(shí)時(shí)間的閾值也是可以的。
但是回到問題本身,問題出現(xiàn)的原因就是分頁語句隨著初始位置的增加,會(huì)有性能問題,所以治本的辦法,是對這個(gè)語句進(jìn)行優(yōu)化,有兩個(gè)優(yōu)化方法:
1 延遲關(guān)聯(lián)法:
我們先查詢出符合要求的主鍵(由于查詢的字段有索引,該索引的葉子節(jié)點(diǎn)就是主鍵,通過索引覆蓋我們可以省去一次回表操作。)
然后再通過主鍵索引查詢數(shù)據(jù),這就省去了遍歷數(shù)據(jù)找初始位置數(shù)據(jù)的過程
通過延遲關(guān)聯(lián)的方法,我們將10sec的耗時(shí)降低到了1.58sec,優(yōu)化了將近1個(gè)數(shù)量級。
2 主鍵閾值法
如果你的主鍵是自增的,那么就可以通過條件推算出符合條件的主鍵最大值&最小值(這里也是通過索引覆蓋省去了一次回表操作)
然后再根據(jù)閾值,取數(shù)據(jù)即可,同樣省去了遍歷數(shù)據(jù)找初始位置數(shù)據(jù)的過程
通過主鍵閾值法的方法,我們將10sec的耗時(shí)降低到了1.12sec,優(yōu)化了1個(gè)數(shù)量級
最后
最后對文章做一下補(bǔ)充說明:
1.文中優(yōu)化效果是僅憑借調(diào)用一次SQL的耗時(shí)給出的,并不科學(xué),僅僅是為了讓大家有一個(gè)直觀的概念。
2.無論是延遲關(guān)聯(lián)法,還是主鍵閾值法。思想都是一樣的,先把符合條件的主鍵找到,然后通過主鍵去定位符合條件的數(shù)據(jù),這里優(yōu)化了2個(gè)點(diǎn):1.通過索引覆蓋避免了回表;2.通過主鍵直接定位數(shù)據(jù)的方法,省去了在數(shù)據(jù)集中查詢初始位置的過程
3.優(yōu)化的效果隨數(shù)據(jù)量增加而增強(qiáng)。萬級別的數(shù)據(jù)優(yōu)化效果可能并不明顯。
總結(jié)
以上是生活随笔為你收集整理的mysql 大分页查询优化_Mysql骚操作:优化大分页查询的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 苹果天气不显示_热门天气APP被苹果拿下
- 下一篇: print输出 pytorch_pyto