历史数据如何处理_数据库表数据量大读写缓慢如何优化(1)【冷热分离】
今天討論的內容是冷熱分離,也許概念并不陌生,對其使用場景也比較熟悉,但涉及鎖的內容時仍然需要認真思考,這部分內容在我們實際開發中的“坑”還是不少的。
業務場景一
曾經經歷過供應鏈相關的架構優化,當時平臺上有一個訂單功能,里面的主表有幾千萬數據量,加上關聯表,數據量達到上億。
這么龐大的數據量,讓平臺的查詢訂單變得格外遲緩,查詢一次都要二三十秒,而且多點擊幾次就會出現宕機。比如業務員多次查詢時,數據庫的 CPU 會立馬狂飆,服務器線程也降不下來。
當時,我們嘗試了優化表結構、業務代碼、索引、SQL 語句等辦法來提高響應速度,但這些方法治標不治本,查詢速度還是很慢。
考慮到我們手頭上還有其他優先級高的需求需要處理,為此,我們跟業務方反饋:“這功能以后你們能不用就不用,暫時先忍受一下。”可經過一段時間后,業務方實在受不了了,直接跟我們放狠話,無奈之下我們屈服了。
最終,我們決定采用一個性價比高的解決方案,簡單方便地解決了這個問題。在處理數據時,我們將數據庫分成了冷庫和熱庫 2 個庫,不常用數據放冷庫,常用數據放熱庫。
通過這樣的方法處理后,因為業務員查詢的基本是近期常用的數據,常用的數據量大大減少了,就再也不會出現宕機的情況了,也大大提升了數據庫響應速度。
其實上面這個方法,就是“冷熱分離”。
一、什么是冷熱分離
冷熱分離就是在處理數據時將數據庫分成冷庫和熱庫 2 個庫,冷庫指存放那些走到了終態的數據的數據庫,熱庫指存放還需要修改的數據的數據庫。
二、什么情況下使用冷熱分離?
假設業務需求出現了如下情況,就可以考慮使用冷熱分離的解決方案:
數據走到終態后,只有讀沒有寫的需求,比如訂單完結狀態;
用戶能接受新舊數據分開查詢,比如有些電商網站默認只讓查詢3個月內的訂單,如果你要查詢3個月前的訂單,還需要訪問另外的單獨頁面。
三、冷熱分離實現思路
在實際操作過程中,冷熱分離整體實現思路如下:
1、如何判斷一個數據到底是冷數據還是熱數據?
2、如何觸發冷熱數據分離?
3、如何實現冷熱數據分離?
4、如何使用冷熱數據?
接下來,我們針對以上4個問題進行詳細的分析。
(一)如何判斷一個數據到底是冷數據還是熱數據?
一般而言,在判斷一個數據到底是冷數據還是熱數據時,我們主要采用主表里的 1 個或多個字段組合的方式作為區分標識。其中,這個字段可以是時間維度,比如“下單時間”這個字段,我們可以把 3 個月前的訂單數據當作冷數據,3 個月內的當作熱數據。
當然,這個字段也可以是狀態維度,比如根據“訂單狀態”字段來區分,已完結的訂單當作冷數據,未完結的訂單當作熱數據。
我們還可以采用組合字段的方式來區分,比如我們把下單時間 > 3 個月且狀態為“已完結”的訂單標識為冷數據,其他的當作熱數據。
而在實際工作中,最終究竟使用哪種字段來判斷,還是需要根據你的實際業務來定。
關于判斷冷熱數據的邏輯,這里還有 2 個注意要點必須說明:
如果一個數據被標識為冷數據,業務代碼不會再對它進行寫操作;
不會同時存在讀冷/熱數據的需求。
(二)如何觸發冷熱數據分離?
了解了冷熱數據的判斷邏輯后,我們就要開始考慮如何觸發冷熱數據分離了。一般來說,冷熱數據分離的觸發邏輯分3種。
1、直接修改業務代碼,每次修改數據時觸發冷熱分離(比如每次更新了訂單的狀態,就去觸發這個邏輯);
2、如果不想修改原來業務代碼,可通過監聽數據庫變更日志binlog的方式來觸發(數據庫觸發器也可);
3、通過定時掃描數據的方式來觸發(數據庫定時任務或通過程序定時任務來觸發);
針對以上三種觸發邏輯,選擇哪種比較好呢?看完以下表格的分析,你心里就有答案了。
| 優點 | 1、代碼靈活可控。2、保證實時性 | 1、與業務代碼解耦。2、可以做到低延時。 | 1、與業務代碼解耦。2、可以覆蓋根據時間區分冷熱數據的場景。 |
| 缺點 | 1、不能按照時間區分冷熱,當數據變為冷數據,期間可能沒有進行任何操作。2、需要修改所有數據寫操作的代碼。 | 1、無法按照時間區分冷熱,當數據變為冷數據,期間沒有進行任何操作。2、需要考慮數據并發操作的問題,即業務代碼與冷熱變更代碼同時操作同一數據。 | 1、不能做到實時性 |
根據表格內容對比,我們可以得出每種出發邏輯的建議場景。
修改寫操作的業務代碼:建議在業務代碼比較簡單,并且不按照時間區分冷熱數據時使用。
監聽數據庫變更日志:建議在業務代碼比較復雜,不能隨意變更,并且不按照時間區分冷熱數據時使用。
定時掃描數據庫:建議在按照時間區分冷熱數據時使用。
(三)如何分離冷熱數據?
分離冷熱數據的基本邏輯如下:
1、判斷數據是冷是熱;
2、將要分離的數據插入冷數據中;
3、再從熱數據庫中刪除分離的數據。
這個邏輯看起來簡單,而實際做方案時,以下三點我們都得考慮在內,這一點就不簡單了。
(1)一致性:同時修改過個數據庫,如何保證數據的一致性
這里提到的一致性要求,指我們如何保證任何一步出錯后數據還是一致的,解決方案為只要保證每一步都可以重試且操作都有冪等性就行,具體邏輯分為四步。
在熱數據庫中,給要搬的數據加個標識:flag=1。(1代表冷數據,0代表熱數據)
找出所有待搬的數據(flag=1):這步是為了確保前面有些線程因為部分原因失敗,出現有些待搬的數據沒有搬的情況。
在冷數據庫中保存一份數據,但在保存邏輯中需加個判斷以此保證冪等性(這里需要用事務包圍起來),通俗點說就是假如我們保存的數據在冷數據庫已經存在了,也要確保這個邏輯可以繼續進行。
從熱數據庫中刪除對應的數據。
(2)數據量大:假設數據量大,一次性處理不完,該怎么辦?是否需要使用批量處理?
前面說的3種冷熱分離的觸發邏輯,前 2 種基本不會出現數據量大的問題,因為每次只需要操作那一瞬間變更的數據,但如果采用定時掃描的邏輯就需要考慮數據量這個問題了。
這個實現邏輯也很簡單,在搬數據的地方我們加個批量邏輯就可以了。為方便理解,我們來看一個示例。
假設我們每次可以搬 50 條數據:
a. 在熱數據庫中給要搬的數據加個標識:flag=1;
b. 找出前 50 條待搬的數據(flag=1);
c. 在冷數據庫中保存一份數據;
d. 從熱數據庫中刪除對應的數據;
e. 循環執行 b。
(3)并發性:假設數據量大到要分到多個地方并行處理,該怎么辦?
在定時搬運冷熱數據的場景里(比如每天),假設每天處理的數據量大到連單線程批量處理都來不及,我們該怎么辦?這時我們就可以開多個線程并發處理了。(雖然大部分情況下多線程較快,但我曾碰到過這種情況:當單線程 batch size 到一定數值時效率特別高,比多線程任何 batch size 都快。所以,需要留意:如果遇到多線程速度不快,我們就考慮控制單線程。)
當多線程同時搬運冷熱數據,我們需要考慮如下實現邏輯。
第 1 步:如何啟動多線程?
因為我們采用的是定時器觸發邏輯,這種觸發邏輯性價比最高的方式是設置多個定時器,并讓每個定時器之間的間隔短一些,然后每次定時啟動一個線程就開始搬運數據。
還有一個比較合適的方式是自建一個線程池,然后定時觸發后面的操作:先計算待搬動的熱數據的數量,再計算要同時啟動的線程數,如果大于線程池的數量就取線程池的線程數,假設這個數量為 N,最后循環 N 次啟動線程池的線程搬運冷熱數據。
第 2 步:某線程宣布某個數據正在操作,其他線程不要動(鎖)。
關于這個邏輯,我們需要考慮 3 個特性。
獲取鎖的原子性: 當一個線程發現某個待處理的數據沒有加鎖,然后給它加鎖,這 2 步操作必須是原子性的,即要么一起成功,要么一起失敗。實際操作為先在表中加上 LockThread 和 LockTime 兩個字段,然后通過一條 SQL 語句找出待遷移的未加鎖或鎖超時的數據,再更新 LockThread=當前線程,LockTime=當前時間,最后利用 MySQL 的更新鎖機制實現原子性。
獲取鎖必須與開始處理保證一致性: 當前線程開始處理這條數據時,需要再次檢查下操作的數據是否由當前線程鎖定成功,實際操作為再次查詢一下 LockThread= 當前線程的數據,再處理查詢出來的數據。
釋放鎖必須與處理完成保證一致性: 當前線程處理完數據后,必須保證鎖釋放出去。
第 3 步:某線程正常處理完后,數據不在熱庫,直接跑到了冷庫,這是正常的邏輯,倒沒有什么特別需要注意的點。
第 4 步:某線程失敗退出了,結果鎖沒釋放怎么辦(鎖超時)?
鎖無法釋放: 如果鎖定這個數據的線程異常退出了且來不及釋放鎖,導致其他線程無法處理這個數據,此時該怎么辦?解決方案為給鎖設置一個超時時間,如果鎖超時了還未釋放,其他線程可正常處理該數據。
設置超時時間時,我們還應考慮如果正在處理的線程并未退出,因還在處理數據導致了超時,此時又該怎么辦?解決方案為盡量給超時的時間設置成超過處理數據的合理時間,且處理冷熱數據的代碼里必須保證是冪等性的。
最后,我們還得考慮一個極端情況:如果當前線程還在處理數據,此時正在處理的數據的鎖超時了,另外一個線程把正在處理的數據又進行了加鎖,此時該怎么辦?我們只需要在每一步加判斷容錯即可,因為搬運冷熱數據的代碼比較簡單,通過這樣的操作當前線程的處理就不會破壞數據的一致性。
(四)如何使用冷數據
在功能設計的查詢界面上,一般都會有一個選項供我們選擇需要查詢冷數據還是熱數據,如果界面上沒有提供,我們可以直接在業務代碼里區分。(說明:在判斷是冷數據還是熱數據時,我們必須確保用戶不允許有同時讀冷熱數據的需求。)
歷史數據如何遷移?一般而言,只要跟持久化層有關的架構方案,我們都需要考慮歷史數據的遷移問題,即如何讓舊架構的歷史數據適用于新的架構?
????????因為前面的分離邏輯在考慮失敗重試的場景時,剛好覆蓋了這個問題,所以這個問題的解決方案也很簡單,我們只需要給所有的歷史數據加上標識:flag=1 后,程序就會自動遷移了。
冷熱分離解決方案的不足
不得不說,冷熱分離解決方案確實能解決寫操作慢和熱數據慢的問題,但仍然存在諸多不足。
不足一: 用戶查詢冷數據速度依舊很慢,如果查詢冷數據的用戶比例很低,比如只有 1%,那么這個方案就沒問題。
不足二: 業務無法再修改冷數據,因為冷數據多到一定程度時,系統承受不住。(這點可以通過冷庫再分庫來解決,后面再來探討)
總結
以上是生活随笔為你收集整理的历史数据如何处理_数据库表数据量大读写缓慢如何优化(1)【冷热分离】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 讯飞linux_深度deepin又添一员
- 下一篇: spring配置主库从库_spring下