一 背景大部分開發(fā)和DBA同行都對分頁查詢非常非常了解,看帖子翻頁需要分頁查詢,搜索商品也需要分頁查詢。那么問題來了,遇到上千萬或者上億的數(shù)據(jù)量怎么快速的拉取全量,比如大商家拉取每月千萬級別的訂單數(shù)量到自己獨立的ISV做財務統(tǒng)計;或者擁有百萬千萬粉絲的公眾大號,給全部粉絲推送消息的場景。本文講講個人的優(yōu)化分頁查詢的經(jīng)驗,拋磚引玉。 二 分析在講如何優(yōu)化之前我們先來看看一個比較常見錯誤的寫法
該SQL是一個非常典型的排序+分頁查詢:
MySQL 執(zhí)行此類SQL時需要先掃描到N行,然后再去取M行。對于此類操作,獲取前面少數(shù)幾行數(shù)據(jù)會很快,但是隨著掃描的記錄數(shù)越多,SQL的性能就會越差,因為N的值越大,MySQL需要掃描越多的數(shù)據(jù)來定位到具體的N行,這樣耗費大量的 IO 成本和時間成本。一圖勝千言,我們使用簡單的圖來解釋為什么 上面的sql 的寫法掃描數(shù)據(jù)會慢。 t 表是一個索引組織表,key idxkidtype(kid,type) 。 符合kid=3 and type=1 的記錄有很多行,我們取第 9,10行。 select * from t where kid =3 and type=1 order by id desc 8,2; MySQL 是如何執(zhí)行上面的sql 的?對于Innodb表,系統(tǒng)是根據(jù) idxkidtype 二級索引里面包含的主鍵去查找對應的行。對于百萬千萬級別的記錄而言,索引大小可能和數(shù)據(jù)大小相差無幾,cache在內存中的索引數(shù)量有限,而且二級索引和數(shù)據(jù)葉子節(jié)點不在同一個物理塊兒上存儲,二級索引與主鍵的相對無序映射關系,也會帶來大量的隨機IO請求,N值越大越需要遍歷大量索引頁和數(shù)據(jù)葉,需要耗費的時間就越久。 鑒于上面的大分頁查詢耗費時間長的原因,我們思考一個問題,是否需要完全遍歷“無效的數(shù)據(jù)”?如果我們需要limit 8,2;我們跳過前面8行無關的數(shù)據(jù)頁遍歷,可以直接通過索引定位到第9,第10行,這樣操作是不是更快了?依然是一圖勝千言,通過這其實也是 延遲關聯(lián)的 核心思思:通過使用覆蓋索引查詢返回需要的主鍵,再根據(jù)主鍵關聯(lián)原表獲得需要的數(shù)據(jù),而不是通過二級索引獲取主鍵再通過主鍵去遍歷數(shù)據(jù)頁。 通過上面的原理分析,我們知道通過常規(guī)方式進行大分頁查詢慢的原因,也知道了提高大分頁查詢的具體方法 ,下面我們討論一下在線上業(yè)務系統(tǒng)中常用的解決方法。 三 實踐出真知針對limit 優(yōu)化有很多種方式: 1 前端加緩存、搜索,減少落到庫的查詢操作。比如海量商品可以放到搜索里面,使用瀑布流的方式展現(xiàn)數(shù)據(jù),很多電商網(wǎng)站采用了這種方式。 2 優(yōu)化SQL 訪問數(shù)據(jù)的方式,直接快速定位到要訪問的數(shù)據(jù)行。 3 使用書簽方式 ,記錄上次查詢最新/大的id值,向后追溯 M行記錄。 對于第二種方式 我們推薦使用'延遲關聯(lián)'的方法來優(yōu)化排序操作,何謂'延遲關聯(lián)' :通過使用覆蓋索引查詢返回需要的主鍵,再根據(jù)主鍵關聯(lián)原表獲得需要的數(shù)據(jù)。 3.1 延遲關聯(lián)優(yōu)化前
其執(zhí)行時間: 優(yōu)化后:
執(zhí)行時間: 優(yōu)化后 執(zhí)行時間 為原來的1/3 。 3.2 使用書簽的方式首先要獲取復合條件的記錄的最大 id和最小id(默認id是主鍵)
其次 根據(jù)id 大于最小值或者小于最大值 進行遍歷。
案例 當遇到延遲關聯(lián)也不能滿足查詢速度的要求時
使用延遲關聯(lián)查詢數(shù)據(jù)510ms ,使用基于書簽模式的解決方法減少到10ms以內 絕對是一個質的飛躍。
四 小結從我們的優(yōu)化經(jīng)驗和案例上來講,根據(jù)主鍵定位數(shù)據(jù)的方式直接定位到主鍵起始位點,然后過濾所需要的數(shù)據(jù) 相對比延遲關聯(lián)的速度更快些,查找數(shù)據(jù)的時候少了二級索引掃描。但是 優(yōu)化方法沒有銀彈,沒有一勞永逸的方法。比如下面的例子 order by id desc 和 order by asc 的結果相差70ms ,生產上的案例有l(wèi)imit 100 相差1.3s ,這是為什么呢?留給大家去思考吧。 |
|
來自: jackeyqing > 《數(shù)據(jù)庫》