1. 程式人生 > >阿里雲分散式快取OCS與DB之間的資料一致性

阿里雲分散式快取OCS與DB之間的資料一致性

來源:http://www.csdn.net/article/1970-01-01/2825234

摘要:OCS是阿里巴巴集團的分散式快取產品,支撐著淘寶、阿里巴巴、支付寶的日常運作,尤其在雙11等大型活動上,承載了絕大多數的資料請求。與OCS相比,著名的Memcached具備了分散式叢集管理的功能。

OCS概要介紹

據AlertSite網路分析公司表示,Facebook的響應時間在2010年平均為1秒鐘,到2011年中期已提高到了0.73秒。對比來看,響應時間佔第二位的LinkedIn,網路下載內容時要花費將近2倍的時間。Twitter的響應時間則整整遲了2秒鐘。響應時間優化的首要手段就是採用快取技術,減少系統間互動請求和磁碟IO。

OCS是阿里巴巴集團的分散式快取產品,支撐著淘寶、阿里巴巴、支付寶的日常運作,尤其在雙11等大型活動上,承載了絕大多數的資料請求。與OCS相比,著名的Memcached具備了分散式叢集管理的功能。2014年OCS經歷了從分散式到雲服務的進化,作為阿里雲服務的快取產品正式商業化。

OCS技術講解

OCS的核心儲存是淘寶的開源產品TAIR(發音:太愛兒)

TAIR原理

角色上分為DataServer,ConfigServer:

  1. ConfigServer負責資料的路由表管理,決定著哪些資料應該去哪裡訪問。同時也管理著DataServer節點的存活狀態,自動踢出宕機或者異常節點。
  2. DataServer是資料儲存節點,負責資料的增刪改查。通過Plugin機制支援多種儲存引擎。常用的有基於記憶體的,所有的資料儲存在記憶體之中,查詢速度快,但不持久化,網路正常的情況下,客戶端在0.2ms內得到請求響應。另外一種常用儲存引擎基於SSD介質再依靠記憶體加速,特點是容量大,成本低,效能上接近記憶體引擎,客戶端請求響應時間大概是1ms。

叢集初始化時ConfigServer會根據DataServer的數量分配BucketID到DataServer上,這層對映關係就是資料路由索引,BucketID屬於[0-1023]的範圍內。客戶端第一次啟動時會從ConfigServer上拉取對映關係,之後的讀寫請求,根據全域性約定的Hash演算法(例如MurmurHash(key)24)計算出BucketID,根據對映關係描述向指定的DataServer上傳送請求。

ConfigServer上的路由資訊會根據DataServer存活狀況動態修改更新;新結果再告知給DataServer;當DataServer處理客戶端響應時,將變更通知給客戶端。


圖1 路由路徑

從TAIR到OCS

雲服務化的過程中,首要問題是滿足使用者的相容性需求,使用者訪問介面上支援廣泛流行的Memcached介面,原生於Memcached的應用,可以無縫遷移到OCS上來。

其次是穩定性,叢集升級時,由於程序重啟會造成應用請求OCS瞬間報錯,OCS實現了一套熱升級方案,在保持TCP連結不中斷的情況下重啟程序。

雲服務還有一個重要特性就是多租戶,多租戶的情況下,為了防止某一兩個使用者的高併發訪問造成叢集負載上升,從而影響了其他租戶的穩定性。OCS內部對不同的租戶進行了資源隔離,針對請求量、頻寬、記憶體使用量做了嚴格的限制。不同規格的使用者可以購買不同規格的OCS例項,之間不會互相干擾。

與OCS相比,自建的Memcached解決了單機容量上線問題,實現擴容自動化且不需要修改客戶端配置,同時輸出了效能監控指標,網頁版Console命令等。

快取失效一致性問題

一般快取的使用方式是:先讀取快取,若不存在則從DB中讀取,並將結果寫入到快取中;下次資料讀取時便可以直接從快取中獲取資料。

資料的修改是直接失效快取資料,再修改DB內容,避免DB修改成功,但由於網路或者其他問題導致快取資料沒有清理,造成了髒資料。

