以上內容摘取自拉勾《阿里前輩的架構經》
算法
分佈式數據庫和分佈式存儲是分佈式系統中難度最大、挑戰最大,也是最容易出問題的地方。互聯網公司只有解決分佈式數據存儲的問題,才能支撐更屢次億級用戶的涌入。
數據庫
接下來,你將花費十分鐘掌握如下三方面內容:服務器
MySQL複製:包括主從複製和主主複製;架構
數據分片:數據分片的原理、分片的方案、分片數據庫的擴容;併發
數據庫分佈式部署的幾種方案。負載均衡
MySQL的主從複製,就是將MySQL主數據庫中的數據複製到從數據庫中去。運維
主要目的是實現數據庫讀寫分離,寫操做訪問主數據庫,讀操做訪問從數據庫,從而使數據庫具備更強大的訪問負載能力,支撐更多的用戶訪問。分佈式
它的主要的複製原理是:當應用程序客戶端發送一條更新命令到數據庫的時候,數據庫會把這條更新命令同步記錄到Binlog中,而後由另一個線程從Binlog中讀取這條日誌,而後經過遠程通信的方式將它複製到從服務器上面去,從服務器得到這條更新日誌後,將其加入到本身的Relay log中,而後由另一個SQL執行線程從Relay log中讀取這條新的日誌,並把它在本地的數據庫中從新執行一遍。性能
這樣當客戶端應用程序執行一個update命令的時候,這個命令會在主數據庫和從數據庫上同步執行,從而實現了主數據庫向從數據庫的複製,讓從數據庫和主數據庫保持同樣的數據。編碼
MySQL的主從複製是一種數據同步機制,除了能夠將一個主數據庫中的數據同步複製到一個從數據庫上,還能夠將一個主數據庫上的數據同步複製到多個從數據庫上,也就是所謂的MySQL的一主多從複製。
多個從數據庫關聯到主數據庫後,將主數據庫上的Binlog日誌同步地複製到了多個從數據庫上。經過執行日誌,讓每一個從數據庫的數據都和主數據庫上的數據保持了一致。這裏面的數據更新操做表示的是全部數據庫的更新操做,除了不包括SELECT之類的查詢讀操做,其餘的INSERT、DELETE、UPDATE這樣的DML寫操做,以及CREATE TABLE、DROPT ABLE、ALTER TABLE等DDL操做也均可以同步複製到從數據庫上去。
一主多從複製有四大優勢,分別是分攤負載、專機專用、便於冷備和高可用。
a.分攤負載
將只讀操做分佈在多個從數據庫上,從而將負載分攤到多臺服務器上。
b.專機專用
能夠針對不一樣類型的查詢,使用不一樣的從服務器。
c.便於進行冷備
即便數據庫進行了一主多從的複製,在一些極端的狀況下。也可能會致使整個數據中心的數據服務器都丟失。因此一般說來不少公司會對數據作冷備,可是進行冷備的時候有一個困難點在於,數據庫若是正在進行寫操做,冷備的數據就可能不完整,數據文件可能處於損壞狀態。使用一主多從的複製就就能夠實現零停機時間的備份。只須要關閉數據的數據複製進程,文件就處於關閉狀態了,而後進行數據文件拷貝,拷貝完成後再從新打開數據複製就能夠了。
d.高可用
若是一臺服務器宕機了,只要不發請求給這臺服務器就不會出問題。當這臺服務器恢復的時候,從新發請求到這臺服務器。因此,在一主多從的狀況下,某一臺從服務器宕機不可用,對整個系統的影響是很是小的。
可是一主多從只可以實現從服務器上的這些優勢,當主數據庫宕機不可用的時候,數據依然是不可以寫入的,由於數據不可以寫入到從服務器上面去,從服務器是隻讀的。
爲了解決主服務器的可用性問題,咱們可使用MySQL的主主複製方案。所謂的主主複製方案是指兩臺服務器都看成主服務器,任何一臺服務器上收到的寫操做都會複製到另外一臺服務器上。
如上主主複製原理圖,當客戶端程序對主服務器A進行數據更新操做的時候,主服務器A會把更新操做寫入到Binlog日誌中。而後Binlog會將數據日誌同步到主服務器B,寫入到主服務器的Relay log中,而後執Relay log,得到Relay log中的更新日誌,執行SQL操做寫入到數據庫服務器B的本地數據庫中。B服務器上的更新也一樣經過Binlog複製到了服務器A的Relay log中,而後經過Relay log將數據更新到服務器A中。
經過這種方式,服務器A或者B任何一臺服務器收到了數據的寫的操做都會同步更新到另外一臺服務器,實現了數據庫主主複製。主主複製能夠提升系統的寫可用,實現寫操做的高可用。
使用MySQL服務器實現主主複製時,數據庫服務器失效該如何應對?
正常狀況下用戶會寫入到主服務器A中,而後數據從A複製到主服務器B上。當主服務器A失效的時候,寫操做會被髮送到主服務器B中去,數據從B服務器複製到A服務器。
主主失效的維護過程以下:
最開始的時候,全部的主服務器均可以正常使用,當主服務器A失效的時候,進入故障狀態,應用程序檢測到主服務器A失效,檢測到這個失效可能須要幾秒鐘或者幾分鐘的時間,而後應用程序須要進行失效轉移,將寫操做發送到備份主服務器B上面去,將讀操做發送到B服務器對應的從服務器上面去。
一段時間後故障結束,A服務器須要重建失效期間丟失的數據,也就是把本身看成從服務器從B服務器上面去同步數據。同步完成後系統才能恢復正常。這個時候B服務器是用戶的主要訪問服務器,A服務器看成備份服務器。
使用MySQL進行主主複製的時候須要注意的事項以下:
a.不要對兩個數據庫同時進行數據寫操做,由於這種狀況會致使數據衝突。
b.複製只是增長了數據的讀併發處理能力,並無增長寫併發的能力和系統存儲能力。
c.更新數據表的結構會致使巨大的同步延遲。
須要更新表結構的操做,不要寫入到到Binlog中,要關閉更新表結構的Binlog。若是要對錶結構進行更新,應該由運維工程師DBA對全部主從數據庫分別手工進行數據表結構的更新操做。
數據複製只能提升數據讀併發操做能力,並不能提升數據寫操做併發的能力以及數據整個的存儲容量,也就是並不能提升數據庫總存儲記錄數。若是咱們數據庫的寫操做也有大量的併發請求須要知足,或者是咱們的數據表特別大,單一的服務器甚至連一張表都沒法存儲。解決方案就是數據分片。
a.主要目標:將一張數據表切分紅較小的片,不一樣的片存儲到不一樣的服務器上面去,經過分片的方式使用多臺服務器存儲一張數據表,避免一臺服務器記錄存儲處理整張數據錶帶來的存儲及訪問壓力。
b.主要特色:數據庫服務器之間互相獨立,不共享任何信息,即便有部分服務器故障,也不影響整個系統的可用性。第二個特色是經過分片鍵定位分片,也就是說一個分片存儲到哪一個服務器上面去,到哪一個服服務器上面去查找,是經過分片鍵進行路由分區算法計算出來的。在SQL語句裏面,只要包含分片鍵,就能夠訪問特定的服務器,而不須要鏈接全部的服務器,跟其餘的服務器進行通訊。
c.主要原理:將數據以某種方式進行切分,一般就是用剛纔提到的分片鍵的路由算法。經過分片鍵,根據某種路由算法進行計算,使每臺服務器都只存儲一部分數據。
如圖例子,經過應用程序硬編碼的方式實現數據分片。假設咱們的數據庫將數據表根據用戶ID進行分片,分片的邏輯是用戶ID爲奇數的數據存儲在服務器2中,用戶ID爲偶數的數據存儲在服務器1中。那麼,應用程序在編碼的時候,就能夠直接經過用戶ID進行哈希計算,一般是餘數計算。若是餘數爲奇數就鏈接到服務器2上,若是餘數爲偶數,就鏈接到服務器1上,這樣就實現了一張用戶表分片在兩個服務器上。
這種硬編碼主要的缺點在於,數據庫的分片邏輯是應用程序自身實現的,應用程序須要耦合數據庫分片邏輯,不利於應用程序的維護和擴展。一個簡單的解決辦法就是將映射關係存儲在外面。
應用程序在鏈接數據庫進行SQL操做的時候,經過查找外部的數據存儲查詢本身應該鏈接到哪臺服務器上面去,而後根據返回的服務器的編號,鏈接對應的服務器執行相應的操做。在這個例子中,用戶ID=33查找服務器是2,用戶ID=94查找服務器也是2,它們根據查找到的用戶服務器的編號,鏈接對應的服務器,將數據寫入到對應的服務器分片中。
數據庫分片面臨如圖的挑戰:
如今有一些專門的分佈式數據庫中間件來解決上述這些問題,比較知名的有Mycat。Mycat是一個專門的分佈式數據庫中間件,應用程序像鏈接數據庫同樣的鏈接Mycat,而數據分片的操做徹底交給了Mycat去完成。
以下這個例子中,有3個分片數據庫服務器,數據庫服務器dn一、dn2和dn3,它們的分片規則是根據prov字段進行分片。那麼,當咱們執行一個查詢操做」select * from orders where prov=‘wuhan’「的時候,Mycat會根據分片規則將這條SQL操做路由到dn1這個服務器節點上。dn1執行數據查詢操做返回結果後,Mycat再返回給應用程序。經過使用Mycat這樣的分佈式數據庫中間件,應用程序能夠透明的無感知的使用分片數據庫。同時,Mycat還必定程度上支持分片數據庫的聯合join查詢以及數據庫事務。
一開始,數據量還不是太多,兩個數據庫服務器就夠了。可是隨着數據的不斷增加,可能須要增長第三個第四個第五個甚至更多的服務器。在增長服務器的過程當中,分片規則須要改變。分片規則改變後,之前寫入到原來的數據庫中的數據,根據新的分片規則,可能要訪問新的服務器,因此還須要進行數據遷移。
不論是更改分片的路由算法規則,仍是進行數據遷移,都是一些比較麻煩和複雜的事情。所以在實踐中一般的作法是數據分片使用邏輯數據庫,也就是說一開始雖然只須要兩個服務器就能夠完成數據分片存儲,可是依然在邏輯上把它切分紅多個邏輯數據庫。具體的操做辦法,本文不用大篇幅進行闡述了。
這是最簡單的部署方案。應用服務器可能有多個,可是它們完成的功能是單一的功能。多個完成單一功能的服務器,經過負載均衡對外提供服務。它們只連一臺單一數據庫服務器,這是應用系統早期用戶量比較低的時候的一種架構方法。
若是對系統的可用性和對數據庫的訪問性能提出更高要求的時候,就能夠經過數據庫的主從複製進行初步的伸縮。經過主從複製,實現一主多從。應用服務器的寫操做鏈接主數據庫,讀操做從從服務器上進行讀取。
隨着業務更加複雜,爲了提供更高的數據庫處理能力,能夠進行數據的業務分庫。數據的業務分庫是一種邏輯上的,是基於功能的一種分割,將不一樣用途的數據表存儲在不一樣的物理數據庫上面去。
在這個例子中,有產品類目服務和用戶服務,兩個應用服務器集羣,對應的也將數據庫也拆分紅兩個,一個叫作類目數據庫,一個叫作用戶數據庫。每一個數據庫依然使用主從複製。經過業務分庫的方式,在同一個系統中,提供了更多的數據庫存儲,同時也就提供了更強大的數據訪問能力,同時也使系統變得更加簡單,系統的耦合變得更低。
根據不一樣數據的訪問特色,使用不一樣的解決方案進行應對。好比說類目數據庫,也許經過主從複製就可以知足全部的訪問要求。可是若是用戶量特別大,進行主從複製或主主複製,仍是不可以知足數據存儲以及寫操做的訪問壓力,這時候就就能夠對用戶數據庫進行數據分片存儲了。同時每一個分片數據庫也使用主從複製的方式進行部署。
以上爲分佈式數據庫的部署方案,若是你的應用不是非要使用關係數據庫的話,你還能夠選擇NoSQL數據庫,NoSQL數據庫會提供更強大的數據存儲能力和併發讀寫能力。可是NoSQL數據庫由於CAP原理的約束可能會遇到數據不一致的問題。解決數據不一致的問題,能夠經過時間戳合併、客戶端判斷以及投票這樣的幾種機制解決,實現最終一致性。