我們 Sportmaster 如何選擇快取系統。 第1部分

你好!我叫 Alexey Pyankov,是 Sportmaster 的開發人員。 郵政 我講述了 2012 年 Sportmaster 網站工作的開始過程、我們成功「推進」了哪些舉措以及我們遇到了哪些陷阱。

今天我想分享我在另一個故事之後的想法——在網站管理面板中為 Java 後端選擇快取系統。這個故事對我來說意義非凡——雖然只持續了兩個月,但這 2 天我們每天工作 60-12 小時,沒有一天休息。我從未想過,也從未想過自己可以工作這麼多。

因此,我把文章分成兩部分,以免內容過於冗長。第一部分會比較輕鬆——準備工作、介紹以及一些關於快取的思考。如果您已經是一位經驗豐富的開發人員,或者曾經使用過快取——從技術角度來看,本文很可能不會有什麼新內容。但對於初學者來說,這樣一篇簡短的概述可以指引他,在遇到這樣的十字路口時該如何思考。

我們 Sportmaster 如何選擇快取系統。 第1部分

Sportmaster 網站新版上線時,資料接收方式略顯繁瑣。先前的網站版本(Bitrix)的表格需要導入 ETL 系統,重新設計外觀,並添加十幾個系統的各種花俏功能。網站上要顯示新圖片或產品描述,必須等到第二天才能更新,而且每天只能在晚上更新一次。

起初,從投入生產的最初幾週開始,我們非常擔心,認為這些對內容管理員來說的不便不過是小事一樁。但一切塵埃落定後,專案繼續推進——幾個月後,也就是2015年初,我們開始積極開發管理面板。 2015年和2016年,一切進展順利,我們定期發布新版本,管理面板的資料準備工作也越來越豐富,我們也在為團隊即將接手最重要、最棘手的工作——產品輪廓(所有產品數據的全面準備和維護)——做好準備。然而,在2017年夏天,就在產品輪廓正式上線之前,專案卻陷入了非常艱難的境地——恰恰是因為緩存問題。我想在這篇由兩部分組成的文章的第二部分中,向大家講述這段經歷。

但在這篇文章中,我將從遠處開始,分享一些想法 - 有關快取的想法,這將是在大型專案之前進行審查的一個很好的步驟。

當出現快取問題時

快取任務並非憑空而來。我們是開發者,我們開發了一個軟體產品,並希望它能夠受到歡迎。如果產品受歡迎且成功,使用者就會湧入。而且用戶越來越多。隨著使用者數量的增加,產品的負載也會越來越高。

在最初階段,我們不會考慮程式碼最佳化和效能。主要關注功能,快速推出試點項目並測試各種假設。如果負載增加,我們會升級硬體。我們會將其增加兩到三倍、五倍,甚至十倍。但到了一定時候,財務狀況就不允許再增加硬體了。那麼用戶數量會成長多少倍呢?不會是10、2、5倍,但如果成功的話,將會成長10、100倍,甚至1000萬倍。這遲早會發生,但優化是必須的。

假設程式碼的某一部分(我們姑且稱之為函數)運行時間過長,而我們想要減少這段程式碼的執行時間。函數可以是存取資料庫,也可以是執行某些複雜的邏輯-關鍵在於執行時間非常長。那麼,執行時間可以減少多少呢?極限情況下,可以減少到零,不能再少了。那麼,如何才能將執行時間降到零呢?答案是:完全不執行。而是立即回傳結果。那麼,我們要如何找到結果呢?答案是:要嘛計算,要嘛查找。計算需要很長時間。查找意味著,例如,記住函數上次使用相同參數呼叫時給出的結果。

也就是說,函數的具體實作對我們來說並不重要。我們只需要知道結果依賴哪些參數就足夠了。然後,如果將參數值呈現為一個對象,並可將其用作某些儲存中的鍵,那麼我們就可以保存計算結果,並在下次訪問時讀取。如果這些寫入-讀取結果的速度比函數執行速度更快,那麼我們就能獲得速度上的收益。收益值可以達到 100、1000 甚至 100 萬倍(10^5 是一個例外,但在基數相當滯後的情況下,這是完全有可能的)。

