CAP原理指的是,在分布式系統(tǒng)中這三個要素最多只能同時實現(xiàn)兩點,不可能三者兼顧。因此在進行分布式架構(gòu)設(shè)計時,必須做出取舍。而對于分布式數(shù)據(jù)系統(tǒng),分區(qū)容忍性是基本要求,否則就失去了價值。因此設(shè)計分布式數(shù)據(jù)系統(tǒng),就是在一致性和可用性之間取一個平衡。對于大多數(shù)Web應(yīng)用,其實并不需要強一致性,因此犧牲一致性而換取高可用性,是目前多數(shù)分布式數(shù)據(jù)庫產(chǎn)品的方向。
一致性(Consistency):數(shù)據(jù)在多個副本之間是否能夠保持一致的特性。(當(dāng)一個系統(tǒng)在一致狀態(tài)下更新后,應(yīng)保持系統(tǒng)中所有數(shù)據(jù)仍處于一致的狀態(tài))。
可用性(Availability):系統(tǒng)提供的服務(wù)必須一直處于可用狀態(tài),對每一個操作的請求必須在有限時間內(nèi)返回結(jié)果。
分區(qū)容錯性(Tolerance of network Partition):分布式系統(tǒng)在遇到網(wǎng)絡(luò)分區(qū)故障時,仍然需要保證對外提供一致性和可用性的服務(wù),除非整個網(wǎng)絡(luò)都發(fā)生故障。
例如,服務(wù)器中原本存儲的value=0,當(dāng)客戶端A修改value=1時,為了保證數(shù)據(jù)的一致性,要寫到3個服務(wù)器中,當(dāng)服務(wù)器C故障時,數(shù)據(jù)無法寫入服務(wù)器C,則導(dǎo)致了此時服務(wù)器A、B和C的value是不一致的。這時候要保證分區(qū)容錯性,即當(dāng)服務(wù)器C故障時,仍然能保持良好的一致性和可用性服務(wù),則Consistency和Availability不能同時滿足。為什么呢? 如果滿足了一致性,則客戶端A的寫操作value=1不能成功,這時服務(wù)器中所有value=0。如果滿足可用性,即所有客戶端都可以提交操作并得到返回的結(jié)果,則此時允許客戶端A寫入服務(wù)器A和B,客戶端C將得到未修改之前的value=0結(jié)果。
1)Basically Available(基本可用)分布式系統(tǒng)在出現(xiàn)不可預(yù)知故障的時候,允許損失部分可用性
2)Soft state(軟狀態(tài))軟狀態(tài)也稱為弱狀態(tài),和硬狀態(tài)相對,是指允許系統(tǒng)中的數(shù)據(jù)存在中間狀態(tài),并認(rèn)為該中間狀態(tài)的存在不會影響系統(tǒng)的整體可用性,即允許系統(tǒng)在不同節(jié)點的數(shù)據(jù)副本之間進行數(shù)據(jù)同步的過程存在延時。
2)Eventually consistent(最終一致性)最終一致性強調(diào)的是系統(tǒng)中所有的數(shù)據(jù)副本,在經(jīng)過一段時間的同步后,最終能夠達(dá)到一個一致的狀態(tài)。因此,最終一致性的本質(zhì)是需要系統(tǒng)保證最終數(shù)據(jù)能夠達(dá)到一致,而不需要實時保證系統(tǒng)數(shù)據(jù)的強一致性。
ACID 是傳統(tǒng)數(shù)據(jù)庫常用的設(shè)計理念,追求強一致性模型。BASE 支持的是大型分布式系統(tǒng),提出通過犧牲強一致性獲得高可用性。 ACID 和 BASE 代表了兩種截然相反的設(shè)計哲學(xué),在分布式系統(tǒng)設(shè)計的場景中,系統(tǒng)組件對一致性要求是不同的,因此 ACID 和 BASE 又會結(jié)合使用。
接口的冪等性實際上就是接口可重復(fù)調(diào)用,在調(diào)用方多次調(diào)用的情況下,接口最終得到的結(jié)果是一致的。有些接口可以天然的實現(xiàn)冪等性,比如查詢接口,對于查詢來說,你查詢一次和兩次,對于系統(tǒng)來說,沒有任何影響,查出的結(jié)果也是一樣。除了查詢功能具有天然的冪等性之外,增加、更新、刪除都要保證冪等性。
1)全局唯一ID:全局唯一ID就是根據(jù)業(yè)務(wù)的操作和內(nèi)容生成一個全局ID,在執(zhí)行操作前先根據(jù)這個全局唯一ID是否存在,來判斷這個操作是否已經(jīng)執(zhí)行。如果不存在則把全局ID,存儲到存儲系統(tǒng)中,比如數(shù)據(jù)庫、redis等。如果存在則表示該方法已經(jīng)執(zhí)行。 從工程的角度來說,使用全局ID做冪等可以作為一個業(yè)務(wù)的基礎(chǔ)的微服務(wù)存在,在很多的微服務(wù)中都會用到這樣的服務(wù),在每個微服務(wù)中都完成這樣的功能,會存在工作量重復(fù)。另外打造一個高可靠的冪等服務(wù)還需要考慮很多問題,比如一臺機器雖然把全局ID先寫入了存儲,但是在寫入之后掛了,這就需要引入全局ID的超時機制。 使用全局唯一ID是一個通用方案,可以支持插入、更新、刪除業(yè)務(wù)操作。但是這個方案看起來很美但是實現(xiàn)起來比較麻煩,下面的方案適用于特定的場景,但是實現(xiàn)起來比較簡單。
2)去重表:這種方法適用于在業(yè)務(wù)中有唯一標(biāo)的插入場景中,比如在以上的支付場景中,如果一個訂單只會支付一次,所以訂單ID可以作為唯一標(biāo)識。這時,我們就可以建一張去重表,并且把唯一標(biāo)識作為唯一索引,在我們實現(xiàn)時,把創(chuàng)建支付單據(jù)和寫入去去重表,放在一個事務(wù)中,如果重復(fù)創(chuàng)建,數(shù)據(jù)庫會拋出唯一約束異常,操作就會回滾。
3)插入或更新:這種方法插入并且有唯一索引的情況,比如我們要關(guān)聯(lián)商品品類,其中商品的ID和品類的ID可以構(gòu)成唯一索引,并且在數(shù)據(jù)表中也增加了唯一索引。這時就可以使用InsertOrUpdate操作。在mysql數(shù)據(jù)庫中如下:
insert into goods_category (goods_id,category_id,create_time,update_time)?
? ? ? ?values(#{goodsId},#{categoryId},now(),now())?
? ? ? ?on DUPLICATE KEY UPDATE
? ? ? ?update_time=now()
4)多版本控制:這種方法適合在更新的場景中,比如我們要更新商品的名字,這時我們就可以在更新的接口中增加一個版本號,來做冪等
boolean updateGoodsName(int id,String newName,int version);
在實現(xiàn)時可以如下
update goods set name=#{newName},version=#{version} where id=#{id} and version<${version}
5)狀態(tài)機控制:這種方法適合在有狀態(tài)機流轉(zhuǎn)的情況下,比如就會訂單的創(chuàng)建和付款,訂單的付款肯定是在之前,這時我們可以通過在設(shè)計狀態(tài)字段時,使用int類型,并且通過值類型的大小來做冪等。比如訂單的創(chuàng)建為0,付款成功為100,付款失敗為99 。
在做狀態(tài)機更新時,我們就這可以這樣控制
update `order` set status=#{status} where id=#{id} and status<#{status}
分布式事務(wù)是指事務(wù)的參與者、支持事務(wù)的服務(wù)器、資源服務(wù)器以及事務(wù)管理器分別位于不同的分布式系統(tǒng)的不同節(jié)點之上。一個大的操作由 N 多的小的操作共同完成。而這些小的操作又分布在不同的服務(wù)上。針對于這些操作,要么全部成功執(zhí)行,要么全部不執(zhí)行。
1.UUID:時間戳+時鐘序列(計數(shù)器)+唯一的IEEE機器識別碼(比如網(wǎng)卡的MAC地址) 。
缺點:對數(shù)據(jù)庫不友好,因為隨機不連續(xù)。
2.數(shù)據(jù)庫自增:對于數(shù)據(jù)庫集群模型,要設(shè)置不同的數(shù)據(jù)庫起始值不同,但是步長(自增幾)相同。
3.Leaf-segment:(美團大眾點評的)采用每次獲取一個ID區(qū)間的方式。比如一次和數(shù)據(jù)庫的交互,就請求到100個id,數(shù)據(jù)來了直接用。避免每次添加數(shù)據(jù)都請求一個id,增加了數(shù)據(jù)庫的壓力。 也是對數(shù)據(jù)庫自增策略的一個優(yōu)化。
4.雪花算法:其核心思想是:41位時間戳+10位機器id+12位序列號+符號位(0)。結(jié)果是一個長度為64bit的long型的ID。
優(yōu)點:12位序列號是說每個節(jié)點在每毫秒可以產(chǎn)生4096 個ID,并且是遞增的。 這樣適合于Mysql的聚集索引,索引的連續(xù)性也好。
缺點:依賴于時間戳,時間戳是根據(jù)機器的時間得到的。比如linux中,如果人為的進行時鐘回?fù)埽涂赡茉斐蒳d重復(fù)。
● 使用jwt
● 使用cookie (有安全風(fēng)險)
● 服務(wù)器之間進行session同步:保證每個服務(wù)器都有session信息,消耗比較大。
● ip綁定策略:比如使用Ngnix進行源地址哈希法的負(fù)載均衡,讓每一個ip固定訪問一個服務(wù)器, 但是這種就失去分布式的作用。
● 使用redis存儲:是業(yè)界最廣泛的。 可實現(xiàn)不同服務(wù),不同平臺(網(wǎng)頁/app),甚至不同語言的session共享。