表數據量大讀寫緩慢如何優化(2)【查詢分離】

上一篇聊到過,冷熱分離解決方案的性價比高,但它並非一個最優的方案,仍然存在諸多不足,好比:查詢冷數據慢、業務沒法再修改冷數據、冷數據多到必定程度系統依舊扛不住,咱們若是想把這些問題一一解決掉,能夠用另一種解決方案——查詢分離。(注意:查詢分離與讀寫分離仍是有區別的。面試

業務場景二

某 SaaS 客服系統,系統裏有一個工單查詢功能,工單表中存放了幾千萬條數據,且查詢工單表數據時須要關聯十幾個子表,每一個子表的數據也是超億條。數據庫

面對如此龐大的數據量,跟前面的冷熱分離同樣,每次客戶查詢數據時幾十秒才能返回結果,即使咱們使用了索引、SQL 等數據庫優化技巧,效果依然不明顯。編程

加上工單表中有些數據是幾年前的,可是這些數據涉及訴訟問題,須要繼續保持更新,所以沒法將這些舊數據封存到別的地方,也就無法經過前面的冷熱分離方案來解決。多線程

最終採用了查詢分離的解決方案,才得以將這個問題順利解決:將更新的數據放在一個數據庫裏,而查詢的數據放在另一個系統裏。由於數據的更新都是單表更新,不須要關聯也沒有外鍵,因此更新速度立馬獲得提高,數據的查詢則經過一個專門處理大數據量的查詢引擎來解決,也快速地知足了實際的查詢需求。架構

經過這種解決方案處理後,每次查詢數據時,500ms 內就可獲得返回結果,客戶不再抱怨了。併發

經過上面這個例子,你們對查詢分離的業務場景已經有了必定認知,但若是想掌握整個業務場景,繼續往下看吧。異步

什麼是查詢分離?

關於查詢分離的概念,從簡單的字面意思上也好理解,即每次寫數據時保存一份數據到另外的存儲系統裏,用戶查詢數據時直接從另外的存儲系統裏獲取數據。示意圖以下:ide

表數據量大讀寫緩慢如何優化(2)【查詢分離】

何種場景下使用查詢分離?

當在實際業務中遇到如下情形,則能夠考慮使用查詢分離解決方案。工具

  • 數據量大;
  • 全部寫數據的請求效率尚可;
  • 查詢數據的請求效率很低;
  • 全部的數據任什麼時候候均可能被修改;
  • 業務但願優化查詢數據的效率;

你們對查詢分離這個概念特別熟悉,可是對於查詢分離的使用場景一無所知,這可不行,只有瞭解了查詢分離的真正使用場景,才能在遇到實際問題時採起最正確的解決方案。大數據

查詢分離實現思路

在實際工做中,若是業務要求必須使用查詢分離的解決方案,咱們就務必掌握查詢分離的實現思路。也只有這樣,咱們真正遇到問題時纔能有條不紊地開展工做。

查詢分離解決方案的實現思路以下:

  1. 如何觸發查詢分離?
  2. 如何實現查詢分離?
  3. 查詢數據如何存儲?
  4. 查詢數據如何使用?

針對以上問題,咱們一點一點來討論。

(一)如何觸發查詢分離?

這個問題說明的是咱們應該在何時保存一份數據到查詢數據中,即何時觸發查詢分離這個動做。

通常來講,查詢分離的觸發邏輯分爲3種。

(1)修改業務代碼:在寫入常規數據後,同步創建查詢數據。

表數據量大讀寫緩慢如何優化(2)【查詢分離】

(2)修改業務代碼:在寫入常規數據後,異步創建查詢數據。

表數據量大讀寫緩慢如何優化(2)【查詢分離】

(3)監控數據庫日誌:若有數據變動,更新查詢數據。

表數據量大讀寫緩慢如何優化(2)【查詢分離】

經過觀察以上3種觸發邏輯示意圖,發現了什麼嗎?3種觸發邏輯的優缺點對比表以下:

修改業務代碼同步創建查詢數據 修改業務代碼異步創建查詢數據 監控數據庫日誌
優勢 一、保證查詢數據的實時性和一致性。二、業務邏輯靈活可控 一、不影響主流程 一、不影響主流程。二、業務代碼0侵入
缺點 一、侵入業務代碼。二、減緩寫操做速度。 一、查詢數據更新前,用戶可能會查詢到過期的數據。 一、查詢數據更新前,用戶可能會查詢到過期的數據。二、架構複雜一些

爲方便理解表中的內容,咱們來一塊兒聊一下其中的幾個概念。

什麼叫業務靈活邏輯可控?舉個例子:通常來講,寫業務代碼的人能從業務邏輯中快速判斷出何種狀況下更新查詢數據,而監控數據庫日誌的人並不能將所有的數據庫變動分支窮舉,再把全部的可能性關聯到對應的更新查詢數據邏輯中,最終致使任何數據的變動都須要從新創建查詢數據。

什麼叫減緩寫操做速度?創建查詢數據的一個動做能減緩多少寫操做速度?答案:不少。舉個例子:當你只是簡單更新了訂單的一個標識,原本查詢數據時間只須要 2ms,而在查詢數據時可能會涉及重建(好比使用 ES 查詢數據時會涉及索引、分片、主從備份,其中每一個動做又細分爲不少子動做,這些內容後面文章會聊到),這時創建查詢數據的過程可能就須要 1s 了,從 2ms 變成 1s,你說減緩幅度大不大?

查詢數據更新前,用戶可能查詢到過期數據。 這裏咱們結合第 2 種觸發邏輯來說,好比某個操做正處於訂單更新狀態,狀態更新時會經過異步更新查詢數據,更新完後訂單才從「待審覈」狀態變爲「已審覈」狀態。假設查詢數據的更新時間須要 1 秒,這 1 秒中若是用戶正在查詢訂單狀態,這時主數據雖然已變爲「已審覈」狀態,但最終查詢的結果仍是顯示「待審覈」狀態。

根據前面的對比表,總結每種觸發邏輯的適用場景以下:

觸發邏輯 適用場景
修改業務代碼,同步創建查詢數據 業務代碼比較簡單且對寫操做響應速度要求不高
修改業務代碼,異步創建查詢數據 業務代碼比較簡單且對寫操做響應
監控數據庫日誌 業務代碼比較複雜,或者改動代價太大

這裏,結合實戰案例說明下:在一個真實業務場景中,雖然咱們對業務的代碼比較熟悉,可是業務要求每次修改工單請求時響應速度快,咱們最終就選擇了修改業務代碼異步創建查詢數據這種觸發邏輯。

(二)如何實現查詢分離?

以上共談到 3 種觸發邏輯,第 1 種是同步創建查詢數據的過程比較簡單,這裏就不展開說明,第 3 種監控數據庫日誌我會在 13 講具體講解,因此這部份內容咱們主要圍繞第 2 種討論。

關於第 2 種觸發方案:修改業務代碼異步創建查詢數據,最基本的實現方式是單獨起一個線程創建查詢數據,不過這種作法會出現以下狀況:

  • 寫操做較多且線程太多,最終撐爆JVM。
  • 建查詢數據的線程出錯了,如何自動重試。
  • 多線程併發時,不少併發場景須要解決。

面對以上三種狀況,咱們該如何處理?此時使用MQ管理這些這些線程便可解決。

MQ的具體操做思路爲每次主數據寫操做請求處理時,都會發一個通知給MQ,MQ收到通知後喚醒一個線程更新查詢數據,示意圖以下:

表數據量大讀寫緩慢如何優化(2)【查詢分離】

瞭解了MQ的具體操做思路後,咱們還應該考慮如下5大問題。

問題一:MQ如何選型?

若是公司已使用 MQ,那選型問題也就不存在了,畢竟技術部門不會同時維護 2 套 MQ 中間件,而若是公司還沒使用 MQ,這就須要考慮選型問題了。

這裏我分享兩點選型原則,但願對你有幫助。

(1)召集技術中心全部能作技術決策的人共同投票選型。

(2)無論咱們選擇哪一個 MQ ,最終都能實現想要的功能,只不過是易用不易用、多寫少寫業務代碼的問題,所以咱們從易用性和代碼工做量角度考量便可。

問題二:MQ宕機了怎麼辦?

若是 MQ 宕機了,咱們只須要保證主流程正常進行,且 MQ 恢復後數據正常處理便可,具體方案分爲三大步驟。

  • 每次寫操做時,在主數據中加個標識:NeedUpdateQueryData=true,這樣發到 MQ 的消息就很簡單,只是一個簡單的信號告知更新數據,並不包含更新的數據 id。

  • MQ 的消費者獲取信號後,先批量查詢待更新的主數據,而後批量更新查詢數據,更新完後查詢數據的主數據標識 NeedUpdateQueryData 就更新成 false 了。

  • 固然還存在多個消費者同時搬運動做的狀況,這就涉及併發性的問題,所以問題與上一篇聊的冷熱分離中的併發性處理邏輯相似,這裏就不細聊了(有興趣的同窗能夠去看看)。

問題三:更新查詢數據的線程失敗了怎麼辦?

若是更新的線程失敗了,NeedUpdateQueryData 的標識就不會更新,後面的消費者會再次將有 NeedUpdateQueryData 標識的數據拿出來處理。但若是一直失敗,咱們能夠在主數據中多添加一個嘗試搬運次數,好比每次嘗試搬運時 +1,成功後就清零,以此監控那些嘗試搬運次數過多的數據。

問題四:消息的冪等消費

在編程中,一個冪等操做的特色是屢次執行某個操做均與執行一次操做的影響相同。

舉個例子,好比主數據的訂單 A 更新後,咱們在查詢數據中插入了 A,但是此時系統出問題了,系統誤覺得查詢數據沒更新,又把訂單 A 插入更新了一次。

所謂冪等,就是無論更新查詢數據的邏輯執行幾回,結果都是咱們想要的結果。所以,考慮消費端併發性的問題時,咱們須要保證更新查詢數據冪等。

問題五:消息的時序性問題

好比某個訂單 A 更新了 1 次數據變成 A1,線程甲將 A1 的數據搬到查詢數據中。不一下子,後臺訂單 A 又更新了 1 次數據變成 A2,線程乙也啓動工做,將 A2 的數據搬到查詢數據中。

所謂的時序性就是若是線程甲啓動比乙早,但搬運數據動做比線程乙還晚完成,就有可能出現查詢數據最終變成過時的 A1。以下圖(動做前面的序號表明實際動做的前後順序):

表數據量大讀寫緩慢如何優化(2)【查詢分離】

此時解決方案爲主數據每次更新時,都更新上次更新時間 last_update_time,而後每一個線程更新查詢數據後,檢查當前訂單 A 的 last_update_time 是否跟線程剛開始得到的時間同樣,且 NeedUpdateQueryData 是否等於 false,若是都知足的話,咱們就將 NeedUpdateQueryData 改成 true,而後再作一次搬運。

看到這,你心中可能有個疑問:MQ 在這裏的做用只是一個觸發信號的工具,若是不用 MQ 好像也沒啥問題啊,這你就大錯特錯了,MQ 的做用還很多呢,不信你往下看。

  • 服務的解耦:這樣主業務邏輯就不會依賴更新查詢數據這個服務了。
  • 控制更新查詢數據服務的併發量:若是咱們直接調用更新查詢數據服務,因寫操做速度快,更新查詢數據速度慢,寫操做一旦併發量高,會給更新查詢數據服務形成超負荷壓力。若是經過消息觸發更新查詢數據服務,咱們就能夠經過控制消息消費者的線程數來控制負載。

(三)查詢數據如何存儲?

咱們應該使用什麼技術存儲查詢數據呢?目前,市面上主要使用 Elasticsearch 實現大數據量的搜索查詢,固然還可能會使用到MongoDB、HBase 這些技術,這就須要咱們對各類技術的特性瞭如指掌,再進行技術選型。

關於技術選型這個問題,我以爲不少時候咱們不能單單隻考慮業務功能的需求,還須要考慮組織結構。團隊最熟悉哪款中間件,花費的成本最小,優先考慮的就應該是這種。

(四)查詢數據如何使用?

因 ES 自帶 API,因此使用查詢數據時,咱們在查詢業務代碼中直接調用 ES 的 API 就行。

不過,這個辦法會出現一個問題:數據查詢更新完前,查詢數據不一致怎麼辦?這裏分享 2 種解決思路。

  1. 在查詢數據更新到最新前,不容許用戶查詢。(咱們沒用過這種設計,但我確實見過市面上有這樣的設計。
  2. 給用戶提示:您目前查詢到的數據多是 1 秒前的數據,若是發現數據不許確,能夠嘗試刷新一下,這種提示用戶通常比較容易接受。

總體方案

以上,咱們已經把四個問題都討論完了,咱們再一塊兒看看查詢分離的總體方案,以下圖所示:

表數據量大讀寫緩慢如何優化(2)【查詢分離】

總結一下,本篇關於查詢分離的架構主要分爲四個部分:如何觸發查詢分離?如何實現查詢分離?查詢數據如何存儲?查詢數據如何使用?

歷史數據遷移

新的架構方案上線後,舊的數據如何適用新的架構方案?這是實際業務中須要咱們考慮的問題。

在這個方案裏,咱們只須要把全部的歷史數據加上這個標識:NeedUpdateQueryData=true,程序就會自動處理了。

查詢分離解決方案的不足

查詢分離這個解決方案雖然能解決一些問題,但咱們也要清醒地認識到它的不足。

不足一: 使用 Elasticsearch 存儲查詢數據時,注意事項是什麼(此方案並未詳細展開)?

不足二: 主數據量愈來愈大後,寫操做仍是慢,到時仍是會出問題。

不足三: 主數據和查詢數據不一致時,假設業務邏輯須要查詢數據保持一致性呢?

接下來的文章將會來聊使用elastic search作查詢數據的存儲系統時須要注意哪些問題,這個問題無論是面試仍是實際工做中,咱們都會碰到。一個技術使用起來並不難,難的是使用這個技術時你會碰到什麼問題,你又是如何解決的?

相關文章
相關標籤/搜索