分布式環境下,怎么保證線程安全


避免並發

在分布式環境中,如果存在並發問題,那么很難通過技術去解決,或者解決的代價很大,所以我們首先要想想是不是可以通過某些策略和業務設計來避免並發。比如通過合理的時間調度,避開共享資源的存取沖突。另外,在並行任務設計上可以通過適當的策略,保證任務與任務之間不存在共享資源,比如在以前博文中提到的例子,我們需要用多線程或分布式集群來計算一堆客戶的相關統計值,由於客戶的統計值是共享數據,因此會有並發潛在可能。但從業務上我們可以分析出客戶與客戶之間 數據是不共享的,因此可以設計一個規則來保證一個客戶的計算工作和數據訪問只會被一個線程或一台工作機完成,而不是把一個客戶的計算工作分配給多個線程去 完成。這種規則很容易設計,例如可以采用hash算法。

時間戳

分布式環境中並發是沒法保證時序的,無論是通過遠程接口的同步調用或異步消息,因此很容易造成某些對時序性有要求的業務在高並發時產生錯誤。比如系統A需要把某個值的變更同步到系統B,由於通知的時序問題會導致一個過期的值覆蓋了有效值。對於這個問題,常用的辦法就是采用時間戳的方式,每次系統A發送變更給系統B的時候需要帶上一個能標示時序的時間戳,系統B接到通知后會拿時間戳與存在的時間戳比較,只有當通知的時間戳大於存在的時間戳,才做更新。這種方式比較簡單,但關鍵在於調用方一般要保證時間戳的時序有效性。

串行化

有的時候可以通過串行化可能產生並發問題操作,犧牲性能和擴展性,來滿足對數據一致性的要求。比如分布式消息系統就沒法保證消息的有序性,但可以通過變分布式消息系統為單一系統就可以保證消息的有序性了。另外,當接收方沒法處理調用有序性,可以通過一個隊列先把調用信息緩存起來,然后再串行地處理這些調用。

數據庫

分布式環境中的共享資源不能通過Java里同步方法或加鎖來保證線程安全,但數據庫是分布式各服務器的共享點,可以通過數據庫的高可靠一致性機制來滿足需求。比如,可以通過唯一性索引來解決並發過程中重復數據的生產或重復任務的執行;另外有些更新計算操作也盡量通過sql來完成,因為在程序段計算好后再去更新就有可能發生臟復寫問題,但通過一條sql來完成計算和更新就可以通過數據庫的鎖機制來保證update操作的一致性。

行鎖

有的事務比較復雜,無法通過一條sql解決問題,並且有存在並發問題,這時就需要通過行鎖來解決,一般行鎖可以通過以下方式來實現:
對於Oracle數據庫,可以采用select ... for update方式。這種方式會有潛在的危險,就是如果沒有commit就會造成這行數據被鎖住,其他有涉及到這行數據的任務都會被掛起,應該謹慎使用
在表里添加一個標示鎖的字段,每次操作前,先通過update這個鎖字段來完成類似競爭鎖的操作,操作完成后在update鎖字段復位,標示已歸還鎖。這種方式比較安全,不好的地方在於這些update鎖字段的操作就是額外的性能消耗

統一觸發途徑

當一個數據可能會被多個觸發點或多個業務涉及到,就有並發問題產生的隱患,因此可以通過前期架構和業務設計,盡量統一觸發途徑,觸發途徑少了一是減少並發的可能,也有利於對於並發問題的分析和判斷。

如何保證高並發時線程安全?

對於商城一類系統中,單點登錄、購物車、訂單這些都有並發。

AtomicInteger、synchronized、Lock、ThreadLocal等類來保證在代碼層面上的線程安全如果是功能上需要自主多線程處理,那么也會使用線程池ThreadPool來提高並發效率

對高並發的處理會使用Redis的分布式鎖(setnx),將對於服務器的承載力達到一定數量后,之后的請求全部加入隊列處理。

負載均衡:在代碼層級上對不同的業務進行讀寫分離;而數據庫上進行集群和主從復制。在應用服務器上對應的對每個服務器都運用lvs+keepalive模式進行服務器集群;如果硬件資源足夠的話那么可以對集群節點更加多和更加分散提高並發能力和系統穩定性。

 

Redis是一個開源,先進的key-value存儲,並用於構建高性能,可擴展的Web應用程序的完美解決方案,是線程安全的。
Redis三個主要特點:
  Redis數據庫完全在內存中,使用磁盤僅用於持久性。
  相比許多鍵值數據存儲,Redis擁有一套較為豐富的數據類型(list,string,sort,set,hash)。
  Redis可以將數據復制到任意數量的從服務器。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM