資料庫單錶大量資料的分頁優化方案

2021-10-22 15:41:00 字數 3865 閱讀 8492

1.1 單錶資料量大,訪問最後幾頁的時候特別慢

比如我們單錶的資料有1000w以上,此時當偏移量offset比較大的時候,普通查詢會導致查詢效率從前幾頁的幾十毫秒,增加到十幾秒甚至更高

1.2 非正常訪問

其實絕大部分情況下沒有多少使用者真的翻頁到最後幾頁,大致的情況為前端分頁元件有跳轉到最後幾頁的鏈結,或者是被別人惡意請求資料,比如爬蟲爬取資料。

分頁操作通常會使用 limit 加上偏移量的辦法實現,同時再加上合適的 order by 子句。但這會出現乙個常見問題:當偏移量非常大的時候,它會導致 mysql 掃瞄大量不需要的行然後再拋棄掉。

也就是說,在使用limit的時候,當過濾條件不足以過濾掉大部分行的時候,offset過大會導致mysql掃瞄出大量的資料,再丟棄其中很大一部分,留下想要的部分。

假設我們的sql如下:

/*偏移量為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

[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

3.1 顯式告訴mysql過濾掉足夠多的行

上面的sql中,並沒有寫過濾條件,假設我們不僅僅知道過上面的濾條件,還知道要從多少行開始掃瞄(假設這個點叫掃瞄開始標誌位),那我們就可以顯式告訴mysql應該從哪些資料行開始掃瞄。

3.1.1 使用索引覆蓋子查詢先獲取掃瞄開始標誌位

/*子查詢獲取偏移100條的位置的id,在這個位置上往後取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,在這個位置上往後取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

[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

請注意:這是必須要獲取到正確的標誌位,且子查詢必須使用索引覆蓋才可以達到這個效果。假設我們的索引不能覆蓋子查詢,這樣寫只會讓sql更慢。獲取正確的標誌位的意思是,裡面使用的過濾條件和排序方式必須和外面一致。

其原理就是僅僅掃瞄了索引過濾掉了足夠的資料再去連表查詢,如果索引不能覆蓋過濾條件和排序方式,這樣寫就不起作用了

3.1.2 記住上次查詢的結束位置

/*記住了上次的分頁的最後一條資料的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;

/*記住了上次的分頁的最後一條資料的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

[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

注意:此種情況因為是要記錄上一次查詢到了多少條,然後增加了乙個過濾條件。所以不適合跳頁查詢的情況,尤其是倒著跳頁的情況。比如先查詢了第10000頁,然後再去查詢第1頁,導致沒有資料,這種情況雖然也有優化的可能,但是優化起來複雜度較高。

3.2 降級策略

一般情況下來說,如果沒有跳頁乙個正常使用者是沒有可能翻頁到1000頁以後的,這種情況大多出現於指令碼刷資料等情況,所以可以針對這種惡意情況,可以使用相應的策略限制此種訪問或者返回欺騙性資料。

比如:

3.3 分表

本文討論大表優化,但是有時候大表的查詢速度的確是有上限的,必要的時候還是要進行分表優化,分表不論是使用路由的方式還是其他的方式都可以,本文就不贅述了。

資料庫 分頁查詢優化

select top10 from admin where adminid notin select top1500000 adminid from admin 使用not in會引發全表掃瞄 理論上會,由於資料庫會對查詢進行優化,所以實際我並不知道會不會 其用時如下 cpu 時間 562 毫秒,占...

資料庫效能優化二 資料庫表優化

資料庫優化包含以下三部分,資料庫自身的優化,資料庫表優化,程式操作優化.此文為第二部分優化 設計規範化表,消除資料冗餘 資料庫正規化是確保資料庫結構合理,滿足各種查詢需要 避免資料庫操作異常的資料庫設計方式。滿足正規化要求的表,稱為規範化表,正規化產生於20世紀 70年代初,一般表設計滿足前三正規化...

資料庫效能優化二 資料庫表優化

優化 設計規範化表,消除資料冗餘 資料庫正規化是確保資料庫結構合理,滿足各種查詢需要 避免資料庫操作異常的資料庫設計方式。滿足正規化要求的表,稱為規範化表,正規化產生於20世紀 70年代初,一般表設計滿足前三正規化就可以,在這裡簡單介紹一下前三正規化 第一正規化 1nf 無重複的列 所謂第一正規化 ...