Reddit是如何解決三個臭皮匠的快取首次更新問題?
本月,Reddit 的一名資深軟體工程師分享了一個真實世界的例子,說明微服務如何幫助提高 Reddit 的彈性——一個深思熟慮的案例研究,來自他自己處理搜尋請求突然激增的經驗。這是一個很好的例子,可以分享您的知識,透過解決您遇到的有趣問題和解決方案來幫助更大社群中的其他人。但這也是一個例子,說明長期存在的問題如何在微服務領域找到新的解決方案。
最重要的是,他用三個臭皮匠的有趣比喻完美地說明了這一切。
三個臭皮匠是一個鬧劇喜劇三重奏,他們經常嘗試在簡單的日常任務上進行協作,但最終總是妨礙對方並傷害對方。一個場景:他們一起試圖穿過一個門口。但是因為他們試圖同時並肩穿過,他們撞到了對方;最終,沒有人能夠透過。Reddit 也遇到過透過分散式微服務架構推送請求的類似模式。
問題
在 Reddit,我們在從中斷中恢復時遇到了一個有趣的規模問題。我們在微服務上游的 API 閘道器級別有一個響應快取;和快取的響應有一個 TTL。現在假設該站點停機的時間比 TTL 長,因此快取已被重新整理清零。
當站點恢復時,我們會收到大量請求 其中許多是重複請求同一個資源,都是在短時間內發生的。在正常操作期間,大多數這些重複請求將從快取中提供服務。但是當從這樣的中斷中恢復時,因為沒有快取任何東西,所有重複的請求都會同時命中我們的微服務、底層資料庫和搜尋引擎。這會導致流量氾濫,以至於在請求超時內沒有任何請求成功,因此沒也有響應被快取;並且該站點會立即再次進行修復。
我們將這種情況稱為“三個臭皮匠問題”,儘管它更常被稱為“雷霆萬鈞Thundering Herd”、 “狗堆效應”或“快取踩踏事件”。
解決方案
我們對三個臭皮匠問題的解決方案是在微服務級別對請求進行重複資料刪除和快取響應。請求重複資料刪除(也稱為請求摺疊或請求合併)意味著重新排序重複的請求,以便它們一次執行一個。此解決方案有效的原因是,從概念上講,它對請求重新排序,以便從不重複併發執行,甚至不在不同的後端例項上(分散式鎖強制執行此操作)。然後第一個請求被處理並且它的響應被快取。然後,該請求的所有後續重複項將被序列執行並從快取中得到滿足。這使我們能夠更有效地利用我們的快取,併為我們節省了底層資料庫和搜尋引擎上重複請求的負載。
將重複資料刪除/快取移至其堆疊中的不同點——特別是其微服務級別——以及“一個可以處理許多併發請求的網路堆疊”。然後工程師只需實現程式碼,確保不會無意中同時處理兩個重複項(使用分散式鎖)。剩下的就是檢查已經檢索到的響應是否存在,如果沒有找到快取的響應,則只建立新的響應。
將重複資料刪除視為迫使 三個臭皮匠在通往廚房的門口排成一條有秩序的隊伍。然後第一個臭皮匠進了廚房,拿著一碗扁豆湯離開,那碗湯就被收起來了。然後另外兩個臭皮匠得到了快取的碗湯。
Reddit 的 API 閘道器將來自不同平臺的所有傳入請求整理成標準形式,以便於處理(同時丟棄任何不相關的多餘變數)。但是,當它們達到微服務級別時,重複資料刪除最終會使用一種稱為雜湊表的簡單程式設計結構來處理——其中一個值與一個可以在以後檢索它的唯一識別符號(一個鍵)配對。這建立了一種發現重複值的簡單方法,因為它們已經被分配了一個識別符號。
Reddit 提供了一個示例中的程式碼片段,其中使用 Pottery 的Redis Redlock實現來實現分散式鎖。GitHub 儲存庫中,
重要的是,分散式鎖具有自動釋放超時以保持活性。想象一個執行緒獲取鎖然後在臨界區死亡的情況。如果沒有自動釋放超時,鎖將永遠不會被釋放,從而導致死鎖。
解決三個臭皮匠問題的最後一個構建塊是快取響應結果。同樣,我們可以實現一個裝飾器來包裝端點函式來快取響應,它使用請求雜湊作為快取鍵。它嘗試查詢該快取鍵,並在命中時返回快取的響應。如果未命中,它會呼叫底層端點函式,快取後續重複請求的響應,然後返回計算出的響應。
問答
- 為什麼不在 CDN 或邊緣而是在微服務級別對請求和快取響應進行重複資料刪除?
請求來自不同的平臺和不同的形式。所有這些請求都由我們的 API 閘道器整理成標準格式。因此,透過在我們知道它們不相關的層扔掉不相關的變數,我們的更多請求看起來是一樣的。這提高了我們識別重複請求的能力並最大化我們的響應快取命中率。
此外,作為微服務所有者,我們的團隊可以更好地控制微服務中請求和響應發生的事情,而配置邊緣發生的事情的能力則更弱。這不僅僅是一種所有權權衡;它還允許我們在我們的微服務中進行許可權檢查、個性化等操作。
最後,透過在微服務級別進行重複資料刪除和快取,我們有更多機會為我們的原始請求流進一步檢測、記錄和觸發事件。
- 在請求重複資料刪除期間,如果您的底層基礎架構出現問題並且分散式鎖自動超時,會發生什麼情況?
我們使用分散式鎖只是為了防止重複請求造成負載。我們不會使用鎖來強制執行資料一致性、防止競爭條件或出於任何其他原因。因此在最壞的情況下,如果鎖超時,一些重複的請求可以立即執行臨界區。即使在這種情況下,鎖也有助於透過防止所有重複同時執行來減輕我們苦苦掙扎的基礎設施的負載。
- 為什麼不在您的微服務中更深入地刪除重複的函式呼叫和快取函式返回值?
這是一種有效的方法,您可能會考慮在微服務中執行此操作。您可以使用函式的引數來構造鎖定/快取鍵,並且可以快取昂貴函式的返回值。由於引數排列較少,在微服務中進行更深層次的重複資料刪除和快取可以提供更高的快取命中率。
另一方面,在您的微服務中更高層進行重複資料刪除和快取可以節省更多工作。您可能有一個昂貴的 I/O 繫結函式來查詢您的資料儲存,以及另一個昂貴的 CPU 繫結函式來呈現響應。更高階別的快取,例如圍繞端點函式,將節省對兩個昂貴函式的呼叫。
在此示例中,為簡單起見,我們對端點函式進行了重複資料刪除和快取。
相關文章
- 如何解決快取失效問題快取
- 快取三大問題及解決方案快取
- 快取世界中的三大問題及解決方案快取
- 快取過程存在的三大問題及解決方案快取
- 快取問題(四) 快取穿透、快取雪崩、快取併發 解決案例快取穿透
- 如何解決資料庫與快取的一致性問題資料庫快取
- 程式碼解決快取穿透和快取雪崩問題快取穿透
- 快取的問題快取
- Redis 的高效能快取機制的三類問題:快取擊穿、快取雪崩 和 快取穿透Redis快取穿透
- 快取穿透問題與解決方法快取穿透
- 如何解決相親交友原始碼中Redis快取擊穿、雪崩問題?原始碼Redis快取
- 快取常見問題及解決方案快取
- 如何解決快應用堆疊溢位問題
- 如何解決蘋果Mac安裝Axure首次開啟報錯的問題?蘋果Mac
- [譯] 什麼是快取 false sharing 以及如何解決(Golang 示例)快取FalseGolang
- 快取問題(一) 快取穿透、快取雪崩、快取併發 核心概念快取穿透
- 面對一個Bug,高手程式設計師是如何解決問題的?程式設計師
- 手摸手教你解決重定向快取問題快取
- redis快取相關問題及解決方案Redis快取
- Spring 迴圈依賴的三種方式(三級快取解決Set迴圈依賴問題)Spring快取
- JQuery中ajax的使用與快取問題的解決方法jQuery快取
- 快取更新快取
- 記憶體模型是怎麼解決快取一致性問題的?記憶體模型快取
- 快取 Laravel 模型的小問題快取Laravel模型
- 使用雙快取解決 Canvas clearRect 引起的閃屏問題快取Canvas
- gulp外掛解決瀏覽器快取問題瀏覽器快取
- 前端使用 gulp 解決多專案快取問題前端快取
- 布隆過濾器解決快取穿透問題過濾器快取穿透
- 如何解決快應用頁面滑動卡頓問題
- macOS Big Sur更新後你遇到了哪些問題如何解決Mac
- 阿里一面:關於【快取穿透、快取擊穿、快取雪崩、熱點資料失效】問題的解決方案阿里快取穿透
- 到底是先更新資料庫還是先更新快取?資料庫快取
- 如何解決 github 訪問慢的問題Github
- (轉)快取更新的套路快取
- 微軟是如何解決 PC 端程式多開問題的微軟
- 關於快取命中率的幾個關鍵問題!快取
- nginx代理天地圖做快取解決跨域問題Nginx地圖快取跨域
- win10系統下提示清除dns快取執行期間函式出了問題如何解決Win10DNS快取函式