上億數據怎麼玩深度分頁?兼容MySQL + ES + MongoDB

面試題 & 真實經歷

面試題:在數據量很大的狀況下,怎麼實現深度分頁?

你們在面試時,或者準備面試中可能會遇到上述的問題,大多的回答基本上是分庫分表建索引,這是一種很標準的正確回答,但現實老是很骨感,因此面試官通常會追問你一句,<font color="red">如今工期不足,人員不足,該怎麼實現深度分頁?</font>mysql

這個時候沒有實際經驗的同窗基本麻爪,So,請聽我娓娓道來。面試

慘痛的教訓

首先必須明確一點:深度分頁能夠作,可是<font color="red">深度隨機跳頁絕對須要禁止。</font>sql

上一張圖:數據庫

大家猜,我點一下第142360頁,服務會不會爆炸?bash

MySQLMongoDB數據庫還好,自己就是專業的數據庫,處理的很差,最多就是慢,但若是涉及到ES,性質就不同了,咱們不得不利用 SearchAfter Api,去循環獲取數據,這就牽扯到內存佔用的問題,若是當時代碼寫的不優雅,直接就可能致使內存溢出。併發

爲何不能容許隨機深度跳頁

從技術的角度淺顯的聊一聊爲何不能容許隨機深度跳頁,或者說爲何不建議深度分頁高併發

MySQL

分頁的基本原理:post

SELECT * FROM test ORDER BY id DESC LIMIT 10000, 20;

LIMIT 10000 , 20的意思掃描知足條件的10020行,扔掉前面的10000行,返回最後的20行。若是是LIMIT 1000000 , 100,須要掃描1000100 行,在一個高併發的應用裏,每次查詢須要掃描超過100W行,不炸纔怪。性能

MongoDB

分頁的基本原理:測試

db.t_data.find().limit(5).skip(5);

一樣的,隨着頁碼的增大,skip 跳過的條目也會隨之變大,而這個操做是經過 cursor 的迭代器來實現的,對於cpu的消耗會很是明顯,當頁碼很是大時且頻繁時,必然爆炸。

ElasticSearch

從業務的角度來講,ElasticSearch不是典型的數據庫,它是一個搜索引擎,若是在篩選條件下沒有搜索出想要的數據,繼續深度分頁也不會找到想要的數據,退一步講,假如咱們把ES做爲數據庫來使用進行查詢,在進行分頁的時候必定會遇到max_result_window 的限制,看到沒,官方都告訴你最大偏移量限制是一萬。

查詢流程:

  1. 如查詢第501頁,每頁10條,客戶端發送請求到某節點
  2. 此節點將數據廣播到各個分片,各分片各自查詢前 5010 條數據
  3. 查詢結果返回至該節點,而後對數據進行整合,取出前 5010 條數據
  4. 返回給客戶端

由此能夠看出爲何要限制偏移量,另外,若是使用 Search After 這種滾動式API進行深度跳頁查詢,也是同樣須要每次滾動幾千條,可能一共須要滾動上百萬,千萬條數據,就爲了最後的20條數據,效率可想而知。

再次和產品對線

<font color="red">俗話說的好,技術解決不了的問題,就由業務來解決!</font>

在實習的時候信了產品的邪,必須實現深度分頁 + 跳頁,現在必須撥亂反正,業務上必須有以下更改:

  • <font color="red">儘量的增長默認的篩選條件,如:時間週期</font>,目的是爲了減小數據量的展現
  • <font color="red">修改跳頁的展示方式,改成滾動顯示,或小範圍跳頁</font>

滾動顯示參考圖:

小規模跳頁參考圖:

通用解決方案

短期內快速解決的方案主要是如下幾點:

  • 必備:對排序字段,篩選條件務必設置好索引
  • 核心:<font color="red">利用小範圍頁碼的已知數據,或者滾動加載的已知數據,減小偏移量</font>
  • 額外:若是遇到很差處理的狀況,也能夠獲取多餘的數據,進行必定的截取,性能影響並不大

MySQL

原分頁SQL:

# 第一頁
SELECT * FROM `year_score` where `year` = 2017 ORDER BY id limit 0, 20;

# 第N頁
SELECT * FROM `year_score` where `year` = 2017 ORDER BY id limit (N - 1) * 20, 20;

經過上下文關係,改寫爲:

# XXXX 表明已知的數據
SELECT * FROM `year_score` where `year` = 2017 and id > XXXX ORDER BY id limit 20;

沒內鬼,來點乾貨!SQL優化和診斷 一文中提到過,LIMIT會在知足條件下中止查詢,所以該方案的掃描總量會急劇減小,效率提高Max!

ES

方案和MySQL相同,此時咱們就能夠隨用所欲的使用 FROM-TO Api,並且不用考慮最大限制的問題。

MongoDB

方案基本相似,基本代碼以下:

相關性能測試:

若是非要深度隨機跳頁

若是你沒有槓過產品經理,又該怎麼辦呢,不要緊,還有一絲絲的機會。

SQL優化 一文中還提到過MySQL深度分頁的處理技巧,代碼以下:

# 反例(耗時129.570s)
select * from task_result LIMIT 20000000, 10;

# 正例(耗時5.114s)
SELECT a.* FROM task_result a, (select id from task_result LIMIT 20000000, 10) b where a.id = b.id;

# 說明
# task_result表爲生產環境的一個表,總數據量爲3400萬,id爲主鍵,偏移量達到2000萬

該方案的核心邏輯即基於聚簇索引,在不經過回表的狀況下,快速拿到指定偏移量數據的主鍵ID,而後利用聚簇索引進行回表查詢,此時總量僅爲10條,效率很高。

所以咱們在處理MySQLESMongoDB時,也能夠採用同樣的辦法:

  1. 限制獲取的字段,只經過篩選條件,深度分頁獲取主鍵ID
  2. 經過主鍵ID定向查詢須要的數據

瑕疵:當偏移量很是大時,耗時較長,如文中的 5s

最後

參考文章:MongoDB中文社區

感謝 @程大設計師 爲我傾情設計的二維碼😜

若是以爲對你有用的話,不要忘記點個贊啊~

相關文章
相關標籤/搜索