對於數據庫相關的業務,逃不過的數據分頁場景,不管是前臺分頁瀏覽仍是劃到頁面底部自動加載。對於分頁需求,各數據庫也提供了成熟的SQL支持,相似於Hibernate等ORM框架也集成了相關的方法。可是基於數據庫(框架)提供的分頁方法,咱們可否結合業務,提供更高效更優化的分頁方法呢?數據庫
數據查詢語句中,和分頁相關的有兩個參數,分別是:api
MySQL中的語法:框架
SELECT fields_list FROM table_name [ ORDER BY ... ] [LIMIT offset, limit];
但這個語法不兼容PostgreSQL,兼容MySQL和PostgreSQL的語法爲:優化
SELECT fields_list FROM table_name [ ORDER BY ... ] [ LIMIT {number | ALL} ] [ OFFSET number];
注:LIMIT和OFFSET都是可選字段。code
下文以第二種兼容的語法來進行描述。本文示例的數據庫表信息以下:
示例表爲訂單表,表名:orders,每頁顯示條數:10 page_sieze
。server
查詢第一頁:索引
SELECT * FROM orders ORDER BY order_id LIMIT 10 OFFSET 0;
查詢第二頁:開發
SELECT * FROM orders ORDER BY order_id LIMIT 10 OFFSET 10;
查詢第n頁:get
SELECT * FROM orders ORDER BY order_id LIMIT page_sieze OFFSET page_sieze * n;
若是熟悉Openstack API的開發人員會注意到,Openstack原生API採用了特殊的分頁方式:Openstack分頁以limit
和marker
兩個字段進行控制,limit控制每頁顯示數量,marker標識數據起始位置,即本分頁第一條數據的ID。
以nova list的API爲例,官方對兩個字段的說明以下:
Nova List APIit
Limit字段說明
Requests a page size of items. Returns a number of items up to a limit value. Use the limit parameter to make an initial limited request and use the ID of the last-seen item from the response as the marker parameter value in a subsequent limited request.
Marker字段說明
The ID of the last-seen item. Use the limit parameter to make an initial limited request and use the ID of the last-seen item from the response as the marker parameter value in a subsequent limited request.
以上描述中的關鍵信息是:若是分頁查詢,返回的JSON格式中會包含marker字段,指示下一頁數據第一條數據的ID。另外這個API也存在一個限制,就是分頁時,只能瀏覽上一頁/下一頁,而不能跳頁。
不過借鑑這個思路,咱們能夠對某些場景的分頁查詢進行優化。
對於按需自動加載(劃到頁面底部自動加載更多內容)或者只提供上一頁/下一頁瀏覽模式的場景,能夠進行以下優化:
查詢下一頁
SELECT * FROM orders WHERE order_id > page_last_id ORDER BY order_id LIMIT page_sieze OFFSET 0;
查詢上一頁
SELECT * FROM orders WHERE order_id < page_first_id ORDER BY order_id LIMIT page_sieze OFFSET 0;
小技巧:每次查詢數據時,多返回一條數據,即返回page_size + 1條數據,但顯示時去掉最後一條數據,經過這多出來一條數據,咱們能夠用來判斷數據是否還有下一頁。
另外對於能夠跳轉到任意頁面的場景,也能夠進行優化,這種可跳轉場景,分頁顯示也是有限的,通常模式是第一頁/上一頁/當前頁先後10頁……/下一頁/最後一頁,也就是說,分頁時,數據是在必定範圍內(先後10頁)移動,能夠以當前頁數據爲基礎,對數據進行過濾,減小數據掃描範圍。
考慮orders表有10W條記錄,每頁顯示10條,當前頁碼爲1000時的場景,若是按照單獨limit和offset模式,offset=1W,也就是數據庫要掃碼1W條記錄。假如如今翻頁要從1000頁跳轉到1005頁,咱們以第1000頁最後一條數據ID爲過濾條件,offset跳過1001-1004的40條數據便可。
查詢1005頁
SELECT * FROM orders WHERE order_id > page_1000_last_id ORDER BY order_id LIMIT page_sieze OFFSET page_size * 4;
這種方法相比基礎的分頁方式,只要order by字段是主鍵或索引字段,數據掃描的行數從1W多條降低到了幾十條,效率大大提高。