SQL分頁查詢方案的性能對比

2021-02-24 CSDN雲計算

導讀


本文主要介紹了基於ROWNUM、主鍵列/非空唯一性列、分析函數、OFFSET-FETCH NEXT機制的幾種SQL分頁查詢方案的性能對比。

分頁查詢可分為邏輯分頁和物理分頁兩種。邏輯分頁是應用代碼級別實現的分頁,指用戶通過一次查詢就取出所有的數據結果集並進行緩存,然後根據當前頁所需要展示的數據內容進行切分並遍歷顯示,若需要查詢的數據量非常大,則會消耗大量的內存來緩存數據,並且在會話生命周期內重複訪問數據時,可直接訪問緩存的數據,不過此時有可能訪問不到最新的數據。物理分頁是指使用資料庫自帶的分頁機制,比如MySQL的limit offset機制,Oracle的rownum和offset-fetch機制進行分頁查詢,是對資料庫表數據進行分頁條件查詢,每一次物理分頁都會直接訪問資料庫,可以保證數據是最新的,並且不需要在會話級別緩存過多的數據。

本文主要介紹的SQL分頁,即物理分頁,主要用於在數據結果集較大時控制數據在前臺(比如報表,列表框,頁面等)的分頁顯示,這樣既可以降低內存消耗,提高查詢效率,也可以方便數據在前臺的展示。文中如有疏漏之處,望指正!


環境版本信息


Oracle 版本:19.3.0.0.0

MySQL版本:8.0.18

OS版本:CentOS 8.0

方案及性能對比


1.確認測試表emp中的數據量


2.確認表結構和索引信息

3.通過rownum實現分頁查詢(不使用order by排序)

SQL: select * from ( select rownum rowno,e.* from emp e where rownum<=&ROW_NUM1) t where t.rowno>=&ROW_NUM2;

 執行計劃信息:

通過執行計劃和評估開銷可以看出,該方法將使用全表掃描,前段的分頁查詢效率會比較高,但是隨著ROWNUM值的增大,在分頁後期查詢的速度會越來越慢,這個情況和MySQL的limit機制一樣,當表中數據量較大時,隨著查詢範圍的擴大,每次需要讀取的表數據塊越來越多,查詢效率越來越低。如下圖所示:

 

4.通過rownum實現分頁查詢(使用order by排序)

SQL: select * from ( select rownum rowno,e.* from (select * from emp order by id) e where rownum<=&2) t where t.rowno>=&1;

執行計劃信息:

由執行計劃信息可以看出,當使用order by對數據集進行排序後再分頁時,由於索引數據在存儲的時候默認已經進行了升序排序(若有需要,也可以創建降序索引,該案例是基於Oracle環境,對於MySQL資料庫,從8.0開始也支持了真正意義的降序索引),因此使用了索引全掃描(即索引遍歷)來避免排序,後期需要遍歷的索引塊越來越多,並且由於index full scan是單塊讀,所以該方法會出現在分頁後期查詢效率越來越慢的情況。如下圖所示:

5.直接使用主鍵代替ROWNUM進行分頁查詢

查出id的最大值和最小值:

SQL: select * from emp where id between &1 and &2;

執行計劃信息: 

從執行計劃信息可以看出,該方法使用了主鍵索引的range scan,當表數據量較大時,不會出現隨著查詢範圍的擴大而查詢效率越來越低的情況,因為可以直接通過主鍵或非空唯一性索引讀取到符合條件的rowid,然後直接通過rowid找到數據塊讀取數據,如下圖所示:

說明:

該方法需要主鍵值是連續的,否則有可能出現分頁查詢時每一頁的數據行數不一樣的情況。

假如表上有其他的非空唯一性索引列,則同樣可以基於該列做分頁查詢。

若在分頁查詢時表上有一定的DML操作,則可以考慮進行最後一頁查詢時將SQL中的變量2設置較大一些(也可以通過子查詢直接獲取max(id))。


6.使用分析函數進行分頁查詢

