如何在微服務分散式架構中刪除資料? - bennorthrop

banq發表於2021-11-12

儘管微服務具有各種好處,但似乎也有許多新的複雜性和併發症。我最近經常遇到的一種情況(並沒有找到很多很好的資源)是刪除資料。考慮一個簡單的例子:
有三種服務:
  1. Product 服務,管理與所提供的產品,
  2. Order 追蹤產品購買服務,
  3. Catalog服務 ,其控制被髮布到不同的媒介(比如,印刷,數字等)。

現在如果需要刪除給定的產品會發生什麼?說“Widget X”不再是要出售的產品。或者“Widget X”被某個昏昏欲睡的使用者意外新增,只需要刪除。處理這個問題的最佳方法是什麼?
 

解決方案 1:只需刪除它
在單體世界中,我們也許可以刪除表中的“Widget X”行PRODUCT,就是這樣。如果在另一個表中的某行有一些對該產品行的外來鍵引用(例如,因為該產品是在先前的訂單中銷售的,或者該產品包含在現有目錄中),則可能會有一個刪除級聯,它會自動也刪除這些行(通常是危險的事情),或者至少會有一些外來鍵約束在嘗試刪除該產品時會引發錯誤。不管怎樣,因為單體應用中的所有資料都駐留在一個資料庫中,所以可以集中管理參照完整性(即不留下孤立資料)的管理。
在微服務架構中,這顯然行不通。當然,“Widget X”行可以直接從PRODUCTProduct 服務的表中刪除,但其他服務的資料將被有效地孤立。
例如,訂單服務可能在其ORDER_DETAILS舊訂單的表中引用了“Widget X” ,並且可能想要獲取有關此產品的資訊以顯示完整的訂單詳細資訊。如果從 Product 服務中刪除了“Widget X”,則 Order 服務將在其 GET 請求中收到 404 /products/widget-x。換句話說,即使每個微服務都旨在解耦和自治,但這並不意味著它從資料的角度來看是完全獨立的。如果我們想保持一定程度的參照完整性,協調和溝通是必要的。

(banq注:這是最初設計問題,訂單在其ORDER_DETAILS舊訂單不能引用產品“Widget X”,而是複製一份,因為當時下訂單的上下文時的產品快照必須被記錄到訂單上下文中,這樣,即使該產品資訊在下完訂單以後更改,也不會引起下這個訂單的使用者的異議,技術問題如果不結合業務上下文就很難解決)
 
解決方案2:同步協調

下一個可能性是讓產品服務在刪除給定產品時首先檢查任何依賴服務以檢視它是否能夠刪除該產品,然後在必要時執行該“級聯”。例如,產品服務可以首先檢查訂單服務和目錄服務是否可以安全地刪除“Widget X”(即沒有對它的引用),並且只有在確認它是安全的之後才會從PRODUCT表中刪除. 或者,產品服務可以繼續進行自己的刪除,然後同步呼叫訂單和目錄服務,並說“我剛剛刪除了我的小部件 X,因此從您的資料庫中刪除對它的任何引用”。
即使從業務規則的角度刪除這種方式是可以的,這也是不明智的。建立從產品服務到目錄和訂單服務的同步依賴有很大的缺點——複雜性增加、每個請求的額外延遲以及彈性/可用性受損(即,如果訂單服務關閉,產品服務無法執行刪除)。微服務應該儘可能地獨立。將它們與執行時依賴項繫結在一起時應該慎重考慮,以免我們只是建立一個分散式單體
 

解決方案 3:不要刪除
在這一點上,我們可能會觀察到,無論如何刪除產品並沒有真正意義!正如Udi Dahan 指出的那樣,在現實世界中確實沒有“刪除”事物的概念。相反,資料只是改變狀態。員工不會被刪除、被解僱或辭職。訂單不會被刪除,它們會被取消。並且產品不會被刪除,它們不再銷售。換句話說,在幾乎所有情況下,在給定行上支援某些狀態欄位比完全刪除它要好。
在這裡的例子中,這將解決一些問題。產品服務不支援真正的“刪除”,而是隻公開產品的“退役”(或其他)端點,但將資料保留在其資料庫中。這樣,目錄和訂單服務將不再有孤立的資料——它們仍然可以回撥產品服務以獲取有關“Widget X”的資訊,即使它當前不再銷售。
但這並不能解決所有問題。如果目錄服務需要知道某個產品正在退役,以便不會針對給定目錄向客戶顯示該產品,該怎麼辦?當然,它可以查詢產品服務以獲取此資訊,但這可能會引入從目錄服務到產品服務的同步依賴關係,並引入上面討論的那些相同的複雜性、延遲和彈性問題。
 

解決方案 4:非同步更新
為了支援目錄服務的自主性,它可以維護自己的產品資料本地快取,而不是依賴於產品服務,使其與產品服務的更改保持同步。當產品退役時,產品服務可以發出一個ProductDecommissioned目錄服務會監聽的事件,然後它可以更新自己的本地產品商店。在“Widget X”的情況下,一旦退役,Catalog 服務就會知道不會在其目錄中顯示它。這有效。除了...
這個產品資料的本地快取 究竟什麼?是記憶體嗎?還是目錄服務資料庫中的表?我們如何確保它與事實來源,即產品服務同步?這裡有幾個選項,每個選項都有自己的優點和缺點。
如果本地快取在記憶體中,那麼這很容易實現,並且可能很容易同步(例如,可能有一些協調過程來檢查產品服務並確保其自己的資料是最新的/最新的)。缺點是它放棄了資料庫級別的參照完整性。如果CATALOG在其資料庫的表中存在某個 product_id ,則無需(在資料庫級別)強制該 product_id 實際上有效(即對映到產品服務資料庫中的 product_id)。此外,如果要快取的資料很大,則記憶體快取可能無法成立。
或者,Catalog Service 中的本地快取也可以是一個資料庫表,這將從上面解決資料庫級引用完整性和儲存問題。一個問題是複雜性。編寫 SQL 以播種和更新本地PRODUCT表並非易事 - 編寫的程式碼越多意味著需要維護的程式碼越多。此外,同步還增加了複雜性——如果給定微服務有多個例項,但只有一個資料庫例項,那麼哪個微服務或哪個程式負責更新和同步快取?
可能更好的解決方案是實現一個事件日誌(使用 Kafka 等),並讓每個微服務利用它來與資料更改保持同步。這個選項有很多需要考慮的地方(超出了這篇博文的範圍),一個很好的起點是事件驅動微服務。雖然這種模式優雅地解決了上述所有問題,但在我看來,這是一種需要構建和支援的升級架構,因此只有在沒有足夠專業知識和資源的情況下才能進入。
 
作者:Ben Northrop
我是一個“老”程式設計師,已經寫了將近 20 年的部落格。2017 年,我創辦了Highline Solutions,這是一家幫助軟體架構和全棧開發的諮詢公司。我有卡內基梅隆大學的兩個學位,一個是實用的(資訊和決策系統),另一個不是(哲學 -論文在這裡)。
 
 

相關文章