溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

快狗打車CTO沈劍:數(shù)據(jù)庫架構(gòu)一致性最佳實踐

發(fā)布時間:2020-08-09 20:32:59 來源:ITPUB博客 閱讀:359 作者:云編 欄目:數(shù)據(jù)庫

   本文根據(jù)沈劍在2018年10月18日【第十屆中國系統(tǒng)架構(gòu)師大會(SACC2018)】現(xiàn)場演講內(nèi)容整理而成。

   講師介紹:

快狗打車CTO沈劍:數(shù)據(jù)庫架構(gòu)一致性最佳實踐

  沈劍,快狗打車CTO,互聯(lián)網(wǎng)架構(gòu)技術(shù)專家,“架構(gòu)師之路”公眾號作者。曾任百度高級工程師,58同城高級架構(gòu)師,58同城技術(shù)委員會主席。2015年調(diào)至58到家任高級總監(jiān),技術(shù)委員會主席,負責基礎(chǔ)架構(gòu),技術(shù)平臺,運維安全,信息系統(tǒng)等后端技術(shù)體系搭建。現(xiàn)任快狗打車CTO,負責快狗打車技術(shù)體系的搭建,本質(zhì)是技術(shù)人一枚。

   本文摘要:

  沈劍分享了快狗打車數(shù)據(jù)庫架構(gòu)的一致性實踐,在一致性實踐的過程中,能夠體現(xiàn)快狗打車數(shù)據(jù)庫架構(gòu)的演進歷程。從單庫到多庫再到高可用等等,包括在研究的過程中,每個階段可能會碰到不同的問題,快狗打車是采用一些什么樣的技術(shù)手段去解決這些問題?以快狗打車的實踐跟大家做一些分享。

   分享大綱:

  主從不一致,優(yōu)化實踐

  緩存不一致,優(yōu)化實踐

  數(shù)據(jù)冗余不一致,優(yōu)化實踐

  多庫事務(wù)不一致,優(yōu)化實踐

  總結(jié)

   演講正文:

  快狗打車(原58速運)是一個創(chuàng)業(yè)型公司,技術(shù)架構(gòu)、技術(shù)體系、數(shù)據(jù)庫架構(gòu)的變遷,和在座很多公司是很相近的,今天和大家聊一聊,我們在快狗打車數(shù)據(jù)庫架構(gòu)一致性方面碰到一些問題。

   不一致的優(yōu)化歷程,也是數(shù)據(jù)庫架構(gòu)演進的過程

  主線是我們的數(shù)據(jù)庫架構(gòu)變化的過程,在這個過程中,我列出了四個跟一致性相關(guān)的節(jié)點,主從會不一致、緩存會不一致、冗余數(shù)據(jù)會不一致、多庫多實例會不一致。不一致的優(yōu)化歷程,也是我們數(shù)據(jù)庫架構(gòu)演進的一個過程。從單庫到現(xiàn)在,有哪些坑在等著我們呢?

快狗打車CTO沈劍:數(shù)據(jù)庫架構(gòu)一致性最佳實踐

  先看一下,最初的數(shù)據(jù)庫架構(gòu),最早是這個樣子的。那個時候沒有什么微服務(wù)分層, web通過DAO訪問一個單庫數(shù)據(jù)庫,最早我這么玩的。單庫,它不具備什么高可用,高并發(fā)特性,擴展性也比較差。我相信很多創(chuàng)業(yè)公司初期也是這樣。

  單庫最早會遇到什么樣的瓶頸呢?在創(chuàng)業(yè)的時候,數(shù)據(jù)量變大了,并發(fā)量大了,業(yè)務(wù)變復(fù)雜了,整個系統(tǒng)的瓶頸最先出現(xiàn)在哪里?我的經(jīng)驗是數(shù)據(jù)庫。數(shù)據(jù)庫的瓶頸又會在哪里?我的經(jīng)驗是讀。因為絕大部分的業(yè)務(wù)是讀多寫少的業(yè)務(wù),讀,最容易稱為系統(tǒng)的瓶頸。

  最早在數(shù)據(jù)庫讀扛不住的時候,最先想到的優(yōu)化方式是什么?互聯(lián)網(wǎng)公司都講快,今天出問題,能不能明天后天給我搞定?最先想到的方案是什么,如何能快速擴充數(shù)據(jù)庫的讀性能呢?

