天天看點

資料庫架構

分庫

mer,l1u

fre,

acc,

tra,

other,

backup

tra中的chinaarea表copy到l1u中

資料庫架構演變

架構的拓展周期的想法(僅個人觀點)

首先,我認為初期規劃不該太複雜或者龐大,無論項目的中長期可能會發展地如何如何,前期都應該以靈活為優先,像分庫分表等操作不應該在開始的時候就考慮進去。

其次,我認為需求變更是非常正常的,這點在我等開發的圈子裡吐槽的最多,其中自然有 “上司們” 在業務方面欠缺整體考慮的因素,但我們也不該局限在一個觀點内,市場中變則通,不變則死,前期更是如此,是以在前幾版的架構中我們必須要考慮較高的可擴充性。

最後,當項目經過幾輪市場的洗禮和疊代開發,核心業務趨于穩定了,此時我們再結合中長期的規劃給系統來一次重構,細緻地去劃分領域邊界,該解耦的解耦,該拆分的拆分。

======================================================

也許有的同學會提出分庫、分表、分區、拆字段、上緩存、上搜尋引擎、上大資料分析…但是這些執行完畢後,項目代碼也要配套更新,緊接着是測試、安全、并發等等問題亟待解決(目前團隊穩定麼?有這些真實力麼?技術成本提高後緊接着帶來的是招聘成本和維護成本)…

一個進階工程師的月薪應該差不多夠改善硬體了吧?不夠?那麼整個研發部門的月薪呢?項目穩定性帶來的業務市場直接價值和潛在價值呢?

架構設計時就要訂好分庫政策,否則一方面技術設計會沒有預留,另一方面資料模型設計上也會缺乏預留。

分庫有N種政策,比如:活躍庫/曆史庫,讀寫分離,業務垂直分離 等等。

架構設計初期,訂好未來将采用的分庫政策,然後按該政策預留好技術支援和資料模型上的支援,比如:更新時間戳、資料屬地資訊、資料的業務分類資訊等,就需要在多張表中都做備援存儲。

初期如果沒有訂好政策,等系統跑個3年後再來分庫,會十分、相當、顯著的痛苦。當然,如果堅持用個3~5年,然後跟客戶說系統老了該掏點錢讓我們更新換代了,也是個方案。

分庫有的是用代理,這樣的話之後分庫不需要修改代碼。但是如果是基于自身api的,分庫後代碼改動就很大,之前的單資料源就要變成多資料源。而且分庫後很多查詢操作也不能用原來的了。

設計資料庫時我們一般會把常用、短類型字段放在主表(盡量打造成靜态表),把不常用、長類型字段放在附表,最終2個或者多個表進行JOIN,附表中适當的維護使用備援字段,也是不錯的選擇!

單表單庫--->分表分庫--->主從複制--->讀寫分離--->增加緩存

在資料庫前端增加緩存redis或memcached

初期單庫單表

單庫單表已經達到業務忍受的極限,這時候不管在硬體還是在參數調優上都已經無法滿足業務時,就分庫分表

分庫分表一共有兩種方案:

1) 垂直拆分

2) 水準拆分

當資料庫達到一定規模後(比如說大幾千萬以上),拆分是必須要考慮的。一般來說我們首先要進行垂直拆分,即按業務分割,比如說使用者相關、訂單相關、統計相關等等都可以單獨成庫。

但僅僅如此這是完全不夠的,垂直拆分雖然剝離了一定的資料,但每個業務還是那個數量級,是以我們還得采取水準拆分進一步分散資料。

分庫分表的優點相信上述兩圖都一目了然了,一個是專庫專用,業務更集中,另一個是提升資料庫服務的負載能力。

But there are always two sides to a coin。 從此以後你要接受你的系統複雜度将提升一個檔次,疊代、遷移、運維等都不再容易。

垂直拆分在實作上就是一個多資料源的問題,沒啥好講的,基于 Sharding-JDBC 中間件

單表的資料量限制,當單表資料量到一定條數之後資料庫性能會顯著下降。資料多了之後,對資料庫的讀、寫就會很多。

分庫減少單台資料庫的壓力。

接觸過幾個分庫分表的系統,都是通過主鍵進行散列分庫分表的。這類資料比較特殊,主鍵就是唯一的擷取該條資訊的主要途徑。比如:京東的訂單、财付通的交易記錄等。該類資料的用法,就是通過訂單号、交易号來查詢該筆訂單、交易。

還有一類資料,比如使用者資訊,每個使用者都有系統内部的一個userid,與userid對應的還有使用者看到的登入名。那麼如果分庫分表的時候單純通過userid進行散列分庫,那麼根據登入名來擷取使用者的資訊,就無法知道該使用者處于哪個資料庫中。

