1. 程式人生 > 其它 >Mysql使用limit深度分頁優化

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