快狗打車CTO沈劍:數(shù)據(jù)庫架構(gòu)一致性最佳實踐

  加兩個實例,主從同步,讀寫分離,這是創(chuàng)業(yè)型公司,當數(shù)據(jù)庫讀成為瓶頸的時候,最先想到的方案,快速擴充讀性能。主從同步碰到的問題是什么?這就是本主題要講的第一個問題,主從一致性的問題。

  當數(shù)據(jù)量越來越多,吞吐量越來越大的時候,寫到了主庫,主庫同步到從庫,主從同步存在延時,在延時窗口期內(nèi),讀寫分離去讀從庫,就有可能讀到一個舊數(shù)據(jù)。這個問題,我相信大家也會碰到。

  對于這個問題,不少接業(yè)務(wù)的解法方案是,忍,有些業(yè)務(wù)如果對一致性的要求沒這么高。但有沒有優(yōu)化方案呢?

快狗打車CTO沈劍:數(shù)據(jù)庫架構(gòu)一致性最佳實踐

  這兩個圖是我們的兩個常見的實踐。

  第一個是中間件,我們的服務(wù)層或者站點層不直接調(diào)數(shù)據(jù)庫,通過一個中間層,去調(diào)數(shù)據(jù)庫。中間層它能夠知道哪一個庫,哪一個表,哪一個KEY發(fā)生了寫操作,如果說接下來的這一段時間(假設(shè)主從同步一秒鐘完成),有讀請求落到從庫上,就會讀到舊數(shù)據(jù)。那么此時,中間件就要將讀請求,路由到主庫上去,讀新數(shù)據(jù)。

  第二個是強制讀主。第二個圖,雙主同步,強制讀主有什么好處?第一解決了高可用問題,雙主使用同一個VIP,一個主庫如果掛了,另一個主庫能隨時頂上,保障高可用。第二避免了主從之間的不一致。

  強制讀主它帶來的新的問題是什么呢?解決了一致性問題,但讀性能擴展的問題又來了,主庫抗讀寫,還是沒有解決讀性的擴大的問題。

  除了增加從庫,互聯(lián)網(wǎng)公司還有一種常見的提升系統(tǒng)讀性能的方式,緩存加服務(wù)化。抽象出服務(wù)層,向調(diào)用方屏蔽底層數(shù)據(jù)庫的復(fù)雜性,屏蔽數(shù)據(jù)庫的高可用的復(fù)雜性,屏蔽緩存的復(fù)雜性,對業(yè)務(wù)層提供服務(wù)。

快狗打車CTO沈劍:數(shù)據(jù)庫架構(gòu)一致性最佳實踐

  服務(wù)化加緩存確實是提升系統(tǒng)讀容量的架構(gòu)方案。通過緩存來提升讀性,又會遇到什么新的問題呢?用主從架構(gòu),有主從不一致問題;用緩存架構(gòu),當然也有緩存不一致的問題。只要你把同一份數(shù)據(jù)放在了多個地方,多個地方的修改有時間差,這個時間差就會有數(shù)據(jù)訪問不一致的問題。

  當我們出現(xiàn)數(shù)據(jù)庫與緩存中的數(shù)據(jù)不一致的時候,我們怎么來解決?

  首先來看一下為什么會不一致。緩存的常用玩法是“Cache Aside Pattern”。Cache Aside Pattern,旁路緩存,一般是怎么玩的?淘汰緩存,而不是更新緩存,這是Cache Aside Pattern的結(jié)論。