SQL: select * from ( select e.*, row_number() over (order by id) rn from emp e) where rn between &1 and &2; 

執行計劃信息:

從執行計劃信息可以看出,該方法使用了窗口函數進行分頁查詢,同樣使用了INDEX FULL SCAN來避免排序,該方法也會出現在分頁後期查詢效率越來越慢的情況,因為後期需要遍歷的索引塊越來越多,並且由於index full scan是單塊讀,因此後期的效率有可能會比使用ROWNUM的方式更為低下,如下圖所示:

SQL: select * from emp order by id OFFSET &1 ROWS FETCH NEXT &2 ROWS ONLY;

執行計劃信息:

從執行計劃可以看出,offset-fetch機制在底層本質上還是基於分析函數實現的,同樣使用了索引全掃描(即索引遍歷)來避免排序,因此該方法也會出現在分頁後期查詢效率越來越慢的情況,因為後期需要遍歷的索引塊越來越多,並且由於index full scan是單塊讀,從而產生的物理IO和邏輯IO次數更多,因此後期的效率有可能會比使用ROWNUM的方式更為低下,如下圖所示: 

8.排序列的選擇

當列可為NULL時,Oracle不能使用該列上的索引來避免排序,因為Oracle的索引是不記錄NULL值的,如下圖所示:

通過對比分析,我們可以得出如下結論:

1.當主鍵值或者非空唯一性列值是連續時,推薦使用主鍵值或者非空唯一性列進行分頁,此時分頁效率較高且數據量較大時分頁後期性能不會越來越差。

2.當對分頁後每頁的數據行數沒有較高要求時,同樣推薦使用主鍵值或者非空唯一性列進行分頁。

3.使用分析函數和OFFSET-FETCH實現分頁,分頁後期的性能衰減率可能會比通過ROWNUM的方式高,這是因為index full scan是單塊讀,從而產生了更多次的物理IO和邏輯IO。

4.在使用分析函數和OFFSET-FETCH機制時,需要基於主鍵或非空唯一性列進行order by排序,此時會通過列上的索引來避免排序操作。若選擇的排序列可為NULL,則Oracle資料庫只能通過全表掃描來訪問數據,因為Oracle資料庫的索引是不記錄NULL值的,因此不能基於該列上的索引來避免排序,從而保證不會丟失數據。

5.在MySQL中,索引是會記錄NULL值的,這也是為什麼MySQL中IS NULL可以走索引的原因。

6.MySQL資料庫的分頁中,可以使用可為null的非唯一性列作為排序列,因為此時MySQL會將null值當作最小值參加排序,不會丟失數據。

作者介紹:

吳海存,10g/11g/12c OCM, Oracle Exadata/Golden Gate 專家, 曾於Amazon和Oracle公司擔任全球業務資深DBA,目前供職於中國農業銀行,擔任資深資料庫專家。

更多閱讀推薦

