檢視原始碼 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).
總結
函式
刪除具有鍵 Key
的持久化 term 的名稱。
擷取所有持久化 term 的鍵和值。
擷取與鍵 Key
關聯的持久化 term 的值。
擷取與鍵 Key
關聯的持久化 term 的值。
以 Map 的形式傳回有關持久化 term 的資訊。
將值 Value
儲存為持久化 term,並將其與鍵 Key
關聯。
類型
函式
刪除具有鍵 Key
的持久化 term 的名稱。
如果存在具有鍵 Key
的持久化 term,則傳回值為 true
,如果沒有與該鍵關聯的持久化 term,則傳回 false
。
擷取所有持久化 term 的鍵和值。
鍵將會複製到呼叫 get/0
的程序的堆積,但值不會。
擷取與鍵 Key
關聯的持久化 term 的值。
查找將以常數時間進行,並且該值不會複製到呼叫程序的堆積中。
如果沒有以鍵 Key
儲存的 term,此函式將會因 badarg
例外而失敗。
如果呼叫程序保留持久化 term 的值,並且該持久化 term 在未來被刪除,則該 term 將會複製到該程序。
擷取與鍵 Key
關聯的持久化 term 的值。
查找將以常數時間進行,並且該值不會複製到呼叫程序的堆積中。
如果沒有以鍵 Key
儲存的 term,此函式會傳回 Default
。
如果呼叫程序保留持久化 term 的值,並且該持久化 term 在未來被刪除,則該 term 將會複製到該程序。
-spec info() -> Info when Info :: #{count := Count, memory := Memory}, Count :: non_neg_integer(), Memory :: non_neg_integer().
以 Map 的形式傳回有關持久化 term 的資訊。
該 Map 具有下列鍵:
count
- 持久化 term 的數量。memory
- 所有持久化 term 使用的總記憶體量 (以位元組為單位)。
將值 Value
儲存為持久化 term,並將其與鍵 Key
關聯。
如果值 Value
等於先前為該鍵儲存的值,則 put/2
將不做任何動作並快速傳回。