快狗打車CTO沈劍:數(shù)據(jù)庫架構(gòu)一致性最佳實踐

  讀寫時序是什么樣的?對于讀請求有緩存,毫無爭議的,先讀緩存,如果數(shù)據(jù)命中我就直接返回,如果數(shù)據(jù)沒有命中,讀從庫讀寫分離,把這個數(shù)據(jù)從從庫里拿出,放到緩存里,這是讀請求的一個流程。

  對于寫請求,Cache Aside Pattern的做法是,先寫數(shù)據(jù)庫,再淘汰緩存。在什么情況下會出現(xiàn)不一致?當并發(fā)量相對會比較高時,對于同一個KEY做了一個寫操作,馬上又來了一個讀操作,會出現(xiàn)什么樣的情況?先發(fā)生一個寫操作,先更新到數(shù)據(jù)庫,淘汰了Cache,馬上又來了一個讀操作,這個時候主從同步還沒同步完成,先讀緩存,緩存被剛剛的寫操作已經(jīng)淘汰掉了,又去讀從庫,把從庫的臟數(shù)據(jù)拿過來放到緩存里去,不一致就出現(xiàn)。

  高并發(fā)狀態(tài)下,寫后立即讀的場景,容易出現(xiàn)臟數(shù)據(jù)入Cache。

  大家發(fā)現(xiàn)沒有,這里的數(shù)據(jù)不一致,比主從的數(shù)據(jù)不一致的情況更嚴重。主從不一致,只有一個主動同步時間差不一致,同步之后,從庫就能讀到新數(shù)據(jù)了。但是緩存與數(shù)據(jù)庫的不一致,它會導(dǎo)致后續(xù)一直不一致,一旦臟數(shù)據(jù)入了緩存,臟數(shù)據(jù)會延續(xù)到下一個寫發(fā)生的時候才會被淘汰掉,所以它其實更嚴重。

  如何來解決呢?緩存和數(shù)據(jù)庫的數(shù)據(jù)不一致,我們的兩個實踐:異步淘汰緩存,確保從庫已經(jīng)同步成功;設(shè)定超時時間,極限情況下有機會修正。

  第一個,等從庫已經(jīng)完全同步成功,再去異步淘汰緩存?只要監(jiān)聽從庫的binlog,從庫binlog完成,一定是寫操作執(zhí)行完畢,此時再淘汰緩存,就能避免時間差。

  第二個,就是如果允許Cache miss,不要將緩存過期時間設(shè)為永久,如果你設(shè)置為無限長的過期時間,就沒有一個機會去修正不一致了。

  隨著業(yè)務(wù)的發(fā)展,除了流量的增加,我們要提升系統(tǒng)的讀性能,我們要提升系統(tǒng)的數(shù)據(jù)庫高可用,還會面臨一個什么問題?對了,數(shù)據(jù)量會增大。我們業(yè)務(wù)數(shù)據(jù)量越來越大了,通常采用什么樣的方式去解決?創(chuàng)業(yè)型公司,這兩個方案應(yīng)該是大家用得最多的。

快狗打車CTO沈劍:數(shù)據(jù)庫架構(gòu)一致性最佳實踐

  第一個,分庫。降低每個庫,降低每個實例的數(shù)據(jù)量,這樣就能夠承載更多的數(shù)據(jù)。分庫又帶來什么新的問題?舉了個例子,訂單一個庫,它有多個維度的查詢,有訂單ID的查詢,有用戶ID的查詢,有司機ID的查詢,一個庫沒有任何問題。

  但分庫以后,變成多個庫以后,一旦用了一個維度分庫,你會發(fā)現(xiàn)其他的維度的查詢就要變成多個庫了,是不是?

  一般來說是通過用戶的ID去分庫,在訂單ID里去放上分庫因子,這樣通過用戶ID以及訂單ID都能夠定位到相關(guān)數(shù)據(jù)。但是對于司機ID就不同了,司機ID和用戶ID是一個多對多的關(guān)系。一個用戶他可能下了多個司機的單,一個司機接了多個用戶的單,通過司機ID去查詢,并不能一次性查詢到所有的數(shù)據(jù),同一個司機的訂單一定是分布在多個庫里。怎么辦呢?此時最常用解決方案是,數(shù)據(jù)冗余。

  我用一個存儲元數(shù)據(jù),用一個存儲關(guān)系數(shù)據(jù),元數(shù)據(jù)通過用戶ID來分庫,保證同一個用戶的所有訂單在一個庫里。關(guān)系數(shù)據(jù)用司機ID來分庫,保證同一個司機的所有訂單在一個庫里。同一份數(shù)據(jù),由于它存在兩個維度的查詢,這兩個維度查詢都可以不夸庫,而通過數(shù)據(jù)冗余來實現(xiàn),這個在業(yè)內(nèi)屬于很常見的方案。

  數(shù)據(jù)冗余,又會出現(xiàn)什么問題?一起來看一下。上面是應(yīng)用,中間是服務(wù),一個數(shù)據(jù)存在兩個庫里,一個庫是通過用戶ID分庫,一個庫是通過司機ID去分庫,調(diào)用方來了一個請求,先要往第一份數(shù)據(jù)里寫一個數(shù)據(jù),再往另外一個庫里寫一個冗余數(shù)據(jù)。能保證冗余數(shù)據(jù)的一致性么?是不能夠保證,這兩個庫同時寫成功的,那怎么辦呢?

  這就是冗余數(shù)據(jù)的一致性問題。數(shù)據(jù)冗余數(shù)據(jù)的不一致優(yōu)化,今天介紹三種方法,其實本質(zhì)的方法論都是最終一致性。

