本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。
Memcached 的快取策略
在下列主題中,您可以找到填充和維護 Memcached 快取的策略。
您實作用於填入和維護快取的策略,取決於您要快取的資料,以及該資料的存取模式。例如,您可能不會想對遊戲網站前十名排行榜和趨勢新聞報導使用相同的策略。在本節的其餘部分,我們會討論常見的快取維護策略,以及其優點和缺點。
延遲載入
如同名稱所隱含的意義,延遲載入是一項快取策略,只有在必要時才會將資料載入快取中。它的運作方式如下。
Amazon ElastiCache 是記憶體內鍵值存放區,位於您的應用程式與其存取的資料存放區 (資料庫) 之間。每當您的應用程式請求資料時,它都會先向 ElastiCache 快取提出請求。如果資料存在於快取中且為最新版本, 會將資料ElastiCache 傳回至您的應用程式。如果資料不存在於快取中或已過期,則應用程式會向資料存放區請求資料。接著資料存放區會將資料傳回至應用程式。。應用程式會將從存放區收到的資料寫入快取中。如此一來,下次收到請求時就能更快速擷取這些資料。
當資料存在於快取中且未過期,就會發生快取命中:
應用程式向快取請求資料。
快取將資料傳回給應用程式。
當資料不存在於快取中已過期,就會發生快取未命中:
應用程式向快取請求資料。
快取中沒有所請求的資料,因此傳回
null
。應用程式請求並收到來自資料庫的資料。
應用程式以新資料更新快取。
延遲載入的優點和缺點
延遲載入的優點如下:
-
只會將請求的資料加以快取。
因為大部分資料永遠不會被請求,延遲載入可避免將快取填滿未請求的資料。
-
節點故障不會成為應用程式的嚴重問題。
當節點故障並且由新的空白節點取代時,應用程式會繼續運作,只是會增加延遲。對新節點提出請求時,每次快取未命中都會產生一個資料庫查詢。同時會將資料複本新增至快取中,以便從快取擷取後續的請求。
延遲載入的缺點如下:
延遲載入虛擬程式碼範例
以下是延遲載入邏輯的虛擬程式碼範例。
// *****************************************
// function that returns a customer's record.
// Attempts to retrieve the record from the cache.
// If it is retrieved, the record is returned to the application.
// If the record is not retrieved from the cache, it is
// retrieved from the database,
// added to the cache, and
// returned to the application
// *****************************************
get_customer(customer_id)
customer_record = cache.get(customer_id)
if (customer_record == null)
customer_record = db.query("SELECT * FROM Customers WHERE id = {0}", customer_id)
cache.set(customer_id, customer_record)
return customer_record
在此範例中,取得資料的應用程式程式碼如下。
customer_record = get_customer(12345)
全部寫入
每當有資料寫入資料庫,全部寫入 (write-through) 策略就會在快取中新增或更新資料。
全部寫入的優點和缺點
全部寫入的優點如下:
-
快取中的資料絕不會過時。
快取中的資料每次寫入資料庫時都會進行更新,因此快取中的資料永遠是最新的。
-
寫入懲罰與讀取懲罰。
每次寫入都牽涉到兩個來回行程:
一個寫入快取
一個寫入資料庫
這會對程序增加延遲。也就是說,相較於擷取資料,最終使用者一般更能容忍更新資料時的延遲。固有的想法是更新需要的動作較多因此會花費較長時間。
全部寫入的缺點如下:
-
遺漏資料。
如果啟動新節點,無論是因為節點故障或水平擴展,都會發生資料未命中。這些資料會繼續處於未命中狀態,直到在資料庫上新增或更新為止。您可以藉由搭配全部寫入實作延遲載入來盡可能減少此情形。
-
快取流失。
大部分資料都永遠不會被讀取,導致資源的浪費。透過新增存留時間 (TTL) 值 ,您可以將浪費的空間降到最低。
全部寫入虛擬程式碼範例
下列是全部寫入邏輯的虛擬程式碼範例。
// *****************************************
// function that saves a customer's record.
// *****************************************
save_customer(customer_id, values)
customer_record = db.query("UPDATE Customers WHERE id = {0}", customer_id, values)
cache.set(customer_id, customer_record)
return success
在此範例中,取得資料的應用程式程式碼如下。
save_customer(12345,{"address":"123 Main"})
新增 TTL
延遲載入允許使用過時資料,但不會因空白節點而失敗。全部寫入可確保資料總是保持最新狀態,但可能因為空白節點而失敗,且可能對快取填入多餘的資料。透過為每個寫入新增存留時間 (TTL) 值,您可以擁有每個策略的優點。同時可以大幅避免將快取塞滿多餘的資料。
存留時間 (TTL) 是整數值,可指定金鑰過期前的秒數。Valkey 或 Redis OSS可以為此值指定秒或毫秒。Memcached 需以秒為單位指定此值。應用程式嘗試讀取過期的索引鍵時,系統會視為找不到索引鍵。資料庫會查詢索引鍵並更新快取。這種方法並不能保證值不會過時。不過會確保資料不致於過時太久,且需要偶爾從資料庫重新整理快取中的值。
如需詳細資訊,請參閱 Valkey 和 Redis OSS命令set
命令
TTL 虛擬程式碼範例
以下是具有 的寫入邏輯的虛擬程式碼範例TTL。
// *****************************************
// function that saves a customer's record.
// The TTL value of 300 means that the record expires
// 300 seconds (5 minutes) after the set command
// and future reads will have to query the database.
// *****************************************
save_customer(customer_id, values)
customer_record = db.query("UPDATE Customers WHERE id = {0}", customer_id, values)
cache.set(customer_id, customer_record, 300
)
return success
以下是使用 延遲載入邏輯的虛擬程式碼範例TTL。
// *****************************************
// function that returns a customer's record.
// Attempts to retrieve the record from the cache.
// If it is retrieved, the record is returned to the application.
// If the record is not retrieved from the cache, it is
// retrieved from the database,
// added to the cache, and
// returned to the application.
// The TTL value of 300 means that the record expires
// 300 seconds (5 minutes) after the set command
// and subsequent reads will have to query the database.
// *****************************************
get_customer(customer_id)
customer_record = cache.get(customer_id)
if (customer_record != null)
if (customer_record.TTL < 300)
return customer_record // return the record and exit function
// do this only if the record did not exist in the cache OR
// the TTL was >= 300, i.e., the record in the cache had expired.
customer_record = db.query("SELECT * FROM Customers WHERE id = {0}", customer_id)
cache.set(customer_id, customer_record, 300
) // update the cache
return customer_record // return the newly retrieved record and exit function
在此範例中,取得資料的應用程式程式碼如下。
save_customer(12345,{"address":"123 Main"})
customer_record = get_customer(12345)