大家好,我是程式設計師魚皮。最近老弟小阿巴放暑假,想找點事情做,於是就來問我:老鮍,我想做個練手專案,有沒有什麼好的建議?
我說:練手專案的話,就做個自己感興趣的唄,想加什麼功能就加什麼,做起來會更舒服~
小阿巴:Emm,我感興趣的太多了,有沒有推薦啊?
我說:那就想想自己經常使用的網站或 APP,選個對業務流程相對熟悉的。
小阿巴思考片刻,一拍腦袋:對啊,我天天用微信,那我就做個微信吧!說不定之後大家都在用我做的軟體聊天呢?
我一聽,不禁暗自驚歎,沒想到小夥子年紀輕輕,野心很大啊!
我說:想法不錯,但想做個微信這樣的 IM(即時通訊)專案,可沒有那麼簡單,你有什麼實現思路麼?說來聽聽?
小阿巴:微信的核心功能是收發訊息,我可以把使用者 A 傳送的訊息儲存到資料庫中,使用者 B 進入聊天介面時,從資料庫查詢出發給他的訊息就行。
我一聽這個回答,就知道以小阿巴目前的水平,想做出微信是不太可能了。。。
我問:Emm,暫且不考慮使用者體驗和效能,我們就先實現基礎功能吧,你會怎麼讓使用者檢視自己的歷史訊息呢?
小阿巴思考片刻,然後嘴角微微上揚,露出狡黠的笑容:你是不是以為我會說一次性把所有歷史訊息全部查出來?可惜啊老鮍,你把我想的太天真了,使用者可能有成百上千條歷史訊息,全量載入會很慢,所以我必然會使用 分頁
來查詢!
我說:行,那你打算怎麼分頁呢?
小阿巴:這還真難不倒我,這幾年我苦練增刪改查,分頁寫得很溜的!紙筆呈上來,看我給你手寫 SQL:
select * from message
where user = '魚皮'
limit 0, 20;
我說:Emm,老弟啊,聽我一句勸,咱先別想著做微信了,先實現一個訊息管理系統吧。
小阿巴:怎麼說?吾 SQL 不亦精乎?
其實這也是一道經典的場景題:即時通訊專案中怎麼實現歷史訊息的下拉分頁載入?
下面魚皮給大家講解一下。
如何實現下拉分頁載入?
業務場景
一般在即時通訊專案(比如聊天室)中,我們會採用下拉分頁的方式讓使用者載入歷史訊息記錄。
區別於標準分頁每次只展示當前頁面的資料,下拉分頁載入是 增量載入 的模式,每次下拉時會請求載入一小部分新資料,並放到已載入的資料列表中,從而形成無限滾動的效果,確保使用者體驗流暢。
比如使用者有 10 條訊息記錄,以 5 條為單位進行分頁,剛進入房間時只會載入最新的 5 條訊息:
下拉後,會載入歷史的第 6 - 10 條訊息:
理解了業務場景後,再看下實現方案,為什麼不建議使用傳統分頁實現下拉載入。
傳統分頁的問題
在傳統分頁中,資料通常是 基於頁碼或偏移量 進行載入的。如果資料在分頁過程發生了變化,比如插入新資料、刪除老資料,使用者看到的分頁資料可能會出現不一致,導致使用者錯過或重複某些資料。
舉個例子,對於即時通訊專案,使用者可能會持續收到新的訊息。如果按照傳統分頁基於偏移量載入,第一頁已經載入了第 1 - 5 行的資料,本來要查詢的第二頁資料是第 6 - 10 行(對應的 SQL 語句為 limit 5, 5),資料庫記錄如下:
結果在查詢第二頁前,突然使用者又收到了 5 條新訊息,資料庫記錄就變成了下面這樣。原本的第一頁,變成了當前的第二頁!
這樣就導致查詢出的第二頁資料,正好是之前已經查詢出的第一頁的資料,造成了訊息重複載入。所以不建議採用這種方法。
推薦方案 - 遊標分頁
為了解決這種問題,可以使用遊標分頁。使用一個遊標來跟蹤分頁位置,而不是基於頁碼,每次請求從上一次請求的遊標開始載入資料。
一般我們會選擇資料記錄的唯一識別符號(主鍵)、時間戳、或者具有排序能力的欄位作為遊標。比如即時通訊系統中的每個訊息,通常都有一個唯一自增的 id,就可以作為遊標。每次查詢完當前頁面的資料後,可以將最後一條訊息記錄的 id 作為遊標值傳遞給前端(客戶端)。
當要載入下一頁時,前端攜帶遊標值發起查詢,後端運算元據庫從 id 小於當前遊標值的資料開始查詢,這樣查詢結果就不會受到新增資料的影響。
對應的 SQL 語句為:
SELECT * FROM messages
WHERE id < :cursorId
ORDER BY id DESC
LIMIT 5;
擴充套件知識
其實遊標分頁是一種經典方案,它的應用場景很多,特別適用於增量資料載入、大資料量的高效能查詢和處理。除了 IM 系統獲取歷史訊息記錄之外,常見場景還有社交媒體資訊流、內容推薦系統、資料遷移備份等等。
遊標分頁還有很多擴充套件知識,篇幅原因就不在這裡展開了,感興趣的同學可以在我們的
最後
小阿巴聽完,長嘆道:唉,沒想到光是這麼一個小功能,就把我難住了。
小阿巴:得,那我先去做訊息管理系統了!🐶
更多
💻 程式設計學習交流:程式設計導航:https://www.code-nav.cn
📃 簡歷快速製作:老魚簡歷:https://laoyujianli.com
✏️ 面試刷題神器:面試鴨:https://mianshiya.com