快狗打車CTO沈劍:數(shù)據(jù)庫架構(gòu)一致性最佳實踐

  第一個方案是掃全量。怎么發(fā)現(xiàn)冗余數(shù)據(jù)不一致?寫個腳本,每天晚上跑,理論上A庫里有的B庫里面也有,一旦掃庫發(fā)現(xiàn)怎么A庫有B庫里沒有,就是出現(xiàn)不一致了,就要根據(jù)業(yè)務(wù)特性來做補償。到底是將后一半補進去,還是把前一半刪掉,跟業(yè)務(wù)特性相關(guān),不過思路大致是這樣的,一個異步的方式,最終來保證一致性。

快狗打車CTO沈劍:數(shù)據(jù)庫架構(gòu)一致性最佳實踐

  第二個方案是掃增量。通過服務(wù)操作兩個庫,寫成功第一個庫寫一條日志,寫成功第二個庫再寫一條日志。這些日志里的就是每天改變的數(shù)據(jù),每天不用掃描全量,只要掃描每天改變的數(shù)據(jù)就行了。如果掃描日志不匹配,就通過異步的方式修復(fù),保證最終一致性。

快狗打車CTO沈劍:數(shù)據(jù)庫架構(gòu)一致性最佳實踐

  第三個方式,比前兩種方式更加實時。不寫日志了,而是發(fā)消息。用一個消息組件,數(shù)據(jù)庫正向表操作成功了,發(fā)一個消息,冗余表操作成功了,發(fā)另一個消息。用一個異步的服務(wù)去監(jiān)聽這兩個消息,如果只有一條消息到達,就去數(shù)據(jù)庫檢測一致性,并用異步的方式來補償。

快狗打車CTO沈劍:數(shù)據(jù)庫架構(gòu)一致性最佳實踐

  最后是多實例多庫,這也是解決數(shù)據(jù)量大的一個常見方案。它會帶來什么樣的不一致呢?這里有一個案例,下單的一個操作,可能有三個數(shù)據(jù)要修改,一個是余額的數(shù)據(jù),我可能要扣減一些余額;一個是訂單的數(shù)據(jù),要新增一條訂單;一個是流水的數(shù)據(jù),要新增一條流水。原來是單庫事務(wù)來保證一致性,現(xiàn)在數(shù)據(jù)量大了,變成多個庫,余額是一個單獨的實例,訂單是一個單獨的實例,流水是一個單獨的實例,所以原來的一個事務(wù),在多庫狀態(tài)下,就變成三個事務(wù)。

  多實例,多庫事務(wù),不一致,怎么辦?這一塊我們有兩個優(yōu)化實踐。

