mysql實(shí)現(xiàn)游標(biāo)分頁的方法詳解
這是一種基于排序字段值而不是偏移量的分頁方法。
核心思想
記住上一頁最后一條記錄的位置,然后從這個(gè)位置開始查詢下一頁
逐步分解
第一頁查詢(基礎(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)鍵點(diǎn):我們記錄最后一條記錄的值:
lastScore = 90(id=5的popularity_score)lastId = 5(最后一條記錄的id)
第二頁查詢(使用游標(biāo))
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)
)
這個(gè)條件的意思是:
popularity_score < 90:
- 找到所有評分嚴(yán)格小于90的記錄
- 這些記錄自然排在評分90的記錄后面
popularity_score = 90 AND id < 5:
- 找到評分等于90,但id更小的記錄
- 因?yàn)榕判蚴?
popularity_score DESC, id DESC,所以id小的排在后面
實(shí)際數(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 排序:
問題場景:多個(gè)記錄有相同的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相同時(shí),按id降序,保證順序絕對穩(wěn)定
前端-后端交互流程
// 第一頁請求 PageRequest request1 = new PageRequest(0, 3, null); // 沒有游標(biāo) PageResult result1 = service.getRecommendations(request1); // 返回結(jié)果包含下一頁游標(biāo) String nextCursor = result1.getNextCursor(); // 編碼為: "90_5" // 第二頁請求 PageRequest request2 = new PageRequest(0, 3, "90_5"); // 使用游標(biāo) 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ù)插入,會(huì)導(dǎo)致:
- 重復(fù)記錄(新數(shù)據(jù)擠占了位置)
- 丟失記錄(原有記錄被擠到后面)
游標(biāo)分頁(穩(wěn)定)
-- 第一頁:返回id=10,8,5,記住lastScore=90, lastId=5 -- 第二頁:查詢score<90 OR (score=90 AND id<5)的記錄
優(yōu)勢:
- 不受數(shù)據(jù)增刪影響
- 性能更好(不需要計(jì)算OFFSET)
- 順序絕對穩(wěn)定
總結(jié)
游標(biāo)分頁的核心就是:記住上一頁的終點(diǎn),從終點(diǎn)開始找下一頁,通過復(fù)合排序和精確的條件定位,確保分頁的準(zhǔn)確性和穩(wěn)定性。
游標(biāo)對象設(shè)計(jì)
@Data
public class RecommendationCursor {
private String type; // "new", "rotate", "retain"
private Double lastScore; // 上一頁最后一條的popularity_score
private Long lastId; // 上一頁最后一條的ID
private Integer page; // 當(dāng)前頁碼
private String randomSeed; // 隨機(jī)種子
public static RecommendationCursor parseCursor(String cursorStr, String type) {
if (cursorStr == null) {
return new RecommendationCursor(type, null, null, 0, generateRandomSeed());
}
// 解析游標(biāo)邏輯...
}
public String toCursorString() {
// 生成游標(biāo)字符串邏輯...
}
}
關(guān)鍵要點(diǎn)
- 不要使用 OFFSET:LIMIT offset, count 在數(shù)據(jù)量大時(shí)性能差且不穩(wěn)定
- 游標(biāo)分頁:適合主內(nèi)容區(qū),保證穩(wěn)定性和性能
- 隨機(jī)分頁:適合輪換區(qū),增加多樣性
- 復(fù)合排序:ORDER BY score DESC, id DESC 確保排序穩(wěn)定
- 狀態(tài)保持:通過游標(biāo)或種子保持分頁狀態(tài)
這樣設(shè)計(jì)后,每次分頁查詢都能返回不同的內(nèi)容,同時(shí)保證性能和穩(wěn)定性。
到此這篇關(guān)于mysql實(shí)現(xiàn)游標(biāo)分頁的方法詳解的文章就介紹到這了,更多相關(guān)mysql游標(biāo)分頁內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
MySQL事務(wù)與隔離級(jí)別的使用基礎(chǔ)理論
這篇文章主要介紹了MySQL事務(wù)的隔離級(jí)別詳情,事務(wù)隔離級(jí)別越高,為避免沖突所花費(fèi)的性能也就越多,即效率低。在“可重復(fù)讀”級(jí)別,實(shí)際上可以解決部分的虛讀問題,但是不能防止update更新產(chǎn)生的虛讀問題,要禁止虛讀產(chǎn)生,還是需要設(shè)置串行化隔離級(jí)別2023-02-02
淺談開啟magic_quote_gpc后的sql注入攻擊與防范
通過啟用php.ini配置文件中的相關(guān)選項(xiàng),就可以將大部分想利用SQL注入漏洞的駭客拒絕于門外2012-01-01
解決ERROR?2003?(HY000):?Can‘t?connect?to?MySQL?server?
在Windows系統(tǒng)上使用Django連接Ubuntu虛擬機(jī)中的MySQL數(shù)據(jù)庫時(shí),遇到無法連接的問題,排查后發(fā)現(xiàn)是由于MySQL綁定的IP地址改變導(dǎo)致的,下面就來介紹一下問題解決,感興趣的可以了解一下2024-09-09
MySQL下高可用故障轉(zhuǎn)移方案MHA的超級(jí)部署教程
這篇文章主要介紹了MySQL下高可用故障切換方案MHA的超級(jí)部署教程,文中隊(duì)MHA方案的一些特點(diǎn)做了介紹,示例基于Linux系統(tǒng)的服務(wù)器環(huán)境,需要的朋友可以參考下2015-12-12
MySQL與MSSQl使用While語句循環(huán)生成測試數(shù)據(jù)的代碼
有時(shí)候我們測試性能的時(shí)候經(jīng)常需要生產(chǎn)大量的測試數(shù)據(jù),用sql語句直接生成的數(shù)據(jù)更快,需要的朋友可以參考下。2010-12-12