或許有朋友會說,我們可以維護一個email----userid的映射關系,根據email先查詢到userid,在根據userid的分庫分表規則到對應庫的對應表來擷取使用者的記錄資訊。這麼做是可以的,但是這個映射關系的條數本身也是個瓶頸,原則上是沒有減少單表内資料的條數,算是一個單點。并且要維護這個映射關系和使用者資訊的一緻性(修改登入名、多登入名等其他特殊需求),最大一個原因,其實使用者資訊是一個讀大于寫的庫,web2.0都是以使用者為中心,所有資訊都和使用者資訊相關聯,是以對使用者資訊拆分還是有一定局限性的。

對于這類讀大于寫并且資料量增加不是很明顯的資料庫,推薦采用讀寫分離+緩存的模式,試想一下一個使用者注冊、修改使用者資訊、記錄使用者登入時間、記錄使用者登入IP、修改登入密碼,這些是寫操作。但是以上這些操作次數都是很小的,是以整個資料庫的寫壓力是很小的。唯一一個比較大的就是記錄使用者登入時間、記錄使用者登入IP這類資訊,隻要把這些經常變動的資訊排除在外,那麼寫操作可以忽略不計。是以讀寫分離首要解決的就是經常變化的資料的拆分,比如:使用者登入時間、記錄使用者登入IP。這類資訊可以單獨獨立出來,記錄在持久化類的緩存中(可靠性要求并不高,登陸時間、IP丢了就丢了,下次來了就又來了)

以oracle為例,主庫負責寫資料、讀資料。讀庫僅負責讀資料。每次有寫庫操作,同步更新cache,每次讀取先讀cache再讀DB。寫庫就一個,讀庫可以有多個,采用dataguard來負責主庫和多個讀庫的資料同步。

1、 垂直拆分

所謂垂直拆分,就是将單一資料庫拆分成多個資料庫,可以考慮的方案:

1) 根據業務邏輯進行拆分

2) 根據冷熱資料進行拆分

這裡以商品--訂單--使用者為例,當多種類商品存放于一個資料庫和一張表中時,随着時間推移,資料量增大,單庫單表查詢能力下降,可以根據商品種類來進行拆分,比如公司的産品:高中、國中、國小,由單個表拆分成三個獨立表。如果還大,繼續拆分,高中拆分成高一、高二、高三。國中拆分成初一、初二、初三。國小拆分成小一、小二......小六。(所有的分庫分表都源于生活中的邏輯)。

我們生産環境中針對資料進行了拆分,冷資料(采用MyISAM引擎),熱資料(采用Innodb引擎),同時将MyISAM引擎的資料庫的主控端多采用redis或memcache進行緩存,Innodb引擎的資料庫的主控端針對業務情況配置設定計算型和記憶體型實體機,還是混合型,同時可以考慮使用redis做部分存儲,用于緩解資料庫壓力。

垂直拆分優缺點:

優點:

1) 由單庫單表拆分成多庫多表,降低了資料庫增删改查壓力

2) 對冷熱資料拆分,降低了成本并合理利用硬體

3) 對于垂直拆分的資料庫,在設計資料庫時就應當考慮好資料庫架構的延展性,(否則會對後期的擴充造成很大的阻力)

4) 按照業務分庫後,業務邏輯更加清晰,更友善運維管理。

缺點:

1) 對于聯表查詢,帶來了不便,可以通過調用接口的方式來觸發聯表查詢的操作,對整個系統而言,複雜度提高了。

2) 對于有些資料庫,存在單庫性能瓶頸影響整個業務情況。

3) 同時對于事務而言,複雜度提高了。

2、 水準拆分

所謂水準拆分,簡單來說就是将一張表中的資料按照行拆分成多份存儲到不同的資料庫的表中。比如user表有9000條資料,将user表拆成user1、user2、user3三張表存放于不同的資料庫中,user1存儲前3000條資料,中間3000條資料存儲到user2表中,最後3000條資料存放到user3表中。

水準拆分的分片次元(有很多算法來決定采用哪種水準拆分的方案)

1) 按照哈希切片,對某個字段進行求哈希值,然後除以分片的數量,最後取模,取模相同的資料為一個分片,這就是哈希分片。

這種分片方式沒有時效性。對資料分散比較均勻,缺點是如果需要查詢則需要對資料進行聚合處理。

2) 按照時間序列算法切片,有的業務會有明顯的季度波動,可以使用時間算法。這種算法就是資料配置設定不均。

生産環境中,我們部分業務采用的是雜湊演算法和時間序列算法的混合式

說到水準拆分,不得不提的是水準拆分的路由規則

設計資料庫的時候,就要考慮到資料庫中各種表的路由規則,同時還需要考慮資料表将來按照什麼樣的路由規則來進行分庫分表。

