記憶影像

2011 年 8 月 31 日

當人們開始一個企業應用程式時,最早的問題之一就是「我們如何與資料庫對話」。現在他們可能會問一個稍微不同的問題「我們應該使用哪一種資料庫 - 關係式或其中一個 NOSQL 資料庫?」。但還有另一個問題需要考慮:「我們應該使用資料庫嗎?」

企業應用程式的定義特徵之一是需要儲存長期資料,這自然會讓人們想到資料庫。畢竟,儲存資料是資料庫的主要功能之一。使用記憶影像是一種不涉及資料庫的不同持久化途徑。

記憶影像的關鍵元素是使用事件溯源,這基本上意味著應用程式狀態的每個變更都會擷取到一個事件中,並記錄到一個持久性儲存體中。此外,這表示您可以透過重播這些事件來重建完整的應用程式狀態。然後,事件就成為主要的持久化機制。

使用事件溯源的系統的一個常見範例是版本控制系統。每個變更都會擷取為一個提交,您可以透過將提交重播到一個空的目錄中來重建程式碼庫的目前狀態。當然,實際上重播所有事件太慢了,所以系統會儲存應用程式狀態的定期快照。然後,重建會載入最新的快照,並重播自該快照以來的所有事件。

事件溯源有許多後果,包括重建過去狀態的能力。但對於記憶影像而言,重要的特性是這表示不再需要擔心將應用程式狀態保留在最新的持久性儲存體中。相反地,您只要將應用程式狀態保留在主記憶體中即可。如果程序崩潰,您可以從事件(和快照)中重建它。

使用記憶影像可以讓您獲得高性能,因為所有事情都在記憶體中完成,而沒有資料庫系統的 IO 或遠端呼叫。或許更重要的是,這表示您可以擺脫資料庫對應碼,或擔心在記憶體中狀態和資料庫狀態之間同步。

相較之下,您必須確保您可以可靠地儲存事件並處理它們。您還需要撰寫程式碼來儲存和載入快照,並找出如何快速地還原系統以維持服務品質。資料庫也提供交易並行性以及持久性,所以您必須找出您將如何處理並行性。

另一個相當明顯的限制是,你必須擁有比你需要儲存在其中的資料更多的記憶體。隨著記憶體大小穩定增加,這比過去已成為較不重要的限制。[1]

許多不同類型的系統可以使用記憶體映像,我將提到我遇到的三個範例。

最新的是 LMAX。LMAX 是一個高性能交易系統,在單一 JVM 執行緒上處理 600 萬 TPS。在此,記憶體映像的效能優勢顯然是一個重要因素,但他們發現程式設計模式的簡化同樣重要。他們不必擔心並行性,因為這一切都與單一執行緒有關。為了保持高可用性,他們執行記憶體映像的複本,因此如果一個執行緒中斷,他們可以在保持極高交易率的同時切換到另一個執行個體。

幾年前,我寫了一篇關於使用 EventPoster 架構的幾個系統。此樣式為許多使用者介面提供對記憶體中模型的讀取存取,以進行分析目的。多個使用者介面表示多個執行緒,但只有一個寫入器(事件處理器),這大大簡化了並行性問題。

最古老的範例也是名稱的來源 - Smalltalk 開發環境。大多數開發工具依賴檔案系統中的文字檔,這些檔案會根據需要進行編譯或解釋。Smalltalk 將其所有原始碼和編譯方法保存在映像中 [2]。您執行的每個指令都會儲存在變更記錄中。大多數時候,您會儲存您的映像(快照),但如果需要,您可以從穩定的基礎中重新播放變更記錄,如果您做了愚蠢的事情。

就像許多此類想法一樣,這是一種已被使用和多次重新發明的做法 [3],但從未獲得主流關注。讓資料庫保存持久性資料仍然是較常見的做法。

我聽說過記憶體映像的一個問題是關於遷移。每當您建立軟體系統時,了解它將如何處理變更非常重要。對於記憶體映像,基本任務是確保您可以繼續從事件記錄中重建記憶體映像。

此處有一個陷阱,就是使用事件記錄的序列化結構,如果要變更事件結構,它無法妥善處理演化。如果您建立特定事件類別並序列化它們,這可能會讓您難以處理舊事件,因為您稍後變更了事件類別的結構。通常最好使用通用資料結構(例如地圖和清單)進行序列化。

此外,在事件和模型結構本身之間保持良好的解耦也很重要。您可能會想建立一些自動對應系統,回顧事件資料和模型,但這會將事件和模型結合在一起,這會讓您難以移轉模型,並仍然處理舊事件。

在某些時候,可能值得將事件記錄本身從舊格式移轉到新格式。移轉事件記錄通常更麻煩,但如果您已經從原始事件結構演化很長一段時間,這可能會是一個選項。

很長一段時間以來,反對使用記憶體映像的一個重要論點是大小,但現在大多數商品伺服器都比我們過去習慣在磁碟中擁有的記憶體更多。因此,現在大多數工作集都可以安全地保存在記憶體中。我們在幾年前就注意到了這一點,但記憶體映像仍然相對少見。我認為現在 NOSQL 運動正促使人們重新思考其持久性選項,我們可能會看到這種模式的激增。

備註

1: 如果您只需要事件中的資料子集,您也可能能夠減少記憶體使用量。如果您需要,您可以將相同的事件傳送給不同的記憶體映像系統,以取得不同的資料子集。

2: 我這裡使用過去式來表示 Smalltalk,因為我已經很久沒有使用它了,而且它在這些年來可能已經改變了其行為。它仍然存在,儘管遺憾的是它只是一個利基環境。

3: 有些人可能還記得 Prevayler 專案,它是這種方法的開源實作。它在幾年前在 Java 社群中引起了很多討論,但從那以後就相當沉寂了。該社群使用 系統普遍性 作為這種方法的通用術語。