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