快取系統的基本要求

快取系統首先需要滿足的要求是快速的讀取速度,其次是寫入速度。這點毋庸置疑,但前提是系統要投入生產。

讓我們演練這樣一個案例。

假設我們已經為當前負載提供了硬體支持,現在正在逐步實現快取。用戶數量略有增長,負載也隨之增加——我們添加一些緩存,並進行一些調整。這種情況持續了一段時間,現在那些高負載函數實際上不再被呼叫——所有主要負載都落在了快取上。在此期間,用戶數量增加了 N 倍。

如果初始硬體預留空間可以增加 2-5 倍,那麼在快取的幫助下,我們可以將效能提升 10 倍,在最佳情況下可以提升 100 倍,某些情況下甚至提升 1000 倍。也就是說,在相同的硬體上,我們可以處理 100 倍以上的請求。太棒了,我們值得好好犒賞自己!

但現在,有一天,碰巧系統崩潰了,快取也崩潰了。這沒什麼特別的——畢竟,選擇快取就是為了「高速讀寫,其他都不重要」。

相對於初始負載,我們的硬體儲備是初始負載的 2-5 倍,而這段時間的負載增加了 10-100 倍。借助緩存,我們排除了對高負載函數的調用,因此一切都運行順暢。現在,如果沒有緩存,我們的系統會崩潰多少次?會發生什麼事?系統會崩潰。

即使我們的快取沒有崩潰,只是暫時被清除,也需要預熱,這需要一些時間。在此期間,主要負載將落在功能上。

結論:生產中的高負載項目要求快取系統不僅要有較高的讀寫速度,還要有資料完整性和容錯能力。

選擇的痛苦

在一個有管理面板的專案中,我們做出了這樣的選擇:首先我們安裝了 Hazelcast,因為我們已經從主網站的使用經驗中熟悉了這款產品。但是,這樣的選擇最終失敗了——在我們的負載設定檔下,Hazelcast 的運行速度不僅很慢,而且非常慢。而且當時,我們已經簽訂了將其投入生產的最後期限。

劇透:事情究竟是如何發展到我們錯失良機,陷入如此緊張的境地的——我會在第二部分告訴你——以及我們是如何走到這一步,又是如何脫身的。但現在——我只想說,當時壓力很大,而且「思考——不知怎麼的,根本沒辦法思考,我們只能搖晃瓶子。」「搖晃瓶子」也算劇透,稍後會詳細介紹。

我們做了什麼:

  1. 我們列出了 Google 和 StackOverflow 推薦的所有系統。其中有 30 多個
  2. 我們編寫的測試負載與生產環境的典型負載相當。為此,我們記錄了在生產環境中通過系統的數據——這是一種嗅探器,用於嗅探系統內部而非網路上的數據。我們將這些數據放入測試中。
  3. 整個團隊,每個人都從清單中選擇下一個系統,進行配置,運行測試。如果測試不通過,表示它無法承受負載,我們就把它淘汰,然後繼續測試佇列中的下一個系統。
  4. 到了17號系統,一切都變得毫無希望了。 「搖晃瓶子」夠了,是時候認真思考了。

但當你需要選擇一個能夠在預先準備的測試中「快速通過」的系統時,這是一個選擇。但如果還沒有這樣的測試,而你想選擇更快的系統,該怎麼辦?

讓我們對這樣的選項進行建模(很難想像中級以上的開發人員生活在真空中,並且在選擇時還沒有形成對首先嘗試哪種產品的偏好 - 因此,進一步的推理更多的是理論性的/哲學的/關於初級的)。

明確需求後,我們就開始選擇現成的解決方案。何必再費力:我們直接用現成的快取系統就行了。

