<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          必看!大數(shù)據(jù)量分頁(yè)查詢?cè)趺磧?yōu)化提速?

          共 1965字,需瀏覽 4分鐘

           ·

          2020-07-28 18:37

          ??Java大聯(lián)盟

          ? 幫助萬千Java學(xué)習(xí)者持續(xù)成長(zhǎng)

          關(guān)注



          作者|悠悠i

          cnblogs.com/youyoui/p/7851007.html


          B 站搜索:楠哥教你學(xué)Java

          獲取更多優(yōu)質(zhì)視頻教程


          ??目錄

          1、準(zhǔn)備工作

          2、一般分頁(yè)查詢

          3、使用子查詢優(yōu)化

          4、使用 id 限定優(yōu)化

          5、使用臨時(shí)表優(yōu)化

          6、關(guān)于數(shù)據(jù)表的 id 說明

          當(dāng)需要從數(shù)據(jù)庫(kù)查詢的表有上萬條記錄的時(shí)候,一次性查詢所有結(jié)果會(huì)變得很慢,特別是隨著數(shù)據(jù)量的增加特別明顯,這時(shí)需要使用分頁(yè)查詢。對(duì)于數(shù)據(jù)庫(kù)分頁(yè)查詢,也有很多種方法和優(yōu)化的點(diǎn)。下面簡(jiǎn)單說一下我知道的一些方法。


          準(zhǔn)備工作

          為了對(duì)下面列舉的一些優(yōu)化進(jìn)行測(cè)試,下面針對(duì)已有的一張表進(jìn)行說明。

          • 表名:order_history
          • 描述:某個(gè)業(yè)務(wù)的訂單歷史表
          • 主要字段:unsigned int id,tinyint(4) int type
          • 字段情況:該表一共37個(gè)字段,不包含text等大型數(shù)據(jù),最大為varchar(500),id字段為索引,且為遞增。
          • 數(shù)據(jù)量:5709294
          • MySQL版本:5.7.16 線下找一張百萬級(jí)的測(cè)試表可不容易,如果需要自己測(cè)試的話,可以寫shell腳本什么的插入數(shù)據(jù)進(jìn)行測(cè)試。以下的 sql 所有語(yǔ)句執(zhí)行的環(huán)境沒有發(fā)生改變,下面是基本測(cè)試結(jié)果:
          select?count(*)?from?orders_history;

          返回結(jié)果:5709294

          三次查詢時(shí)間分別為:

          • 8903 ms
          • 8323 ms
          • 8401 ms


          一般分頁(yè)查詢

          一般的分頁(yè)查詢使用簡(jiǎn)單的 limit 子句就可以實(shí)現(xiàn)。limit 子句聲明如下:

          SELECT?*?FROM?table?LIMIT?[offset,]?rows?|?rows?OFFSET?offset

          LIMIT 子句可以被用于指定 SELECT 語(yǔ)句返回的記錄數(shù)。需注意以下幾點(diǎn):

          • 第一個(gè)參數(shù)指定第一個(gè)返回記錄行的偏移量,注意從0開始
          • 第二個(gè)參數(shù)指定返回記錄行的最大數(shù)目
          • 如果只給定一個(gè)參數(shù):它表示返回最大的記錄行數(shù)目
          • 第二個(gè)參數(shù)為 -1 表示檢索從某一個(gè)偏移量到記錄集的結(jié)束所有的記錄行
          • 初始記錄行的偏移量是 0(而不是 1)

          下面是一個(gè)應(yīng)用實(shí)例:

          select?*?from?orders_history?where?type=8?limit?1000,10;

          該條語(yǔ)句將會(huì)從表 orders_history 中查詢offset: 1000開始之后的10條數(shù)據(jù),也就是第1001條到第1010條數(shù)據(jù)(1001 <= id <= 1010)。

          數(shù)據(jù)表中的記錄默認(rèn)使用主鍵(一般為id)排序,上面的結(jié)果相當(dāng)于:

          select?*?from?orders_history?where?type=8?order?by?id?limit?10000,10;

          三次查詢時(shí)間分別為:

          • 3040 ms
          • 3063 ms
          • 3018 ms

          針對(duì)這種查詢方式,下面測(cè)試查詢記錄量對(duì)時(shí)間的影響:

          select?*?from?orders_history?where?type=8?limit?10000,1;
          select?*?from?orders_history?where?type=8?limit?10000,10;
          select?*?from?orders_history?where?type=8?limit?10000,100;
          select?*?from?orders_history?where?type=8?limit?10000,1000;
          select?*?from?orders_history?where?type=8?limit?10000,10000;

          三次查詢時(shí)間如下:

          • 查詢1條記錄:3072ms 3092ms 3002ms
          • 查詢10條記錄:3081ms 3077ms 3032ms
          • 查詢100條記錄:3118ms 3200ms 3128ms
          • 查詢1000條記錄:3412ms 3468ms 3394ms
          • 查詢10000條記錄:3749ms 3802ms 3696ms

          另外我還做了十來次查詢,從查詢時(shí)間來看,基本可以確定,在查詢記錄量低于100時(shí),查詢時(shí)間基本沒有差距,隨著查詢記錄量越來越大,所花費(fèi)的時(shí)間也會(huì)越來越多。

          針對(duì)查詢偏移量的測(cè)試:

          select?*?from?orders_history?where?type=8?limit?100,100;
          select?*?from?orders_history?where?type=8?limit?1000,100;
          select?*?from?orders_history?where?type=8?limit?10000,100;
          select?*?from?orders_history?where?type=8?limit?100000,100;
          select?*?from?orders_history?where?type=8?limit?1000000,100;

          三次查詢時(shí)間如下:

          • 查詢100偏移:25ms 24ms 24ms
          • 查詢1000偏移:78ms 76ms 77ms
          • 查詢10000偏移:3092ms 3212ms 3128ms
          • 查詢100000偏移:3878ms 3812ms 3798ms
          • 查詢1000000偏移:14608ms 14062ms 14700ms

          隨著查詢偏移的增大,尤其查詢偏移大于10萬以后,查詢時(shí)間急劇增加。

          這種分頁(yè)查詢方式會(huì)從數(shù)據(jù)庫(kù)第一條記錄開始掃描,所以越往后,查詢速度越慢,而且查詢的數(shù)據(jù)越多,也會(huì)拖慢總查詢速度。


          使用子查詢優(yōu)化

          這種方式先定位偏移位置的 id,然后往后查詢,這種方式適用于 id 遞增的情況。

          select?*?from?orders_history?where?type=8?limit?100000,1;

          select?id?from?orders_history?where?type=8?limit?100000,1;

          select?*?from?orders_history?where?type=8?and
          id>=(select?id?from?orders_history?where?type=8?limit?100000,1)
          limit?100;

          select?*?from?orders_history?where?type=8?limit?100000,100;

          4條語(yǔ)句的查詢時(shí)間如下:

          • 第1條語(yǔ)句:3674ms
          • 第2條語(yǔ)句:1315ms
          • 第3條語(yǔ)句:1327ms
          • 第4條語(yǔ)句:3710ms

          針對(duì)上面的查詢需要注意:

          • 比較第1條語(yǔ)句和第2條語(yǔ)句:使用 select id 代替 select * 速度增加了3倍
          • 比較第2條語(yǔ)句和第3條語(yǔ)句:速度相差幾十毫秒
          • 比較第3條語(yǔ)句和第4條語(yǔ)句:得益于 select id 速度增加,第3條語(yǔ)句查詢速度增加了3倍

          這種方式相較于原始一般的查詢方法,將會(huì)增快數(shù)倍。


          使用 id 限定優(yōu)化

          這種方式假設(shè)數(shù)據(jù)表的id是連續(xù)遞增的,則我們根據(jù)查詢的頁(yè)數(shù)和查詢的記錄數(shù)可以算出查詢的id的范圍,可以使用 id between and 來查詢:

          select?*?from?orders_history?where?type=2
          and?id?between?1000000?and?1000100?limit?100;

          查詢時(shí)間:15ms 12ms 9ms

          這種查詢方式能夠極大地優(yōu)化查詢速度,基本能夠在幾十毫秒之內(nèi)完成。限制是只能使用于明確知道id的情況,不過一般建立表的時(shí)候,都會(huì)添加基本的id字段,這為分頁(yè)查詢帶來很多便利。

          還可以有另外一種寫法:

          select?*?from?orders_history?where?id?>=?1000001?limit?100;

          當(dāng)然還可以使用 in 的方式來進(jìn)行查詢,這種方式經(jīng)常用在多表關(guān)聯(lián)的時(shí)候進(jìn)行查詢,使用其他表查詢的id集合,來進(jìn)行查詢:

          select?*?from?orders_history?where?id?in
          (select?order_id?from?trade_2?where?goods?=?'pen')
          limit?100;

          這種 in 查詢的方式要注意:某些 mysql 版本不支持在 in 子句中使用 limit。


          使用臨時(shí)表優(yōu)化

          這種方式已經(jīng)不屬于查詢優(yōu)化,這兒附帶提一下。

          對(duì)于使用 id 限定優(yōu)化中的問題,需要 id 是連續(xù)遞增的,但是在一些場(chǎng)景下,比如使用歷史表的時(shí)候,或者出現(xiàn)過數(shù)據(jù)缺失問題時(shí),可以考慮使用臨時(shí)存儲(chǔ)的表來記錄分頁(yè)的id,使用分頁(yè)的id來進(jìn)行 in 查詢。這樣能夠極大的提高傳統(tǒng)的分頁(yè)查詢速度,尤其是數(shù)據(jù)量上千萬的時(shí)候。


          關(guān)于數(shù)據(jù)表的id說明

          一般情況下,在數(shù)據(jù)庫(kù)中建立表的時(shí)候,強(qiáng)制為每一張表添加 id 遞增字段,這樣方便查詢。

          如果像是訂單庫(kù)等數(shù)據(jù)量非常龐大,一般會(huì)進(jìn)行分庫(kù)分表。這個(gè)時(shí)候不建議使用數(shù)據(jù)庫(kù)的 id 作為唯一標(biāo)識(shí),而應(yīng)該使用分布式的高并發(fā)唯一 id 生成器來生成,并在數(shù)據(jù)表中使用另外的字段來存儲(chǔ)這個(gè)唯一標(biāo)識(shí)。

          使用先使用范圍查詢定位 id (或者索引),然后再使用索引進(jìn)行定位數(shù)據(jù),能夠提高好幾倍查詢速度。即先 select id,然后再 select *;


          推薦閱讀

          1、Spring Boot+Vue項(xiàng)目實(shí)戰(zhàn)

          2、B站:4小時(shí)上手MyBatis Plus

          3、一文搞懂前后端分離

          4、快速上手Spring Boot+Vue前后端分離


          楠哥簡(jiǎn)介

          資深 Java 工程師,微信號(hào)?southwindss

          《Java零基礎(chǔ)實(shí)戰(zhàn)》一書作者

          騰訊課程官方 Java 面試官今日頭條認(rèn)證大V

          GitChat認(rèn)證作者,B站認(rèn)證UP主(楠哥教你學(xué)Java)

          致力于幫助萬千 Java 學(xué)習(xí)者持續(xù)成長(zhǎng)。




          有收獲,就點(diǎn)個(gè)在看?
          瀏覽 75
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  99精品人| 第一页国产| 成人 在线观看免费爱爱 | 天天夜夜拍拍视频 | 色丁香婷婷五月天 |