比如,某個新使用者注冊,這個使用者是如何配置設定到哪個庫哪個表中?一般在注冊的時候都會系統自動配置設定一個uid。根據這個uid可以按照某種算法來進行配置設定。比如uid%4,(這裡将一張表分成4個表),如果是1 則分到第一張表中,以此類推。

水準拆分的優缺點:

1) 單庫單表的資料量最大就那麼大,有資料量的限制,我們可以根據業務情況來配置設定,剛好達到最大(這個量很難把握),既能提高該表的操作性能又節省了資源

2) 因為對表的結構改變非常少,對于開發而言更改代碼量非常少,隻需要增加路由規則。

3) 對整體系統的穩定性和負載都有大大的提高

1) jion操作非常困難,尤其是跨庫的聯表查詢

2) 有的拆分規則很難抽象出來

3) 分片的事務一緻性比較難解決

4) 還有資料庫的擴容和維護比較困難

針對上述垂直拆分和水準拆分,都有以下缺點:

1) jion操作困難

2) 事務一緻性困難

3) 多個資料源的管理變複雜。

如何解決事務一緻性問題呢?(這個重要性排在第一位)

1) 采用本地事務的一緻性(能用則用)

2) 分布式事務處理

分布式事務處理的解決方案:

1) 兩階段送出方案(最嚴格的方案,很少使用,因為是阻塞協定造成性能問題):分為準備(鎖定資源)和送出(消費資源)兩個階段。這種方式依賴資料總管。

2) 最大努力保證模式(最常用,極端情況需要實時補償,将已送出的資料進行復原)不依賴資料總管

一共有兩種操作:從消息隊列中消費消息和更新資料庫操作

開始消息事務開始資料庫事務接收消息更新資料庫送出資料庫事務送出消息事務

當更新資料庫時,突然中斷,會進行復原,恢複到初始狀态,特殊情況,如果送出資料庫事務成功,但是送出消息事務失敗,就會造成消息再次消費的情況。這個可以通過消息幂等處理(有時候很多消息無法滿足幂等性比如update操作,可以考慮增加一個消息應用狀态表來記錄消息消耗情況和資料庫事務情況),出現上述極端情況,需要實時補償。這種補償機制類似于TCC模式Try-Confirm-Cancel(如果try都成功了,它會重複confirm或重複cancel,直到都成功),TCC要求Confirm/Cancel都必須是幂等操作。

3) 事務補償機制

前面兩種方案,都不是最好的,事務補償機既能保證性能又能盡最大可能保證事務的一緻性。說白了就是突破事務一失敗就復原的思想,指定在一定的時間内不斷送出直到成功為止,如果逾時則復原。

分庫分表過程中帶來的問題

1、 擴容和資料的遷移

資料已經分片,同時資料量已經快達到門檻值,需要對叢集進行擴容,都采用成倍擴容。

以下是5個擴容步驟:

1) 增加新的路由規則,對新的資料庫采用新路由規則進行寫,同時對舊資料庫采用舊路由規則寫。(雙寫,兩套路由規則)

2) 将雙寫前的舊資料按照新規則寫入到新資料庫中(需要做大量的資料清洗工作)。

3) 将按照舊的分片規則的查詢更改為按照新的分片規則查詢。

4) 将雙寫的路由規則代碼下線,隻按照新規則寫資料

5) 删除按照舊分片規則寫入的曆史資料。

2、 分庫分表帶來的聯表查詢問題。

舉例:

賣家和買家,賣家需要檢視該商品賣出情況,買家需要檢視自己的交易情況,可以考慮使用兩個表,一個以買家次元,記錄買家商品交易情況,一個以賣家次元,記錄賣家該商品交易情況。也就是查詢交易和交易的資料是分别存儲的,并從不同的系統提供接口。

=====================================================

主從複制

為什麼要做主從?我們先來探讨以下這幾個場景:

我們知道每台資料庫伺服器有他的最大連接配接數和 IOPS,若有一天他無法再滿足我們的業務需求,那相比于在單台伺服器上去做性能堆疊,是是否橫向去擴充幾台 Slave 去分擔 Master 的壓力更加合理。

如果服務對資料庫的需求是 IO 密集型的,那可能會經常遇到行鎖等待等問題,若要魚與熊掌兼得,讀寫分離是否是更好的選擇。

如果我們的系統需要做很多報表,或者統計和資料分析,這些業務往往相當地耗費資源但又不是很重要,那針對此,我們是否應該開幾台 Slave,讓他們去小黑屋裡慢慢執行,别來影響我處理核心業務的效率。

Master 的主責為寫,并将資料同步至 Slave,Slave 主要提供查詢功能。