如果您是剛入門,並且會使用 Google,那麼順序可能會有所調整,但總體來說,指南如下。首先,您會遇到 Redis,它人人都在談論。然後,您會了解到 EhCache 是最古老、最成熟的系統。接下來,您將了解 Tarantool,這是一個自主研發的解決方案,它具有獨特的優勢。此外,還有 Ignite,因為它現在越來越受歡迎,並且得到了 SberTech 的支持。最後,還有 Hazelcast,因為它在企業領域中經常出現在大型公司中。

這份名單並非詳盡無遺,有幾十個系統。我們只附上一個。我們將把這五個系統帶去「選美大賽」來評選。誰會是終極的贏家?

Redis的

我們來看看他們官方網站上寫了什麼。
Redis的 — 開源專案。提供記憶體資料儲存、磁碟持久化、自動分割區、高可用性以及網路故障復原功能。

看起來一切都很好,你可以把它拿去擰緊——它能完成所有需要的功能。不過,出於興趣,我們還是來看看其他的候選產品吧。

EhCache

EhCache — 「Java 最廣泛使用的快取」(官網口號翻譯)。而且開源。由此我們可以看出,Redis 並非 Java 專用,而是一個通用緩存,需要封裝器才能與其互動。而 EhCache 則更加便捷。該系統還承諾了什麼?可靠性、可靠性、功能齊全。嗯,它也是應用最廣泛的緩存,能夠緩存 TB 等級的資料。

Redis忘了,準備選EhCache。

但愛國精神促使我看到了 Tarantool 的優點。

塔蘭圖爾

塔蘭圖爾 ——映入眼簾的是「即時資料整合平台」的稱號。這聽起來很複雜,於是我們仔細閱讀了頁面,發現了一個醒目的聲明:「將 100% 的資料緩存在 RAM 中」。這不禁讓人產生疑問——畢竟,資料量可能遠超記憶體容量。解讀一下,Tarantool 的意思是,它不會運行序列化操作將資料從記憶體寫入磁碟。相反,它使用低階系統功能,將記憶體簡單地映射到具有良好 I/O 指標的檔案系統。總而言之,他們做了一件非常棒、很酷的事。

讓我們來看看具體實施情況:Mail.ru 企業主幹網路、Avito、Beeline、Megafon、Alfa-Bank、Gazprom…

如果對 Tarantool 還有任何疑問,那麼萬事達卡的實施案例就徹底打消了我的疑慮。我選擇 Tarantool。

但仍然...

點燃

…還有更多 點燃,號稱是一個「記憶體運算平台…在PB級資料上實現記憶體速度」。它也有很多優點:分散式記憶體快取、最快的鍵值儲存和快取、水平擴展、高可用性、強完整性。總的來說,最快的是Ignite。

實施案例:俄羅斯聯邦儲蓄銀行、美國航空、雅虎日本。我發現 Ignite 不僅在俄羅斯聯邦儲蓄銀行實施,SberTech 團隊還派人到 Ignite 團隊內部進行產品改進。這完全吸引了我,我已經準備好接受 Ignite 了。

完全不清楚為什麼,我看第五點。

淡褐色

我去網站 淡褐色我讀到過。事實證明,最快的分散式快取解決方案是 Hazelcast。它比所有其他解決方案都快幾個數量級,並且通常是記憶體資料網格領域的領導者。在這種背景下選擇其他方案意味著不尊重自己。而且它還使用冗餘資料儲存來確保叢集持續運行,不會遺失資料。

好的,我準備好接受 Hazelcast 了。

對照

但仔細觀察,你會發現這五位候選人各有千秋,堪稱佼佼者。該如何選擇呢?我們可以看看哪位候選人最受歡迎,然後再進行比較,這樣一來,問題就迎刃而解了。

我們找到了一個這樣的 概觀,我們選擇了我們的5個系統。

我們 Sportmaster 如何選擇快取系統。 第1部分

它們的排序如下:Redis 位居榜首,Hazelcast 位居第二,Tarantool 和 Ignite 越來越受歡迎,EhCache 保持不變。