相關焦點

  • 資料庫分頁查詢的幾種實現思路
    PageHelper框架可以讓使用者不用關心分頁語法就可以讓dao查詢接口實現分頁功能,它跟mybatis框架結合使用,使用mybatis的Interceptor機制,通過攔截Executor的query方法攔截所有dao數據查詢,通過替換MappedStatement修改執行的sql,給待執行的sql套上分頁條件把源sql轉換成支持分頁的sql,PageHelper支持mysql、oracle、db2
  • pageHelper分頁失效解決方案
    ,所以在平時的使用時,對於一對多分頁會出現分頁錯誤,這篇文章主要對pageHelper分頁錯誤進行重現以及提出解決方案。分析       mybatis進行一對多查詢時,映射文件(mapper.xml)中的sql語句中使用的左連接,pageHelper會自動對這條左連接sql語句進行select count(0)的處理,並把結果作為分頁結構的記錄總數,然後自動將limit拼接到sql語句末尾進行分頁,由於左連接查詢時,連接條件on條件不唯一(即一對多)時,結果會產生笛卡爾積,所以經過pagehelper
  • MySQL的limit用法和分頁查詢的性能分析及優化
    二、Mysql的分頁查詢語句的性能分析MySql分頁sql語句,如果和MSSQL的TOP語法相比,那麼MySQL的LIMIT語法要顯得優雅了許多。使用它來分頁是再自然不過的事情了。最基本的分頁方式:SELECT ... FROM ... WHERE ... ORDER BY ... LIMIT ...
  • Mysql跨庫分頁查詢方案總結
    背景上文說到,由於用戶表數據量過大,通過水平拆分用戶表到不同的資料庫實際上來提高資料庫性能。本文將分析如何解決不同資料庫實例上的數據分頁解決方案。現假設有N個資料庫實例,按照時間time排序,查詢偏移量為X,查詢數據量為Y。全局視野法全局視野法是通過將每一個資料庫實例上的分頁數據查詢到內存中,然後在服務層進行內存排序,得到分頁數據。
  • 深入剖析-關於分頁語句的性能優化
    一般分頁語句消耗資源的地方有兩點:1、返回where條件過濾的結果集;2、是對這個結果集進行排序,如果表過大同時對返回的結果集排序勢必導致性能嚴重下降,針對分頁語句性能低下的原因。object_id列選擇性非常高,對1000列進行排序性能也很高。
  • MySQL 分頁優化中的 「 INNER JOIN方式優化分頁算法 」 到底在什麼情況下會生效?
    如果是非聚集索引,先對索引分頁,然後再利用索引去查詢數據,先分頁索引確實可以減少掃描的範圍如果經常按照2中的方式查詢,也就是按照非聚集索引排序查詢,那麼為什麼不在該列上建立聚集索引呢。重現經典分頁「優化」,當沒有篩選條件,排序列為聚集索引的時候,並不會有所改善這裡來對比以下兩種寫法在聚集索引列作為排序條件時候的性能select * from t order by id limit
  • sqltoy-orm-4.16.16 發版,並行查詢場景增強、級聯增加排序
    sqltoy的十四個關鍵特點:1、最簡最直觀的sql編寫方式(不僅僅是查詢語句),採用條件參數前置處理規整法,讓sql語句部分跟客戶端保持高度一致2、sql中支持注釋(規避了對hint特性的影響,知道hint嗎?
  • 極致查詢性能 sqltoy-orm-4.12.8 發版增加對國產達夢資料庫支持
    更新內容1、主要增加對國產達夢資料庫的支持(目前除updateFetch:單次交互完成鎖記錄、修改、返回結果報遊標錯誤外其他功能都通過測試)2、修復因增加oceanBase方言導致sqlite方言未放入方言池中產生的bug3、增加findEntity分頁功能
  • 詳細的說說mysql的分頁查詢,請細品
    今天和大家聊聊mysql的分頁查詢,我想關於MySQL的分頁查詢,大家肯定都不陌生,為什麼要講呢?因為當初剛剛學習的時候,被這個分頁搞暈過,所以今天拿出來講講,也是重溫一下當年的感覺,好了,話不多說,直接開始吧!
  • MYSQL logstash 同步數據到es的幾種方案對比以及每種方案數據丟失原因分析.
    方案1每次查詢last_update_time 時間大於等於上一次數據時間的數據,分頁查詢。這個時候在2020-09-30 00:00:03的時候,同步sql有可能已經是翻頁翻到時間為2020-09-30 00:00:02的數據了,因此導致數據丟失。3)主從同步導致數據分頁查詢不到。一般都用es了,都會有主從同步,而es數據同步也是在從庫中讀取數據同步的。因此主從同步的這個時間延時也會導致數據查詢不正確。方案2不分頁查詢,避免分頁造成的數據丟失。
  • sqltoy-orm-4.16.11 發版,部分功能優化
    sqltoy的十四個關鍵特點:1、最簡最直觀的sql編寫方式(不僅僅是查詢語句),採用條件參數前置處理規整法,讓sql語句部分跟客戶端保持高度一致2、sql中支持注釋(規避了對hint特性的影響,知道hint嗎?
  • 提升SQL語句性能的方法
    用具體案例進行SQL語句性能提升的方法。先用實際案例分析了優化SQL語句的方法,然後再結合nat123這個實際案例分析了如何實現外網訪問內網Mysql資料庫的方法。線上mysql資料庫爆出一個慢查詢,DBA觀察發現,查詢時伺服器IO飆升,IO佔用率達到100%, 執行時間長達7s左右。優化方法:優化的總體思路是拆分sql,將排序操作和查詢所有信息的操作分開。
  • 別在用offset和limit分頁了
    「終於要對MySQL優化下手了,本文將對分頁進行優化說明,希望可以得到一個合適你的方案」前言分頁這個話題已經是老生常談了,但是有多少小夥伴一邊是既希望優化的自己的系統,另一邊在項目上還是保持自己獨有的個性。
  • 數據量很大,分頁查詢很慢,推薦個優化方案!
    當需要從資料庫查詢的表有上萬條記錄的時候,一次性查詢所有結果會變得很慢,特別是隨著數據量的增加特別明顯,這時需要使用分頁查詢。對於資料庫分頁查詢,也有很多種方法和優化的點。下面簡單說一下我知道的一些方法。準備工作為了對下面列舉的一些優化進行測試,下面針對已有的一張表進行說明。
  • sqltoy-orm-4.17.5 發布,支持 QueryExecutor 中定義分庫分表
    的十四個關鍵特點:1、最簡最直觀的sql編寫方式(不僅僅是查詢語句),採用條件參數前置處理規整法,讓sql語句部分跟客戶端保持高度一致2、sql中支持注釋(規避了對hint特性的影響,知道hint嗎?搜oracle hint),和動態更新加載,便於開發和後期維護整個過程的管理3、支持緩存翻譯和反向緩存條件檢索(通過緩存將名稱匹配成精確的key),實現sql簡化和性能大幅提升4、支持快速分頁和分頁優化功能,實現分頁最高級別的優化,同時還考慮到了cte多個with as情況下的優化支持5、支持並行查詢6、根本杜絕sql注入問題,以後不需要討論這個話題7、支持行列轉換
  • 比 mybatis 強大優雅的 sqltoy-orm-4.10.5 發版了
    感受sqltoy之美: https://chenrenfei.github.io/sqltoy/#/更新內容:1、緩存翻譯對應的緩存更新機制增加增量更新2、查詢結果計算增加環比計算,請參見sqltoy-showcase下的QueryCaseTest類sqltoy的代表性特性展示:1、最優雅的sql編寫模式
  • MySQL分頁優化解析
    所以,可以試著讓mysql也存儲分頁,當然要程序配合。(這裡只是提出一個設想,歡迎大家一起討論)ASP的分頁:在ASP系統中有Recordset對象來實現分頁,但是大量數據放在內存中,而且不知道什麼時候才失效(請ASP高手指點).
  • 比mybatis 強大優雅的 sqltoy-orm-4.10.5 發版了
    看一下sqltoy怎麼做吧!是不是變成了單表查詢,效率毫無疑問秒殺多表關聯無數倍!> 3、最高層級的分頁查詢優化:@fast 實現先分頁後關聯,page-optimize 將分頁2次查詢變成1.4次查詢。
  • MyBatisPlus&Plus原理包含分頁總結
    以下是執行SQL查詢的過程Executor:內部執行器StatementHandler:負責處理Mybatis與JDBC之間Statement的交互ParameterHandler:負責為 PreparedStatement 的 sql
  • 打造出色查詢:如何優化SQL查詢?
    5.優化limit分頁通常用limits來實現日常分頁,但當偏移量特別大時,查詢效率便會降低。因為Mysql不會跳過偏移量,而是直接獲取數據。哪種方案更好?10.謹慎使用distinct關鍵詞Distinct關鍵詞通常用於過濾重複記錄以返回唯一記錄。當其被用於查詢一個或幾個欄位時,Distinct關鍵詞將為查詢帶來優化效果。然而,在欄位過多的情況下,Distinct關鍵詞將大大降低查詢效率。