SqlAlchemy做數據庫緩存 -- dogpile.cache

dogpile是一種控制結構,它允許選擇單個執行線程作爲某些資源的“創建者”,同時允許其他執行線程在創建過程中引用此資源的先前版本。

dogpile.cache是​​一個緩存API,它提供了一個通用接口來緩存任何種類的後端。

首先安裝dogpile:pip install dogpile.cache
現在有很多有名的key/value服務比如說redis Memcached,他們都很適合用於緩存,特別是Memcached。考慮到緩存系統,dogpile.cache提供了針對該系統的特定Python API的接口。

dogpile.cache配置包含以下組件:

  1. region: 是一個CacheRegion的實例,可以被看做是front end
  2. backend: 是一個CacheBackend的實例,描述了數據是怎麼被儲存的。這個接口只提供了get(),set(),delete()。用於特定CacheRegion的實際CacheBackend類型由用於與緩存通信的底層API,比如pylibmc(Memcached的python客戶端)確定。
  3. Value generation functions: 這些是用戶定義的函數,用於生成要放入緩存的數據。雖然godpile.cache提供了將數據放入緩存的常用set方法,但是通常使用方法是get。傳遞一個創建函數,當且僅當需要時,它將用於生成新值,這種“get-or-create”模式是“Dogpile”系統的全部關鍵。

初步使用:
dogpile.cache包含一個Pylibmc後端

from dogpile.cache import make_region

# 這裏創建了一個CacheRegion實例對象
Region = make_region()
# 對CacheRegion對象配置了backend
region = Region.configure(
    'dogpile.cache.pylibmc',  # 這是name of backend,是需要的唯一arguement
    expiration_time = 3600,
    arguments = {
        'url': ["127.0.0.1"],  # 這裏是傳遞Memcached server的URL
    }
)

@region.cache_on_arguments()
def load_user_info(user_id):
    return some_database.lookup_user_by_id(user_id)

所以最後都是使用region作爲裝飾器


make_region()

make_region()函數會直接調用CacheRegion的結構
該函數有以下參數:

  • name: 可選,region的名字,可以用.name獲取,在config file中配置region時會有用
  • function_key_generator: 可選,用於使用CacheRegion.cache_on_arguments() 方法,這個函數需要有兩個level。 一個是給出data creation function,然後需要返回一個新function用於基於給定的arguement生成key。
    def my_key_generator(namespace, fn, **kw):
        fname = fn.__name__
        def generate_key(*arg):
            return namespace + "_" + fname + "_".join(str(s) for s in arg)
        return generate_key
    
    
    region = make_region(
        function_key_generator = my_key_generator
    ).configure(
        "dogpile.cache.dbm", # python的database manager,python本身有很多dbm庫,但是dbm是一個方便選擇,不用選擇特定的dbm模塊,它可以自己選擇最適合的
        expiration_time=300,
        arguments={
            "filename":"file.dbm"
        }
    )
    
    使用裝飾器:
    @my_region.cache_on_arguments(namespace=('x', 'y'))
    def my_function(a, b, **kw):
        return my_data()
    
  • function_multi_key_generator:可選,用於使用CacheRegion.cache_multi_on_arguments()方法,Generated function會返回一個key列表。
    def my_multi_key_generator(namespace, fn, **kw):
    namespace = fn.__name__ + (namespace or '')
    
    def generate_keys(*args):
        return [namespace + ':' + str(a) for a in args]
    
    return generate_keys
    
  • key_mangler: 可選,比較典型的是SHA1,sha1_mangle_key() 方法會對所有的key做hash
  • async_creation_runner: 它將通過互斥鎖並在完成時負責釋放該互斥鎖
    import threading
    def async_creation_runner(cache, somekey, creator, mutex):
        def runner():
            try:
                value = creator()
                cache.set(somekey, value)
            finally:
                mutex.release()
    
        thread = threading.Thread(target=runner)
        thread.start()
    
    
    region = make_region(
        async_creation_runner=async_creation_runner,
    ).configure(
        'dogpile.cache.memcached',
        expiration_time=5,
        arguments={
            'url': '127.0.0.1:11211',
            'distributed_lock': True,
        }
    )
    


CacheRegion.configure()

根據上面一步,當我們有了CacheRegion的時候,CacheRegion.cache_on_arguments() 方法就可以用來當做裝飾器了,但是這個緩存現在還不能用,必須經過CacheRegion.configure()配置了backend等信息才能使用

CacheRegion.configure() 有以下幾個參數:

  • backend: CacheBackend的名字,必須項
  • expiration_time: 在上次value creation function調用之後,經過這段時間,纔會調用下一次value creation function
  • arguments: 指定backend的地址
  • wrap: ProxyBackend類和/或實例的列表,每個類和/或實例用於包裝原始backend,以便可以應用自定義功能擴充。
  • replace_existing_backend: 設置爲True的話,已經存在的cache backend會被替換,如果不設置,對存在的backend進行配置會報錯。
  • region_invalidator: 重寫默認的無效策略
  • CacheRegion.configure_from_config():使用configuration dictionary和裏面的前綴批量配置backend:
    local_region = make_region()
    memcached_region = make_region()
    # 上面CacheRegion已經創建好,但是還沒有配置
    myconfig = {
        "cache.local.backend":"dogpile.cache.dbm",
        "cache.local.arguments.filename":"/path/to/dbmfile.dbm",
        "cache.memcached.backend":"dogpile.cache.pylibmc",
        "cache.memcached.arguments.url":"127.0.0.1, 10.0.0.1",
    }
    local_region.configure_from_config(myconfig, "cache.local.")
    memcached_region.configure_from_config(myconfig,"cache.memcached.")
    


如何使用Region

上面對CacheRegion該配置的已經配置好了,這玩意兒相當於我們訪問cache的前端接口,它包含下面的方法:

首先介紹invalidate()方法:
默認的invalidation system就是爲value設置當前時間爲最小的創建時間,任何檢索值的創建時間早於這個timestamp就會被判定爲過期,它不會對緩存中的值產生影響。這個timestamp被進程儲存在本地內存,所以不會影響其他進程。但是如果這個timestamp被儲存在緩存中,各進程都會被同一個timestamp影響,那麼就需要制定一個custom RegionInvalidationStrategy.

這個方法可以有hard和soft兩個選項,如果是hard,那麼CacheRegion.get_or_create()會讓所有的getters都等待value重新生成,如果是soft,那麼隨後的getters會獲取old value直到新的value生成。


CacheRegion.get(key, expiration_time=None, ignore_expiration=False)

key: 根據key從Cache裏面獲取value,如果獲取不到,該方法就會返回一個token:NO_VALUE。

expiration_time: 這個過期時間,要麼是在region配置的時候設定好了,要麼是現在調用的時候傳遞的參數,它會比較檢索值的創建時間和現在時間(time.time()),如果過期了,這個緩存值會被忽略並且返回NO_VALUE。

ignore_expiration: 如果我們這樣設置參數ignore_expiration=True, 那麼會繞過這個過期時間檢測


CacheRegion.get_or_create(key, creator, expiration_time=None, should_cache_fn=None, creator_args=None)

根據給定的key在緩存中獲取對應的value,如果這個值不存在或者過期了,那麼給定的creation function可能會創建新的值並且保存。

併發鎖:
是否能使用這個creation function取決於是否能獲取這個dogpile lock,如果不能,證明此時正有另一個線程或者進程在使用creation function爲這個key創建值。
當這個dogpile不能獲取的時候:
如果previous value不存在,那麼這個方法就會block並且等待直到這個鎖被釋放,並且有一個新的值available。
如果previous value存在,那麼會直接被返回。

如果invalidate()方法被called,如果檢索值的timestamp比invalidation timestamp老,那麼該值不管如何都不會被返回,該方法會嘗試獲取dogpile lock去生成新的值,或者等待lock釋放然後返回新的值

key:檢索的key
creator:用於創建新的值的函數
creator_args: 向創建函數傳遞參數 tuple
expiration_time: 會重寫配置region時設置的過期時間,如果不需要過期時間,設置爲-1
should_cache_fn:這個方法會接受creation function的返回值,然後根據返回True或者False判斷新生成的值是否需要被保存到cache中:

def dont_cache_none(value):
    return value is not None
    # 如果這裏返回False,那麼該值不會被緩存

value = region.get_or_create("some key",
                    create_value,  # 這是creation function
                    should_cache_fn=dont_cache_none)

CacheRegion.set(key, value)

在緩存中爲特定的key設置value


CacheRegion.delete(key)

緩存中刪除特定的鍵值對,該操作冪等


CacheRegion.cache_on_arguments(namespace=None, expiration_time=None, should_cache_fn=None, to_str=<class ‘str’>, function_key_generator=None)

這是一個裝飾器函數將creation function創建的鍵值對儲存到緩存中,這個裝飾器會內在的調用CacheRegion.get_or_create()方法去獲取緩存然後看是否需要調用該函數。

@someregion.cache_on_arguments()
def generate_something(x, y):
    return somedatabase.query(x, y)

這個被裝飾的方法可以現在被調用了,數據會從cache裏面獲取,直到新的value被需要
result = generate_something(5, 6)

這個方法有一個屬性爲invalidate(),可以驗證value的timestamp:
generate_something.invalidate(5, 6)

這個方法還可以直接使用set(),這樣會直接儲存這個給定的value,而不需要調用這個被裝飾的函數:
generate_something.set(3, 5, 6)

這個方法有屬性refresh(),該方法會調用被裝飾函數去產生新的value放入緩存:
newvalue = generate_something.refresh(5, 6)

這個方法有屬性original(),它會調用被裝飾函數而不是獲取緩存中的數據:
newvalue = generate_something.original(5, 6)

這個方法有屬性get(),它會返回緩存中的值,或者返回NO_VALUE:
value = generate_something.get(5, 6)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章