但讓我們看看 計算方法:網站連結、對系統的普遍興趣、工作機會——太棒了!也就是說,當我的系統崩潰時,我會說:「不,它很可靠!這裡有很多工作機會…」。這種簡單的比較根本行不通。

所有這些系統都不僅僅是快取系統。它們還具有許多其他功能,包括當資料不傳輸到客戶端進行處理時,而是反之亦然:需要對資料執行的程式碼被移動到伺服器,在那裡執行,並傳回結果。而且它們通常不被視為單獨的快取系統。

好吧,別放棄,讓我們直接比較一下這兩個系統。我們先來比較一下前兩個選項——Redis 和 Hazelcast。我們關注的是速度,所以就用這個參數來比較它們。

Hz 與 Redis 比較

我們發現 對照:
我們 Sportmaster 如何選擇快取系統。 第1部分

藍色代表 Redis,紅色代表 Hazelcast。 Hazelcast 在各方面都勝出,原因如下:它是多執行緒的,高度最佳化,每個執行緒都處理自己的分區,因此沒有鎖。而 Redis 是單線程的,無法從現代多核心 CPU 中獲益。 Hazelcast 採用非同步 I/O,而 Redis-Jedis 則採用阻塞套接字。總而言之,Hazelcast 使用二進位協議,而 Redis 是面向文本的,也就是說,它效率低。

為了以防萬一,我們再看看其他的比較結果。它會帶給我們什麼呢?

Redis 與 Hz

其他 對照:
我們 Sportmaster 如何選擇快取系統。 第1部分

相反,紅色代表 Redis。也就是說,Redis 在性能方面勝過 Hazelcast。在第一次比較中,Hazelcast 勝出,而在第二次比較中,Redis 勝出。 這裡也有 非常準確地解釋了為什麼Hazelcast在之前的比較中獲勝。

事實證明,第一個結果實際上是被操縱的:Redis 被放在一個基礎盒子裡,而 Hazelcast 則被定制用於測試用例。結果證明:首先,你不能相信任何人;其次,當我們最終選擇一個系統時,我們仍然需要正確地配置它。這些設定包括數十個,甚至數百個參數。

搖晃瓶子

我們剛才做的整個過程,我可以用「搖晃瓶子」這個比喻來解釋。也就是說,現在你不需要編程,現在最重要的是能夠閱讀 StackOverflow。我的團隊裡有一個人,他是一位專業人士,在關鍵時刻就是這樣運作的。

他做了什麼?他看到一個不工作的東西,查看堆疊跟踪,從中找出一些詞(這些詞正是他在程序方面的專長),在谷歌上搜索,在 StackOverflow 的答案中找到答案。他不看也不思考,就在問題的答案中,選擇了一個最接近“做這個做那個”的建議(選擇這樣的答案是他的天賦,因為這個答案並不總是能獲得更多點贊),應用它,查看:如果有什麼變化了,那就太好了。如果沒有變化,我們回滾。然後重複啟動-檢查-搜尋的過程。他以如此直觀的方式實現了這一點,以至於一段時間後程式碼就能正常運作了。他不知道為什麼,不知道自己做了什麼,也無法解釋。但是!這種感染起作用了。於是「火被撲滅了」。現在我們終於明白自己做了什麼。當程序正常工作時,一切都變得簡單了,也節省了大量時間。

這個例子很好地解釋了這個方法。

曾經流行在瓶子裡組裝一艘帆船。然而,帆船體積大且易碎,瓶頸又很窄,根本推不進去。該如何組裝呢?

我們 Sportmaster 如何選擇快取系統。 第1部分

有這麼一個方法,非常快捷,非常有效。

這艘船由一堆小東西組成:木棍、繩子、船帆、膠水。我們把這些東西都放進瓶子裡了。
我們雙手捧著瓶子,開始搖晃。搖啊搖啊。通常情況下——當然,它最終會變成一坨屎。但有時……有時它會變成一艘船!更確切地說,是看起來像船的東西。

