Mysql使用limit深度分頁優化
1、背景:
mysql使用select * limit offset, rows分頁在深度分頁的情況下。效能急劇下降。
- 例如:select * 的情況下直接⽤limit 600000,10 掃描的是約60萬條資料,並且是需要回表
60W次,也就是說⼤部分效能都耗在隨機訪問上,到頭來只⽤到10條資料(總共取600010條資料只留10條記錄)
2、limit 語法解讀
limit用於資料的分頁查詢,當然也會用於資料的擷取,下面是limit的用法:
SELECT * FROM table LIMIT [offset,] rows | rows OFFSET offset
變形
-
第一種:SELECT * FROM table LIMIT offset, rows # 常用形式
-- 從0開始,擷取5條記錄,即檢索行為1到5 select * from table limit 0,5 -- 注意: 關鍵字limit後面的兩個參與用逗號分割
-
第二種:SELECT * FROM table LIMIT rows OFFSET offset
-- 從0開始,擷取5條記錄,即檢索行為1到5 select * from tb_account limit 5 offset 0 -- 注意: 使用limit和offset兩個關鍵字,並且各帶一個引數,中間沒有逗號分割
-
第三種:SELECT * FROM table LIMIT rows
-- 擷取記錄的前五行資料,可以理解為offset的預設值為0 select * from tb_account limit 5
3、優化方式
1. 模仿百度、谷歌方案(前端業務控制)
類似於分段。我們給每次只能翻100頁、超過一百頁的需要重新載入後面的100頁。這樣就解決了每次載入數量資料大 速度慢的問題了
2. 記錄每次取出的最大id, 然後where id > 最大id
select * from table_name Where id > 最大id limit 10000, 10;
這種方法適用於:除了主鍵ID等離散型欄位外,也適用連續型欄位datetime等
最大id由前端分頁pageNum和pageIndex計算出來。
3. IN獲取id
select * from table_name where id in (select id from table_name where ( user = xxx )) limit 10000, 10;
4. join方式 + 覆蓋索引(推薦)
select * from table_name inner join ( select id from table_name where (user = xxx) limit 10000,10) b using (id)
如果對於有where 條件,又想走索引用limit的,必須設計一個索引,將where 放第一位,limit用到的主鍵放第2位,而且只能select 主鍵!
select id from test where pid = 1 limit 100000,10;`
建立索引:`alter table test add index idx_pid_id(pid, id)
4、案例
1. jdbcpagingReader使用方式
# MySqlPagingQueryProvider#
public static String generateLimitSqlQuery(AbstractSqlPagingQueryProvider provider, boolean remainingPageQuery,
String limitClause) {
StringBuilder sql = new StringBuilder();
sql.append("SELECT ").append(provider.getSelectClause());
sql.append(" FROM ").append(provider.getFromClause());
buildWhereClause(provider, remainingPageQuery, sql);
buildGroupByClause(provider, sql);
sql.append(" ORDER BY ").append(buildSortClause(provider));
sql.append(" " + limitClause);
return sql.toString();
}
- 解讀:jdbcPageingreader中使用了limit 10 這種寫法。預設是查出10條記錄。等價於 limit 0,10
2. db索引分割槽器使用方式
入參1: 表名 如test_table
入參2: 排序索引欄位 可以是主鍵,也可以是其他索引。需要保證是唯一索引即可。如:id
入參3: 主鍵可手動傳入,也可以根據表名計算出來:現在只支援單列主鍵的。 如:id
入參4: 具體表 要分多少塊。如:4
-- 使用過程 1. 先統計多少資料
select count(1) as countAllNumber from test_table; -- countAllNumber=200
-- 2. 在 根據需要分多少塊,算出每塊需要包含的資料量,即limit
-- countAllNumber /4 =200/4 =50; 也就是每塊的資料量需要包含50個數據。需要算這50個數據的開始節點和結束節點
-- 3. 迴圈遍歷按照主鍵自增的拍尋方式算出第一塊。
-- 3.1 第一塊開始節點為0
select id from test_table where id >=0 order by id limit 50,1; -- 算出第51個元素 如就51;那第一塊的範圍為【0,51);左閉右開
-- 3.2 第二塊 開始節點為51
select id from test_table where id >=51 limit 50,1; -- 算出第101個元素 如101;那第二塊的範圍為【51,101);左閉右開
-- 3.3 第三塊類似,算出第三塊的邊界點為151.
select id from test_table where id>=151 ; -- 算出第四塊的範圍為 【151,+∞);左閉右開
-
使用: 拿到每塊的分塊邊界值。進行主鍵查詢介面。
如第一塊,已經有邊界值為【0,51);
那麼拼接的查詢sql為 。需要的入參為表名,索引名,分割槽開始,分割槽結束
select id from test_table where id >=0 and id <51 order by id