別在用offset和limit分頁了

終於要對MySQL優化下手了,本文將對分頁進行優化說明,但願能夠獲得一個合適你的方案web

前言

分頁這個話題已是老生常談了,可是有多少小夥伴一邊是既但願優化的本身的系統,另外一邊在項目上仍是保持本身獨有的個性。sql

有個性

優化這件事是須要本身主動行動起來的,本身搞測試數據,只有在測試的路上纔會發現更多你未知的事情。數據庫

本文咔咔也會針對分頁優化這個話題進行解讀。微信

1、表結構

這個數據庫結構就是咔咔目前線上項目的表,只不過咔咔將字段名改了而已,還有將時間字段取消了。編輯器

數據庫結構以下工具

CREATE TABLE `tp_statistics` (
  `ss_id` int(11NOT NULL AUTO_INCREMENT,
  `ss_field1` decimal(11,2NOT NULL DEFAULT '0.00',
  `ss_field2` decimal(11,2NOT NULL DEFAULT '0.00',
  `ss_field3` decimal(11,2NOT NULL DEFAULT '0.00',
  PRIMARY KEY (`ss_id`)
ENGINE=InnoDB AUTO_INCREMENT=3499994 DEFAULT CHARSET=utf8 COLLATE=utf8mb4_general_ci ROW_FORMAT=COMPACT;

表結構

根據以上信息能夠看到目前表裏邊的數據有350萬記錄,接下來就針對這350W條記錄進行查詢優化。性能

2、初探查詢效率

先來寫一個查詢的SQL語句,先看一下查詢耗費的時間。學習

根據下圖能夠看到查詢時間基本忽略不計,可是要注意的是limit的偏移量值。測試

初次查詢結果

因而咱們要一步一步的加大這個偏移量而後進行測試,先將偏移量改成10000flex

能夠看到查詢時間仍是很是理想的。

偏移量10000查詢

爲了節省時間咔咔將這個偏移量的值直接調整到340W。

這個時候就能夠看到很是明顯的變化了,查詢時間猛增到了0.79s。

偏移量340w查詢

出現了這樣的狀況,那確定就須要進行優化了,拿起鍵盤就是幹。

3、分析查詢耗時的緣由

提到分析SQL語句,必備的知識點就是explain,若是對這個工具不會使用的能夠去看看MySQL的基礎部分。

根據下圖能夠看到三條查詢語句都進行了表掃描。

explain分析語句

都知道只要有關於分頁就必存在排序,那麼加一個排序再來看一下查詢效率。

排序以後的查詢時間

而後在進行對排序的語句進行分析查看。

經過這裏看到當使用了排序時數據庫掃描的行數就是偏移量加上須要查詢的數量。

分許排序語句

此時就能夠知道的是,在偏移量很是大的時候,就像上圖案例中的limit  3400000,12這樣的查詢。

此時MySQL就須要查詢3400012行數據,而後在返回最後12條數據。

前邊查詢的340W數據都將被拋棄,這樣的執行結果可不是咱們想要的。

咔咔以前看到相關文章說是解決這個問題的方案,要麼直接限制分頁的數量,要麼就優化當偏移量很是大的時候的性能。

若是你都把本文看到了這裏,那怎麼會讓你失望,確定是優化大偏移量的性能問題。

4、優化

既然提到了優化,無非就那麼倆點,加索引,使用其它的方案來代替這個方案。

咔咔提供的這條數據表結構信息,徹底能夠理解爲就是圖書館的借閱記錄,字段的什麼都不要去關心就能夠了。

對於排序來講,在這種場景下是不會給時間加排序的,而是給主鍵加排序,而且因爲添加測試數據的緣由將時間字段給取消了。

接下來使用覆蓋索引加inner join的方式來進行優化。

select ss_id,ss_field1,ss_field2,ss_field3 from tp_statistics inner join ( select ss_id from tp_statistics order by ss_id limit 3000000,10) b using (ss_id);
優化方案一

從上圖能夠看到查詢時間從0.8s優化到了0.4s,可是這樣的效果仍是不盡人意。

因而只能更換一下思路再進行優化。

思考片刻

既然優化最大偏移量這條路有點坎坷,能不能從其它方面進行入手。

估計有不少同窗已經知道咔咔將要拋出什麼話題了。

沒錯,就是使用where > id  而後使用limit。

先來測試一波結果,在寫具體實現方案。

優化方案二

根據上圖能夠看到這種方式是十分可行的,分頁在300W條數據之後的查詢時間也基本忽略不計。

那麼這種方案要怎麼實現呢!

5、方案落地

其實這個方案真的很簡單,只須要簡單的轉換一下思路便可。

是時候作出改變了

當客戶端第一次獲取數據的時候就正常傳遞offset、limit倆個參數。

首次返回的數據就使用客戶端傳遞過來的offset、limit進行獲取。

當第一次的數據返回成功後。

客戶端第二次拉取數據時這個時候參數就發生改變了,就不能再是offset、limit了。

此時應該傳遞的參數就是第一次獲取的數據最後一條數據的id。

此時的參數就爲last_id、limit。

後臺獲取到last_id後就能夠在sql語句中使用where條件 <  last_id

咔咔這裏給的狀況是數據在倒敘的狀況下,若是正序就是大於last_id便可。

接下來咔咔使用一個案例給你們直接明瞭的說明。

實戰案例

以下就是將要實戰演示的案例,例如首次使用page、limit獲取到了數據。

返回結果的最後一條數據的id就是3499984

第一次獲取數據

此時若是在獲取第二條記錄就不是使用offset、limit了,就是傳遞last_id和limit了。

以下圖

此時就是使用的where條件來進行直接過濾數據,條件就是id小於上次數據的最後一條id便可。

獲取第二條數據

時間對比

假設如今要獲取最後一條數據

沒有優化以前

沒有優化以前

優化以後能夠明顯的看到查詢時間的變化

優化以後的查詢

6、總結

關於limit優化簡單幾句話概述一下。

  • 數據量大的時候不能使用offset、limit來進行分頁,由於offset越大,查詢時間越久。
  • 固然不能說全部的分頁都不能夠,若是你的數據就那麼幾千、幾萬條,那就很無所謂,隨便使用。
  • 落地方案就是咔咔上邊的方案,首次使用offset、limit獲取數據,第二次獲取數據使用where條件到第一次數據最後一條id便可。

堅持學習、堅持寫博、堅持分享是咔咔從業以來一直所秉持的信念。但願在偌大互聯網中咔咔的文章能帶給你一絲絲幫助。我是咔咔,下期見。


本文分享自微信公衆號 - PHP初學者必看(PHP0022)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索