天天看點

業務SQL那些事--慎用LIMIT業務SQL那些事--慎用LIMITLIMIT OFFSET, ROW_COUNT實作分頁LIMIT ROW_COUNT會性能差

在業務中使用limit限制sql傳回行數是很常見的事情,但如果不知道其中可能的坑或者說真正執行邏輯,就可能會使sql執行非常慢,嚴重影響性能。

業務反映一條sql執行非常慢。簡單分析,例如下面的schema與sql(示範 databae:postgresql):

其中offset的從0開始,5000遞增,最大可以到200w。sql執行時間就會随着offset的值增加而增加,最終達到業務不可承受的程度。

這條sql因為主鍵有序是以省去了order by的sort,但sql通路表的時候依然至少需要通路$offset + 5000行資料,掃描行數随着offset增加而增加。而且這是至少需要通路的資料量,那麼不難了解為什麼sql會随着offset變大而變慢。

業務是用這條語句實作分頁功能,其分頁的order為c1就是表的主鍵。是以對于查詢條件可能通路大量資料的sql應該記錄last_id來實作分頁。改為如下sql,last_id初始值為'20150224',然後每次擷取資料後記錄最後一行的c1作為下次的last_id。

業務遇到一條包含有limit 0, 15的sql執行時間超過預期。簡單分析,schema與sql如下:

業務雖然建立了索引,同時在索引字首上有限制條件,但是由于滿足限制條件的行非常多,同時order by的column不是索引ordering的字首,是以table層依然需要通路所有滿足索引條件的行,在過濾後進行sort操作。plan如下:

和業務方了解後,c3的值隻有3個(0,1,9),即c3 <> 9可以改寫為 c3 in (0,1)。同時由于c4是定值,考慮到其他sql對c4列的使用,決定讓業務建立index(c4, c3, c2)。在postgresql中如下:

省去了sort的代價,同時table隻需要通路滿足限制條件的15行資料。

不過比較遺憾,示範的postgresql沒有能利用filter: c3 in (0,1)條件對(c4,c3,c2)生成兩個查詢範圍(1,0,1)~(1,0,1000000)和(1,1,1)~(1,1,1000000),即"c4"=1 and ("c3"=0 or "c3"=1) and "c2">1 and "c2"<1000000。