我們把這個東西拿給別人看:「Seryoga,你看到了嗎!?」的確,從遠處看,它看起來像一艘船。但它無法再往前送了。

還有另一種方法。更高級的人,例如駭客,會用它。

你給一個人佈置任務,他做完所有事情就走了。你看著──任務好像完成了。過了一段時間,當你需要改進程式碼時——他又開始做這種事了……還好他能跑遠一點。這些人會用瓶子做例子:你看,瓶子底部-玻璃會彎曲。而且它透明不透明,不太明顯。然後,「駭客」鋸掉底部,把一艘船放進去,再把底部黏回去,看起來就應該是那樣了。

從問題陳述的角度來看,一切似乎都正確。但以船為例:為什麼要製造這艘船?誰需要它?它沒有任何功能。通常,這種船是送給地位很高的人的禮物,他們會把它放在自己頭頂的架子上,作為某種象徵或標誌。但如果這樣的人,比如大企業的老闆或高級官員,擁有這樣一件像國旗一樣掛在脖子上的駭人聽聞的玩意兒呢?最好讓他永遠不要發現。那麼,他們最終是如何製造出這些可以送給重要人物的船的呢?

唯一一個關鍵之處,也是最關鍵之處,根本無法真正做到。船體恰好嵌入瓶頸。而船體是在瓶外組裝的。但這不僅僅是組裝船,更是一門真正的珠寶工藝。部件上添加了特殊的槓桿,以便日後將其提起。例如,船帆被折疊起來,小心翼翼地放入瓶內,然後藉助鑷子,非常熟練、精準地將它們拉起並提起。最終成品是一件藝術品,可以作為禮物贈送,並感到無愧于心。

如果我們希望專案成功,團隊中至少要有一位珠寶設計師。他必須注重產品質量,考慮周全,即使在壓力重重、需要犧牲重要事項緊急處理的情況下,也不會犧牲任何一個方面。所有成功、可持續、經受時間考驗的項目都建立在這項原則之上。這些項目都精準獨特,充分利用了所有可能的可能性。在瓶中船的例子中,船身穿過瓶頸這個設計就得到了充分的體現。

回到選擇快取伺服器的問題,這個方法該如何應用?我建議從所有現有的系統中進行選擇——不要盲目地挑選,也不要盲目地選擇,而是要看它們的原理,以及選擇系統時需要注意什麼。

在哪裡尋找瓶頸

我們盡量不要搖晃瓶子,也不要把裡面的東西一一整理出來,而是看看如果突然出現什麼問題,對於你的任務——自己設計這樣一個系統——來說,會是什麼問題。當然,我們不會組裝自行車,但我們會用這個方案來了解情況,以及產品說明中需要注意的要點。讓我們來勾勒出這樣一個方案。

我們 Sportmaster 如何選擇快取系統。 第1部分

如果系統是分散式的,那麼我們將有多個伺服器(6 台)。假設有四台(為了方便在圖中標出,當然可以有任意數量的伺服器)。如果伺服器位於不同的節點上,則所有節點上都會運行一些程式碼,負責將這些節點組成一個集群,並在發生故障時相互連接和識別。

我們還需要邏輯代碼 (2),它實際上與快取有關。客戶端透過一些 API 與這些程式碼互動。客戶端程式碼 (1) 可以位於同一個 JVM 中,也可以透過網路存取。內部實作的邏輯是決定哪些物件保留在快取中,哪些物件需要丟棄。我們使用記憶體 (3) 來儲存緩存,但如有必要,也可以將一些資料儲存在磁碟 (4) 上。

讓我們看看負載會發生在哪些部分。實際上,每個箭頭和每個節點都會被載入。首先,在客戶端程式碼和 API 之間,如果這是網路交互,那麼延遲會非常明顯。其次,在 API 內部—由於邏輯過於複雜,我們可能會遇到 CPU 負載。如果邏輯不再佔用記憶體就好了。此外,還有與檔案系統的交互——在通常的版本中,這是序列化/恢復和寫入/讀取。