快狗打車CTO沈劍:數(shù)據(jù)庫架構(gòu)一致性最佳實踐

  第一個是補償事務(wù),業(yè)內(nèi)應(yīng)該也經(jīng)常用到補償事務(wù)。

  余額操作,正向的操作是扣減余額,補償事務(wù)就是把余額加回來。

  訂單操作,正向的操作是新增訂單,補償事務(wù)就是把訂單刪除掉。

  流水操作,正向的操作是新增流水,補償事務(wù)就是把流水刪除。

  總之,補償事務(wù)就是當你發(fā)現(xiàn)前面的事務(wù)執(zhí)行失敗的時候,要執(zhí)行一個應(yīng)用層的事務(wù),回滾一個動作。

快狗打車CTO沈劍:數(shù)據(jù)庫架構(gòu)一致性最佳實踐

  另外一種方式,偽分布式事務(wù)的解決方案,是后置提交。

  先細化的看一下三個事務(wù)是怎么執(zhí)行的?第一個事務(wù)先執(zhí)行再提交,第二個事務(wù)執(zhí)行再提交,第三個事務(wù)執(zhí)行再提交。事務(wù)的執(zhí)行過程很慢,事務(wù)的提交過程很快。上圖這個例子,可能執(zhí)行時間200毫秒,提交時間幾毫秒,什么時候會出現(xiàn)不一致呢?第一個事務(wù)提交成功之后,最后一個事務(wù)提交成功之前的中間,任何一個地方出現(xiàn)異常都會導(dǎo)致不一致。

  優(yōu)化其實也很簡單,后置提交。第一個事務(wù)執(zhí)行,第二個事務(wù)執(zhí)行,第三個事務(wù)執(zhí)行;第一個事務(wù)提交,第二個事務(wù)提交,第三個事務(wù)提交。什么時候會出現(xiàn)不一致呢?仍然是第一個事務(wù)提交成功之后,第三個事務(wù)提交成功之前的時間間隔,如果出現(xiàn)了,網(wǎng)絡(luò)異常,服務(wù)器掛了,就會不一致。但是這個間隔就只有后面的兩毫秒,所以整個不一致的概率是降低了百倍左右。

  最后做一個簡單的總結(jié)。根據(jù)我的經(jīng)驗,40分鐘50分鐘的一個技術(shù)分享,第二天能夠記住的只有10%。如果只記住10%,那我希望大家能夠記住這一頁的內(nèi)容,并希望自己的邏輯是清晰的。

快狗打車CTO沈劍:數(shù)據(jù)庫架構(gòu)一致性最佳實踐

  數(shù)據(jù)庫架構(gòu)最初是單庫,單庫會碰到什么問題?會碰到讀性能瓶頸的問題。讀性能瓶頸最早用什么樣的方式去解決?主從同步讀寫分離,它會帶來什么問題?主從的不一致,用什么方案解決?我們的實踐是中間件,以及強制讀主。

  提升讀性能,服務(wù)化加緩存也是常見方案,帶來什么新的問題?緩存和數(shù)據(jù)庫的不一致。在Cache Aside Pattern的情況下,有寫后立即讀的問題,舊數(shù)據(jù)可能入緩存。我們的實踐,可以通過異步淘汰的方式,當寫操作在從庫上真正完成的時候再去淘汰緩存。同時,我們建議為所有允許Cache miss的數(shù)據(jù)設(shè)置超時時間。

  數(shù)據(jù)庫架構(gòu),數(shù)據(jù)量大的問題,怎么解決?常用的解決方案是分庫,多實例。分庫帶來什么新的問題?記得我的例子么,分了庫之后,可以保證同一個用戶的數(shù)據(jù)在同一個庫里,不能夠保證同一個司機數(shù)據(jù)也在同一個庫里,怎么解決?使用數(shù)據(jù)冗余。冗余帶來什么問題?冗余數(shù)據(jù)的不一致問題,方向是最終一致性。怎么最終保證一致性?掃全量,掃增量,實時消息對。除了多庫,多實例也可以擴展數(shù)據(jù)存儲量,會遇到什么問題?多庫的事務(wù)不能在保證原則性,補償事務(wù),后置提交,都是我們的優(yōu)化實踐。

  今天的內(nèi)容這么多,希望大家有收獲,謝謝大家。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI