標籤:

Python 緩存框架 Tache 使用介紹

介紹

Tache 是一個 Python 的緩存框架。它基於如下的目標而設計:

  • 同時支持 Python2 和 Python3
  • 支持緩存普通函數/實例方法/類方法/靜態方法
  • 支持 Batch 批量緩存
  • 支持基於 Tag 的緩存和失效
  • 支持基於參數顯式聲明 key 格式

前兩個目標是必須的,後面三個目標是我們設計 Tache 的初衷。

Tag 的使用

案例一: 變更批量失效。當一個對象變更時,需要失效所有與該改對象相關的查詢。

最初對象相關的查詢可能比較少,隨著業務的增長後來的查詢條件越來越多,對象欄位越來越多,甚至變成了一個聚合對象,內部各欄位甚至都不來自一個存儲,一個服務,每處都加上了緩存。這時本來只是一處的變更,要失效的代碼散落在各地很容易出漏。怎麼辦呢,根據對象給緩存打上同樣的標籤,失效時只需失效一處。

假設有如下的代碼 (以下代碼均為演示而設計,勿對號入座):

@cache(tags=["comment:{0}"])def get_comment(comment_id): ...@cache(tags=["comment:{0}"])def get_comment_status_by_id(comment_id): ... @cache(tag=["comment:{0}"])def get_comment_likes_by_id(comment_id): ... @cache(tag=["comment:{0}", "member:{1}"])def get_replied_comment_by_member(comment_id, member_id):

那麼最後實際需要失效的代碼還需要多少呢?只需一行:

cache.invalidate_tag("comment:{0}".format(comment_id))

案例二: 分頁操作。針對如下的分頁操作,因為 offset, limit 的組合太多,如果對上面的操作整體緩存,如何保證失效時全部失效?

def get_comments(object_type, object_id, status, offset, limit):...

一樣可以用 tag 來控制:

@cache(tags=["comment:{0}:{1}"])def get_comments(object_type, object_id, status, offset, limit): comments = comment_dao.get_comments(..., offset, limit) results = [] for c in comments: results.append(format_comment(c)) return results

這樣當增加和刪除評論時,只要失效 tag, 所有的 offset/limit 查詢緩存全部都失效了。等等,你這樣有另一個問題,會不會緩存失效粒度過大,造成資料庫大量查詢?

很遺憾,確實會這樣。基於此,我們將基於 Batch 批量緩存來優化這個分頁操作。

Batch 的使用

Batch 可以同時對列表中多個元素進行批量緩存,當失效列表中某一元素時不會影響被緩存的其他元素。當列表中有緩存和未緩存的數據都存在時,僅會對未緩存的數據進行查詢。如:

list_objects(1,2,3,4,5) # no cache, 調用完畢全部一次緩存list_objects(3,4,5,6,7) # 3,4,5 從緩存中取,6,7 在調用完畢一次緩存

接著針對上面的分頁操作,我們把它分為兩步:

1. 分頁取 id 列表

2. 用 id 列表批量獲取實體 id 列表

以下是 get_comments 的重構:

@cache(tags=["comment:{0}:{1}"])def get_comments(object_type, object_id, status, offset, limit): comment_ids = comment_dao.get_comment_ids(..., offset, limit) results = get_comments_byids(*comment_ids) return results @batch()def get_comments_byids(*comment_ids): results = [] for cid in comment_ids: comment = comment_dao.get_comment(cid) results.append(format_comment(comment)) return results def add_comment(): ... cache.invalidate_tag("comment:{0}:{1}".format(obtype, obid))

這樣在失效緩存時,我們失效的只是用 offset, limit 取 id 列表這個查詢, 從 id 列表取出實體都還是被緩存的。

顯式聲明 Key

開源的 Python 緩存庫通常生成緩存 key 的規則都是基於模塊名、類名、方法名等的組合自動生成。作為一個有追求的程序員,重構是經常要做的事。但是重構過程中,如果改了函數名、方法名,或者移動了模塊位置,經常會造成緩存失效,對於流量比較大的介面這是比較危險的事情。

Tache 允許你顯式聲明 Key 的生成規則, 這樣不論代碼如何重構生成的 key 都不會改變。

class B: def __init__(self): self.count = 0 @cache.cached("counter.B.add|{0}-{1}") def add(self, a, b): self.count += 1 return a + b + self.count

只要不改 key 的規則,不論代碼如何重構,上述生成的 key 都會形如 counter.B.add|{0}-{1}.format(a, b)

最後

  • 基於 tag 的緩存是個殺器,但要謹慎使用,使用場景推薦用於粗粒度的聚合介面,不建議用來緩存請求量大的細粒度介面。因為每次讀取時會多取一次 tag, 因此對介面緩存的請求量會放大一倍
  • 更詳細的使用說明參見 Tache 的項目文檔。

推薦閱讀:

python下使用selenium怎麼才能控制瀏覽器載入某個元素?
python爬取github數據
theano function調試有什麼好方法?
第十章 Scrapy的架構初探

TAG:Python | 缓存 |