接下來是與叢集的交互。它很可能位於同一系統中,但也可以獨立存在。在這裡,同樣需要考慮向集群的資料傳輸、資料序列化的速度以及集群之間的交互作用。

現在,一方面,我們可以想像在處理來自我們程式碼的請求時,快取系統中「哪些齒輪會旋轉」;另一方面,我們可以估算我們的程式碼會向這個系統產生哪些請求以及請求數量。這足以讓我們做出一個或多或少比較理性的選擇——選擇一個適合我們用例的系統。

淡褐色

讓我們看看這種分解如何應用於我們的清單。例如,Hazelcast。

為了從 Hazelcast 中取得/放入數據,客戶端程式碼需要存取 (1) api。 Hz 允許您以嵌入式方式運行伺服器,在這種情況下,存取 api 是 JVM 內部的方法調用,您可以免費計算。

為了讓 (2) 中的邏輯正常運作,Hz 依賴序列化金鑰位元組陣列的雜湊值——也就是說,金鑰無論如何都會被序列化。這對於 Hz 來說是不可避免的開銷。
驅逐策略已實現良好,但對於特殊情況,您可以自行連接。您無需擔心這部分。

儲存 (4) 可以連接。非常好。嵌入式互動 (5) 可以視為即時的。叢集中節點之間的資料交換 (6) – 是的,確實存在。這是為了提高容錯能力,但犧牲了速度。 Hz 特性的近距離緩存功能可以降低成本-從叢集其他節點接收的資料將被快取。

在這種情況下可以做些什麼來提高速度?

例如,為了避免 (2) 中的鍵序列化,需要在 Hazelcast 之上添加另一個緩存,用於儲存最熱門的資料。 Sportmaster 為此選擇了 Caffeine。

對於等級(6)調整,Hz 提供兩種儲存類型:IMap 和 ReplicatedMap。
我們 Sportmaster 如何選擇快取系統。 第1部分

值得一提的是,Hazelcast 是如何進入 Sportmaster 技術堆疊的。

2012年,當我們在為未來網站進行首個試點計畫時,Hazelcast 竟然成了搜尋引擎給出的第一個連結。我們「第一次」就熟悉了它——僅僅兩個小時後,當我們把 Hz 嵌入系統後,它就運行起來了,這讓我們感到很驚訝。當天結束時,我們完成了一些測試,並且非常滿意。這份喜悅足以克服 Hz 隨著時間的推移所帶來的意外。現在,Sportmaster 團隊沒有理由拒絕 Hazelcast 了。

當然,諸如「搜尋引擎中的第一個連結」和「快速組裝的 HelloWorld」之類的論點只是例外,它們體現了選擇時的當時情況。所選系統的真正考驗始於發佈到生產環境,而選擇任何系統(包括快取系統)時,這個階段都值得關注。實際上,就我們的情況而言,我們可以說選擇 Hazelcast 純屬偶然,但後來事實證明我們的選擇是正確的。

對於生產環境來說,更重要的是:監控、處理單一節點的故障、資料複製、擴展成本。也就是說,需要關注系統維護過程中出現的問題——當負載比計劃高出數十倍時,當我們意外地將錯誤的內容上傳到錯誤的位置時,當需要推出新版本的代碼、替換數據並且不讓客戶察覺時。

對於所有這些要求,Hazelcast 無疑符合要求。

To be continued

但 Hazelcast 並非萬靈丹。 2017 年,我們選擇 Hazelcast 作為管理員緩存,僅僅是基於過去經驗的良好印象。這在一個非常殘酷的玩笑中發揮了關鍵作用,讓我們陷入困境,並「英勇地」在 60 天內擺脫了困境。下一篇會詳細介紹。

到那時…新程式碼快樂!

來源: www.habr.com

為具有 DDoS 保護、VPS VDS 服務器的站點購買可靠的主機 🔥 購買具備 DDoS 防護的可靠網站寄存服務,包括 VPS 和 VDS 伺服器 | ProHoster