各位志同道合的朋友們大家好,我是一個一直在一線互聯(lián)網(wǎng)踩坑十余年的編碼愛好者,現(xiàn)在將我們的各種經(jīng)驗以及架構(gòu)實戰(zhàn)分享出來,如果大家喜歡,就關(guān)注我,一起將技術(shù)學(xué)深學(xué)透,我會每一篇分享結(jié)束都會預(yù)告下一專題
一般我們業(yè)務(wù)在讀多寫少的場景下,遇到的第一個瓶頸就是數(shù)據(jù)庫這塊,大量的讀請求會來到數(shù)據(jù)庫,這樣如果你初期部署的一個數(shù)據(jù)庫就會造成IO大量增加,使得請求變慢,甚至?xí)ㄋ勒麄€數(shù)據(jù)庫,到了這個階段,我們一般會將讀請求和寫請求進(jìn)行分開數(shù)據(jù)處理,即采用主從讀寫分離的方式。
注:這里說的是主從并不是主備,主從中的從服務(wù)器是要承擔(dān)業(yè)務(wù)的,而主備中備份機(jī)器一般只作為備份存在。
我們采用主從讀寫分離的方式,目的是為了將更多的讀請求進(jìn)行分發(fā)來緩解我們的大量讀請求。
讀寫分離架構(gòu)原理
正如上面所說,讀寫分離是為了將請求流量分散到不同的數(shù)據(jù)庫節(jié)點上,將寫入數(shù)據(jù)的請求分發(fā)到主數(shù)據(jù)庫,讀取數(shù)據(jù)的請求分發(fā)到從數(shù)據(jù)庫,從數(shù)據(jù)可以有多臺,即一主多從。如下圖:
從上圖可看出,有個關(guān)鍵技術(shù)就是主從復(fù)制,每次寫入數(shù)據(jù)的時候,需要將主服務(wù)器數(shù)據(jù)復(fù)制到從服務(wù)器中,用來確保數(shù)據(jù)一致性。下面我們來單獨看看主從是怎么復(fù)制的,以我們互聯(lián)網(wǎng)中最熟悉的MySQL為例。
- 從服務(wù)器連接上主服務(wù)器,啟動復(fù)制的時候,則會自身創(chuàng)建一個IO線程去像主數(shù)據(jù)庫服務(wù)器拉取binlog的更新信息。
- 把拉過來的binlog信息寫到自己服務(wù)器的一個relay log日志文件中。
- 從數(shù)據(jù)庫服務(wù)器創(chuàng)建一個SQL線程,是為了將relay log的所有日志信息,進(jìn)行sql回寫到自己的數(shù)據(jù)庫中,這樣就和主庫的數(shù)據(jù)一模一樣了。
- 當(dāng)主數(shù)據(jù)庫有數(shù)據(jù)更新的時候,比如新插入了一條或者update了一條數(shù)據(jù),這時候主庫會將這些數(shù)據(jù)更新到binlog二進(jìn)制文件中,同時,主庫會創(chuàng)建一個binlog dump線程,這個線程將更新了的binlog信息發(fā)送到從庫的IO線程,需要注意的是,這個過程是異步的,如果等著從庫接受完成,是不是特別慢,且影響性能。
這樣的“一主多從”的主從復(fù)制方案做好之后,現(xiàn)在咱們就不怕當(dāng)前這些大量的讀請求了,因為我們把這些大流量讀請求都分發(fā)到這些從數(shù)據(jù)庫中了,寫入數(shù)據(jù)的請求依然還是寫到主數(shù)據(jù),一點不影響我們讀的業(yè)務(wù),互不影響的。同時,從數(shù)據(jù)庫還可以作為備份數(shù)據(jù)庫來使用,萬一主庫突然故障了,它可以頂上去防止數(shù)據(jù)丟失。
但是,我們不能一味的加從數(shù)據(jù)庫,加個十七八個的,這樣做是無腦的操作啊,你想想看,你加一堆的的從數(shù)據(jù)庫連接到數(shù)據(jù)庫,復(fù)制他的數(shù)據(jù),太多的IO線程會造成主數(shù)據(jù)不堪重負(fù)的,就會造成你寫入數(shù)據(jù)慢,還會卡死,這就悲劇了。所以,不能這么搞啊,一般生產(chǎn)環(huán)境一個主數(shù)據(jù)庫掛三個從數(shù)據(jù)就行了,最多不能超過5個以上,要是還是不滿足肯定就會有其他方案了啊,多級緩存方案啊,是不是,后面會講的。
主從延時
上面說了主從讀寫分離的那么多好處, 主從同步是有延遲的,當(dāng)然,這個延時一般都在ms級別,但是如果到了秒級別,可能就會對有些業(yè)務(wù)造成影響,看我們能否接受。比如,我們在支付系統(tǒng)創(chuàng)建支付單后,風(fēng)控系統(tǒng)會進(jìn)入查詢作出相應(yīng)的風(fēng)控操作,如果查不到就會可能終止本次交易了。
主從延遲優(yōu)化
- 我們可以在這些立馬需要查的業(yè)務(wù),讓它直接查主數(shù)據(jù)庫,但是這種方案不推薦,因為量大怕會拖垮主數(shù)據(jù)
- 當(dāng)從數(shù)據(jù)庫讀取不到,我們回去再讀主數(shù)據(jù),這樣就能讀到數(shù)據(jù)了。但是,這種也是有風(fēng)險的,量大也會拖垮主庫。
- 像我前面文章提到過,分重要性業(yè)務(wù)和非重要業(yè)務(wù),將很核心的幾個業(yè)務(wù)查主數(shù)據(jù),其他非核心,讀寫分離。
- 使用緩存,將新增的數(shù)據(jù)同時添加一份緩存,然后查緩存數(shù)據(jù),這種建議新增的數(shù)據(jù)使用緩存較好。
- 使用消息隊列中間件進(jìn)行消息冗余,將新的主數(shù)據(jù)內(nèi)容,通過消息中間件MQ冗余一份當(dāng)前數(shù)據(jù),然后發(fā)到需要查詢的系統(tǒng)。
在消息體不大,推薦使用第 5 種優(yōu)化方案,需要消息中間件;其次考慮緩存, redis,Memcache 都可以的,因為更新的操作,需要更新緩存,也需要解決一致性問題,所以,新增的數(shù)據(jù),就首選緩存優(yōu)化方案。最后,推薦重要性非重要性隔離方案。最好不要使用都查詢主庫的操作。
如何優(yōu)雅使用讀寫分離
我們現(xiàn)在使用了數(shù)據(jù)庫讀寫分離的機(jī)制,但是我們代碼該怎么去友好的去訪問數(shù)據(jù)庫呢?以前我們一個數(shù)據(jù)源配置就可以了,現(xiàn)在有好多個數(shù)據(jù)源了,代碼里既要區(qū)分哪個地方使用寫數(shù)據(jù)庫的數(shù)據(jù)源,哪個地方需要使用讀數(shù)據(jù)的數(shù)據(jù)源。當(dāng)然,肯定是有辦法的,業(yè)界大佬們都早于我們遇到了這些問題,下面我會分享出兩種方案:
1,程序代碼嵌入
代碼嵌入,是指通過在我們的代碼中開發(fā)出數(shù)據(jù)庫訪問中間層,由這個數(shù)據(jù)庫訪問中間層去訪問不同的數(shù)據(jù)源,以實現(xiàn)讀寫分離和數(shù)據(jù)源的管理?,F(xiàn)在推薦使用淘寶開源的TDDL(Taobao Distributed Data Layer),使用方便,直接集成到我們代碼就可以了,它自己管理讀寫分離和數(shù)據(jù)庫配置。
特點是:
- 實現(xiàn)簡單,可以根據(jù)自己業(yè)務(wù)進(jìn)行定制化開發(fā)
- 語言不同,就得開發(fā)不同語言版本的數(shù)據(jù)庫訪問層
2,部署獨立代理層
部署代理層是指,在我們的業(yè)務(wù)服務(wù)器和數(shù)據(jù)庫直接引入數(shù)據(jù)訪問代理層,并不用自己寫代碼。現(xiàn)在為代表的開源中間件有阿里的MyCat、360的Atlas、美團(tuán)的DBProxy等。這些都是使用標(biāo)準(zhǔn)的MySql通信協(xié)議。
特點:
- 不用自己編寫多余代碼,使用方便。
- 支持對語言
- sql語句會跨兩層網(wǎng)絡(luò),性能稍微低一點。
建議,在自己公司沒有比較成熟的中間件團(tuán)隊的話就用程序代碼封裝的方案,雖然寫代碼麻煩點,但是自己可以把控;要是公司有成熟中間件團(tuán)隊,就盡量考慮代理層部署的方案,因為需要有個團(tuán)隊要研究和長期維護(hù)這個代理層,才能確保業(yè)務(wù)正常發(fā)展,現(xiàn)在我們公司大部分都用的是代理層方案,是有個專門團(tuán)隊來維護(hù)。
總結(jié),今天講到了當(dāng)我們讀多寫少的場景下,采取數(shù)據(jù)庫讀寫分離的方式來分?jǐn)偞罅髁?。從而引出了主從?fù)制,并且對主從復(fù)制的延遲進(jìn)行了優(yōu)化方案的講解和給出來相應(yīng)的建議,希望對大家有所幫助。如果大家喜歡就關(guān)注我,讓我們一起聊公司的各種技術(shù),共同學(xué)習(xí)共同進(jìn)步,有什么想說的是想學(xué)的評論區(qū)里說啊,大家都可以給方案,謝謝