檢視原始碼 persistent_term (erts v15.2)

持久化 term。

此模組與 ets 類似,它提供 Erlang term 的儲存空間,可以以常數時間存取。但不同之處在於,persistent_term 已針對讀取 term 進行高度優化,而犧牲了寫入和更新 term 的效能。當持久化 term 被更新或刪除時,會執行全域垃圾回收掃描所有程序,以尋找已刪除的 term,並將其複製到仍然使用它的每個程序中。因此,persistent_term 適用於儲存經常存取但從不或很少更新的 Erlang term。

警告

持久化 term 是一項進階功能,並非 ETS 表格的一般替代品。在使用持久化 term 之前,請務必充分了解更新或刪除持久化 term 時對系統效能的影響。

Term 查找(使用 get/1)是以常數時間完成,並且不需取得任何鎖定,而且 term 不會複製到堆積 (heap) 中 (如同儲存在 ETS 表格中的 term)。

儲存或更新 term (使用 put/2) 所需的時間與已建立的持久化 term 數量成正比,因為保存鍵的雜湊表會被複製。此外,term 本身也會被複製。

當 (複雜的) term 被刪除 (使用 erase/1) 或被另一個 term 取代 (使用 put/2) 時,會啟動全域垃圾回收。其運作方式如下:

  • 系統中的所有程序都會被排程掃描它們的堆積,以尋找已刪除的 term。雖然此類掃描相對輕量,但如果程序數量很多,系統可能會變得反應較慢,直到所有程序都掃描完它們的堆積。
  • 如果刪除的 term (或其任何部分) 仍然被某個程序使用,該程序將執行一次主要的 (全掃描) 垃圾回收,並將該 term 複製到該程序中。但是,一次最多只會有兩個程序被排程執行這種垃圾回收。

原子和其他適合一個機器字元的 term 的刪除經過特殊優化,可避免執行全域 GC。但仍然不建議過於頻繁地使用此類值更新持久化 term,因為每次更新持久化 term 時都會複製保存鍵的雜湊表。

一些適合使用持久化 term 的範例包括:

  • 儲存必須由所有程序輕鬆存取的組態資料。
  • 儲存 NIF 資源的參考。
  • 儲存高效計數器的參考。
  • 儲存一個原子以指示記錄層級或是否開啟偵錯。

儲存巨大的持久化 Term

目前持久化 term 的實作使用也用於 BEAM 程式碼中文字 (常數 term) 的文字 配置器。預設情況下,會在 BEAM 程式碼和持久化 term 中為文字保留 1 GB 的虛擬位址空間。可以使用啟動模擬器時的 +MIscs 選項 變更為文字保留的虛擬位址空間量。

以下範例說明如何將文字的保留虛擬位址空間增加到 2 GB (2048 MB)

    erl +MIscs 2048

使用持久化 Term 的最佳實務

建議使用類似 ?MODULE{?MODULE,SubKey} 的鍵,以避免名稱衝突。

建議建立少數大型持久化 term,而不是建立許多小型持久化 term。儲存持久化 term 的執行時間與已存在的 term 數量成正比。

使用與其已有的值相同的值更新持久化 term 會經過特殊優化,可以快速地不做任何事情;因此,不需要比較舊值和新值,並避免在值相等時呼叫 put/2

當原子或其他適合一個機器字元的 term 被刪除時,不需要全域 GC。因此,以原子作為值的持久化 term 可以更頻繁地更新,但請注意,更新此類持久化 term 仍然比讀取它們要昂貴得多。

如果 term 不適合一個機器字元,更新或刪除持久化 term 將會觸發全域 GC。程序將照常排程,但所有程序都會一次變成可執行,這會使系統反應較慢,直到所有程序都執行完畢並掃描完它們的堆積以尋找已刪除的 term。一種將對反應性的影響降至最低的方法可能是在更新或刪除持久化 term 之前,盡可能減少節點上的程序數量。避免在系統處於尖峰負載時更新 term 也是明智之舉。

如果未來可能會刪除或更新擷取的持久化 term,請避免將其儲存在程序中。如果某個程序在 term 被刪除時仍然持有對持久化 term 的參考,則該程序將會被垃圾回收,並且該 term 會被複製到該程序。