但這樣仍然無法避免髒資料的產生,一種併發的場景下:假設業務對資料Key:Hello Value:World有大量的讀取和修改請求。執行緒A向OCS讀取Key:Hello,得到Not Found結果,開始向DB請求資料,得到資料Key:Hello Value:World;接下來準備向OCS寫入此條資料,但在寫入OCS前(網路,CPU都等可能導致A執行緒處理速度降低)另一B執行緒請求修改資料Key:Hello Value:OCS,首先執行失效快取動作(因為B執行緒並不知道是否有此條資料,因此直接執行失效操作),OCS成功處理了失效請求。轉回到A執行緒繼續執行寫入OCS,將Key:Hello Value:World寫入到快取中,A執行緒任務結束;B執行緒也成功修改了DB資料內容為Key:Hello Value:OCS。


圖2 併發時序

此時OCS中的資料為Key:Hello Value:World;DB中的資料為Key:Hello Value:OCS,出現快取髒資料!

為了解決這個問題,OCS擴充了Memcached協議(公有云即將支援),增加了deleteAndIncVersion介面。此介面並不會真的刪除資料,而是給資料打了標籤,表明已失效狀態,並且增加資料版本號;如果資料不存在則寫入NULL,同時也生成隨機資料版本號。OCS寫入支援原子對比版本號:假設傳入的版本號與OCS儲存的資料版本號一致或者原資料不存在,則准許寫入,否則拒絕修改。

回到剛才的場景上:執行緒A向OCS讀取Key:Hello,得到Not Found結果,開始向DB請求資料,得到資料Key:Hello Value:World;接下來準備向OCS寫入此條資料,版本號資訊預設為1;在A寫入OCS前另一個B執行緒發起了動作修改資料Key:Hello Value:OCS,首先執行刪除快取動作,OCS順利處理了deleteAndIncVersion請求,生成了隨機版本號12345(約定大於1000)。轉回到A執行緒繼續執行寫入OCS,請求將Key:Hello Value:World寫入,此時快取系統發現傳入的版本號資訊不匹配(1 != 12345),寫入失敗,A執行緒任務結束;B執行緒也成功修改了DB資料內容為Key:Hello Value:OCS。

此時OCS中的資料為Key:Hello Value:NULL Version:12345;DB中的資料為Key:Hello Value:OCS,後續讀任務時會再次嘗試將DB中的資料寫入到OCS中。

類似的併發場景還有很多,讀者可以自行推演,同時也可以思考下為何約定隨機生成的版本要大於1000?

快取資料的同步的一致性問題

隨著網站規模增長和可靠性的提升,會面臨多IDC的部署,每個IDC都有一套獨立的DB和快取系統,這時快取一致性又成了突出的問題。

首先快取系統為了保證高效率,會杜絕磁碟IO,哪怕是寫BINLOG;當然快取系統為了效能可以只同步刪除,不同步寫入,那麼快取的同步一般會優先於DB同步到達(畢竟快取系統的效率要高得多),那麼就會出現快取中無資料,DB中是舊資料的場景。此時,有業務請求資料,讀取快取Not Found,從DB讀取並載入到快取中的仍然是舊資料,DB資料同步到達時也只更新了DB,快取髒資料無法被清除。


圖3 併發時序

從上面的情況可以看出,不一致的根本原因是異構系統之間無法協同同步,不能保證DB資料先同步,快取資料後同步。所以就要考慮快取系統如何等待DB同步,或者能否做到兩者共用一套同步機制?快取同步也依賴DB BINLOG是一個可行的方案。

IDC1中的DB,通過BINLOG同步給IDC2中的DB,此事IDC2-DB資料修改也會產生自身的BINLOG,快取的資料同步就可以通過IDC2-DB BINLOG進行。快取同步模組分析BINLOG後,失效相應的快取Key,同步從並行改為序列,保證了先後順序。

這樣,IDC間的資料同步架構更加簡單清晰,系統服用率高,做好BINLOG同步和抓取即可。


圖4 異地同步

總結

不同系統之間的資料同步一直是一個世界性的問題,目前仍然沒有方法解除CAP魔咒,只能根據實際的情況在三者之間尋找理想的平衡點。本文介紹的解決方案,其一是利用了快取系統的原子操作,其二是利用了外部系統同步機制保證先後,都是在犧牲最小的效能代價時獲取最大的一致性保證,但仍然無法覆蓋全部場景下的一致性問題。


相關推薦

no