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