避免一次更新或刪除多個持久化 term。每個已刪除的 term 都會觸發自己的全域 GC。這表示刪除 N 個 term 會使系統反應變慢的時間比刪除單個持久化 term 長 N 倍。因此,要同時更新的 term 應該收集到一個更大的 term 中,例如,一個 Map 或一個 Tuple。

範例

以下範例說明如何透過讓每個排程器都有一個 ETS 表格,來最大程度地減少 ETS 表格的鎖定爭用。ETS 表格的表格識別碼會儲存在單個持久化 term 中。

    %% There is one ETS table for each scheduler.
    Sid = erlang:system_info(scheduler_id),
    Tid = element(Sid, persistent_term:get(?MODULE)),
    ets:update_counter(Tid, Key, 1).

總結

類型

任何 Erlang term。

任何 Erlang term。

函式

刪除具有鍵 Key 的持久化 term 的名稱。

擷取所有持久化 term 的鍵和值。

擷取與鍵 Key 關聯的持久化 term 的值。

擷取與鍵 Key 關聯的持久化 term 的值。

以 Map 的形式傳回有關持久化 term 的資訊。

將值 Value 儲存為持久化 term,並將其與鍵 Key 關聯。

類型

連結到此類型

key()

檢視原始碼 (自 OTP 21.2 起)
-type key() :: term().

任何 Erlang term。

連結到此類型

value()

檢視原始碼 (自 OTP 21.2 起)
-type value() :: term().

任何 Erlang term。

函式

連結到此函式

erase(Key)

檢視原始碼 (自 OTP 21.2 起)
-spec erase(Key) -> Result when Key :: key(), Result :: boolean().

刪除具有鍵 Key 的持久化 term 的名稱。

如果存在具有鍵 Key 的持久化 term,則傳回值為 true,如果沒有與該鍵關聯的持久化 term,則傳回 false

如果存在與鍵 Key 關聯的先前的持久化 term,則在 erase/1 傳回時會啟動全域 GC。請參閱說明

連結到此函式

get()

檢視原始碼 (自 OTP 21.2 起)
-spec get() -> List when List :: [{key(), value()}].

擷取所有持久化 term 的鍵和值。

鍵將會複製到呼叫 get/0 的程序的堆積,但值不會。

連結到此函式

get(Key)

檢視原始碼 (自 OTP 21.2 起)
-spec get(Key) -> Value when Key :: key(), Value :: value().

擷取與鍵 Key 關聯的持久化 term 的值。

查找將以常數時間進行,並且該值不會複製到呼叫程序的堆積中。

如果沒有以鍵 Key 儲存的 term,此函式將會因 badarg 例外而失敗。

如果呼叫程序保留持久化 term 的值,並且該持久化 term 在未來被刪除,則該 term 將會複製到該程序。

連結到此函式

get(Key, Default)

檢視原始碼 (自 OTP 21.3 起)
-spec get(Key, Default) -> Value when Key :: key(), Default :: value(), Value :: value().

擷取與鍵 Key 關聯的持久化 term 的值。

查找將以常數時間進行,並且該值不會複製到呼叫程序的堆積中。

如果沒有以鍵 Key 儲存的 term,此函式會傳回 Default

如果呼叫程序保留持久化 term 的值,並且該持久化 term 在未來被刪除,則該 term 將會複製到該程序。

連結到此函式

info()

檢視原始碼 (自 OTP 21.2 起)
-spec info() -> Info
              when
                  Info :: #{count := Count, memory := Memory},
                  Count :: non_neg_integer(),
                  Memory :: non_neg_integer().

以 Map 的形式傳回有關持久化 term 的資訊。

該 Map 具有下列鍵:

  • count - 持久化 term 的數量。

  • memory - 所有持久化 term 使用的總記憶體量 (以位元組為單位)。

連結到此函式

put(Key, Value)

檢視原始碼 (自 OTP 21.2 起)
-spec put(Key, Value) -> ok when Key :: key(), Value :: value().

將值 Value 儲存為持久化 term,並將其與鍵 Key 關聯。

如果值 Value 等於先前為該鍵儲存的值,則 put/2 將不做任何動作並快速傳回。

如果存在與鍵 Key 關聯的先前的持久化 term,則在 put/2 傳回時會啟動全域 GC。請參閱說明