mysql實現(xiàn)游標分頁的方法詳解
這是一種基于排序字段值而不是偏移量的分頁方法。
核心思想
記住上一頁最后一條記錄的位置,然后從這個位置開始查詢下一頁
逐步分解
第一頁查詢(基礎(chǔ)查詢)
SELECT * FROM loc_common_info WHERE id NOT IN (1,2,3) -- 假設(shè)用戶去過場地1,2,3 AND status = 'ACTIVE' ORDER BY popularity_score DESC, id DESC LIMIT 3; -- 假設(shè)每頁3條
假設(shè)返回結(jié)果:
| id | name | popularity_score |
|---|---|---|
| 10 | 場地A | 95 |
| 8 | 場地B | 95 |
| 5 | 場地C | 90 |
關(guān)鍵點:我們記錄最后一條記錄的值:
lastScore = 90(id=5的popularity_score)lastId = 5(最后一條記錄的id)
第二頁查詢(使用游標)
SELECT * FROM loc_common_info
WHERE id NOT IN (1,2,3)
AND status = 'ACTIVE'
-- 關(guān)鍵條件:找到排在"最后一條記錄"之后的所有記錄
AND (popularity_score < #{lastScore}
OR (popularity_score = #{lastScore} AND id < #{lastId}))
ORDER BY popularity_score DESC, id DESC
LIMIT 3;
條件邏輯詳解
條件分解
AND (
popularity_score < 90
OR
(popularity_score = 90 AND id < 5)
)
這個條件的意思是:
popularity_score < 90:
- 找到所有評分嚴格小于90的記錄
- 這些記錄自然排在評分90的記錄后面
popularity_score = 90 AND id < 5:
- 找到評分等于90,但id更小的記錄
- 因為排序是
popularity_score DESC, id DESC,所以id小的排在后面
實際數(shù)據(jù)示例
假設(shè)數(shù)據(jù)庫中有這些數(shù)據(jù):
| id | popularity_score |
|---|---|
| 10 | 95 |
| 8 | 95 |
| 5 | 90 |
| 3 | 90 |
| 7 | 85 |
| 2 | 85 |
| 4 | 80 |
第二頁查詢條件:(score < 90) OR (score = 90 AND id < 5)
符合條件的記錄:
- id=3:滿足
score = 90 AND id < 5 - id=7:滿足
score < 90 - id=2:滿足
score < 90 - id=4:滿足
score < 90
排序后第二頁結(jié)果:
| id | popularity_score |
|---|---|
| 3 | 90 |
| 7 | 85 |
| 2 | 85 |
為什么需要復(fù)合排序ORDER BY score DESC, id DESC
如果只按 popularity_score DESC 排序:
問題場景:多個記錄有相同的popularity_score
第一頁:id=10(95), id=8(95), id=5(90) ← 最后一條id=5, score=90
第二頁應(yīng)該顯示:id=3(90), id=7(85), id=2(85)
但如果只按score排序,數(shù)據(jù)庫不保證相同score的記錄順序穩(wěn)定,可能導(dǎo)致:
- 第一次查詢:id=10(95), id=8(95), id=5(90)
- 第二次查詢:id=10(95), id=8(95), id=3(90) ← 順序變了!
復(fù)合排序確保:
- 主要按popularity_score降序
- score相同時,按id降序,保證順序絕對穩(wěn)定
前端-后端交互流程
// 第一頁請求 PageRequest request1 = new PageRequest(0, 3, null); // 沒有游標 PageResult result1 = service.getRecommendations(request1); // 返回結(jié)果包含下一頁游標 String nextCursor = result1.getNextCursor(); // 編碼為: "90_5" // 第二頁請求 PageRequest request2 = new PageRequest(0, 3, "90_5"); // 使用游標 PageResult result2 = service.getRecommendations(request2);
與傳統(tǒng)OFFSET分頁對比
OFFSET分頁(有問題)
-- 第一頁 SELECT ... LIMIT 0, 3; -- 返回記錄1,2,3 -- 第二頁 SELECT ... LIMIT 3, 3; -- 返回記錄4,5,6
問題:如果第一頁和第二頁之間有新數(shù)據(jù)插入,會導(dǎo)致:
- 重復(fù)記錄(新數(shù)據(jù)擠占了位置)
- 丟失記錄(原有記錄被擠到后面)
游標分頁(穩(wěn)定)
-- 第一頁:返回id=10,8,5,記住lastScore=90, lastId=5 -- 第二頁:查詢score<90 OR (score=90 AND id<5)的記錄
優(yōu)勢:
- 不受數(shù)據(jù)增刪影響
- 性能更好(不需要計算OFFSET)
- 順序絕對穩(wěn)定
總結(jié)
游標分頁的核心就是:記住上一頁的終點,從終點開始找下一頁,通過復(fù)合排序和精確的條件定位,確保分頁的準確性和穩(wěn)定性。
游標對象設(shè)計
@Data
public class RecommendationCursor {
private String type; // "new", "rotate", "retain"
private Double lastScore; // 上一頁最后一條的popularity_score
private Long lastId; // 上一頁最后一條的ID
private Integer page; // 當前頁碼
private String randomSeed; // 隨機種子
public static RecommendationCursor parseCursor(String cursorStr, String type) {
if (cursorStr == null) {
return new RecommendationCursor(type, null, null, 0, generateRandomSeed());
}
// 解析游標邏輯...
}
public String toCursorString() {
// 生成游標字符串邏輯...
}
}
關(guān)鍵要點
- 不要使用 OFFSET:LIMIT offset, count 在數(shù)據(jù)量大時性能差且不穩(wěn)定
- 游標分頁:適合主內(nèi)容區(qū),保證穩(wěn)定性和性能
- 隨機分頁:適合輪換區(qū),增加多樣性
- 復(fù)合排序:ORDER BY score DESC, id DESC 確保排序穩(wěn)定
- 狀態(tài)保持:通過游標或種子保持分頁狀態(tài)
這樣設(shè)計后,每次分頁查詢都能返回不同的內(nèi)容,同時保證性能和穩(wěn)定性。
到此這篇關(guān)于mysql實現(xiàn)游標分頁的方法詳解的文章就介紹到這了,更多相關(guān)mysql游標分頁內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Winserver2012下mysql 5.7解壓版(zip)配置安裝教程詳解
這篇文章主要介紹了Winserver2012下mysql 5.7解壓版(zip)配置安裝教程詳解,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2017-01-01

