1. 程式人生 > >Django快取框架詳解(官方文件翻譯來)

Django快取框架詳解(官方文件翻譯來)

首先看Django官網的介紹:

    動態網站的一個基本權衡就是他們是動態的,每次一個使用者請求一個頁面,web伺服器進行各種各樣的計算-從資料庫查詢到模板渲染到業務邏輯-從而生成站點訪問者看到的頁面。從處理開銷的角度來看,相比標準的從檔案系統讀取檔案的伺服器排程,這是昂貴了不少。儘管對於大多數網站來說,這種開銷不是什麼大問題,因為大多數web應用不過是想學學院的首頁那樣,都是小到中型的站點,流量也很少。但對於中到大型的站點來說,必須儘可能的減少開銷。這就是快取的由來。
    django自帶一個強大的快取系統,提供不同層次的快取粒度:你可以快取某個檢視函式的輸出,或者只是某個特別難生成的部分或者是整個站點。同時django也有類似“上流”快取的東西,類似於Squid和基於瀏覽器的快取,這類快取是你無法直接控制但是你可以通過一些提示(比如http頭部)指定你網站的那些部分需要和如何快取。

快取系統工作原理:

    對於一個給定的url,嘗試從快取中找到對應的url網址,檢查頁面是否存在快取中,如果存在,直接將快取中的頁面返回,如果快取中沒有,一系列操作(比如資料庫查詢)後,將內容頁面儲存到快取系統中以供下次使用,並將生成的頁面返回給前端。

設定快取:

    快取系統需要少量的配置才能使用,你必須告訴系統你的快取資料存放在哪裡-資料庫還是檔案系統亦或是直接存在快取中-這是一個重要的決定,直接影響到你的快取效能;當然,一些快取型別本來就是比其他的快,這我們無法迴避。

        在專案的setting.py裡面可以通過CACHES配置快取,django中可用的快取系統有Memcached、資料庫、檔案、本地記憶體,下面一一講解。


Memcached:

    Memcached是Django本地支援的最快速,最高效的快取記憶體型別,它是一個完全基於記憶體的快取伺服器,最初開發用於處理LiveJournal.com的高負載,隨後由Danga Interactive開源。 Facebook和維基百科等網站使用它來減少資料庫訪問並顯著提高網站效能。它用於動態Web應用以減輕資料庫負載從而顯著提供網站效能,也是django中到目前為止最有效率的可用快取。

    Memcached作為守護程序執行,並分配了指定數量的RAM。 它所做的就是提供一個快速介面來新增,檢索和刪除快取中的資料。 所有資料都直接儲存在記憶體中,因此不會產生資料庫或檔案系統使用的開銷。

    在安裝Memcached之後,您需要安裝Memcached繫結。 有幾個Python Memcached繫結可用; 最常見的兩個是python-memcached和pylibmc。

    在Django中使用Memcached:
 將BACKEND設定為django.core.cache.backends.memcached.MemcachedCache或django.core.cache.backends.memcached.PyLibMCCache(具體取決於您選擇的memcached繫結)
          將LOCATION設定為ip:port values,其中ip是Memcached守護程式的IP地址,port是執行Memcached的埠,或者是unix:路徑值,其中path是到達Memcached Unix套接字檔案的路徑。

    在本例中,Memcached使用python-memcached繫結在本地主機(127.0.0.1)埠11211上執行:

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
        'LOCATION': '127.0.0.1:11211',
    }
}

在本例中,Memcached通過使用python-memcached繫結的本地Unix套接字檔案/tmp/memcached.sock可用:

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
        'LOCATION': 'unix:/tmp/memcached.sock',
    }
}

Memcached的一個優秀功能是它能夠在多個伺服器上共享快取。 這意味著您可以在多臺機器上執行Memcached守護程序,並且程式會將這組機器視為單個快取,而無需在每臺機器上重複快取值。 要利用此功能,請將LOCATION中的所有伺服器地址包含在分號或以逗號分隔的字串中,或作為列表。

在此示例中,快取記憶體通過在IP地址172.19.26.240和172.19.26.242上執行的Memcached例項共享,這兩個例項都位於埠11211上:

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
        'LOCATION': [
            '172.19.26.240:11211',
            '172.19.26.242:11211',
        ]
    }
}

在以下示例中,快取是通過執行在IP地址172.19.26.240(埠11211),172.19.26.242(埠11212)和172.19.26.244(埠11213)上的Memcached例項共享的:

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
        'LOCATION': [
            '172.19.26.240:11211',
            '172.19.26.242:11212',
            '172.19.26.244:11213',
        ]
    }
}

        關於Memcached的最後一點是基於記憶體的快取有一個缺點:因為快取的資料儲存在記憶體中,所以如果伺服器崩潰,資料將會丟失。 很明顯,記憶體不適用於永久資料儲存,因此不要將基於記憶體的快取作為唯一的資料儲存。 毫無疑問,沒有一個Django快取後端應該用於永久儲存 - 它們都是為了快取而不是儲存的解決方案 - 但是我們在這裡指出了這一點,因為基於記憶體的快取特別是暫時的。

資料庫儲存:

  Django可以將其快取的資料儲存在資料庫中。 如果你有一個快速,索引良好的資料庫伺服器,這最好。
將資料庫表用作快取後端:
將BACKEND設定為django.core.cache.backends.db.DatabaseCache
將LOCATION設定為表名,即資料庫表的名稱。 這個名稱可以是任何你想要的,只要它是一個尚未在資料庫中使用的有效表名即可。在這個例子中,快取表的名字是my_cache_table:  
CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
        'LOCATION': 'my_cache_table',
    }
}

    在上面操作前,我們還沒建好快取表,所以首先我們應該建立好快取表,使用如下命令,注意cache_table_name不要和資料庫中已經存在的表名衝突:

python manage.py createcachetable [cache_table_name]  #這裡cache_table_name即為上面的my_cache_table
    這將在您的資料庫中建立一個表格,該表格採用Django的資料庫快取系統所需的適當格式。 該表的名稱取自LOCATION。
    如果您使用多個數據庫快取,createcachetable會為每個快取建立一個表。
    如果您使用多個數據庫,createcachetable會觀察資料庫路由器的allow_migrate()方法。
    像遷移一樣,createcachetable不會觸及現有的表。 它只會建立缺少的表格。

    要列印將要執行的SQL,而不是執行它,請使用createcachetable --dry-run選項。

檔案系統快取:

    基於檔案的後端將每個快取值序列化並存儲為單獨的檔案。 要使用此後端將BACKEND設定為“django.core.cache.backends.filebased.FileBasedCache”和LOCATION到合適的目錄。 例如,要將快取的資料儲存在/ var / tmp / django_cache中,請使用以下設定:

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
        'LOCATION': '/var/tmp/django_cache',
        #'LOCATION': 'c:/foo/bar',#windows下的示例
    }
}

注意:目錄路徑應該是絕對的 - 也就是說,它應該從檔案系統的根目錄開始。 在設定結束時是否放置斜線並不重要。必須保證伺服器對你列出的路徑具有讀寫許可權。

本地記憶體快取:

    如果你想具有記憶體快取的優點但有沒有能力執行Memcached的時候,你可以考慮本地記憶體快取,這個快取是多程序和執行緒安全的,後端設定為django.core.cache.backends.lovMemCache

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
        'LOCATION': 'unique-snowflake',
    }
}

    快取LOCATION用來區分每個記憶體儲存,如果你只有一個本地記憶體快取,你可以忽略這個設定;但如果你有多個的時候,你需要至少給他們中一個賦予名字以區分他們。快取使用最近最少使用(LRU)剔除策略。注意每個程序都有它們自己的私有快取例項,所以跨進陳快取是不可能的,因此,本地記憶體快取不是特別有效率的,建議你只是在內部開發測時使用,不建議在生產環境中使用。

虛擬快取:

這在實際應用中時非常有用的,假如你有一個產品用釋出的時候非常需要用到快取,但在開發和測試的時候你並不想使用快取,同時你也不想等到產品釋出的時候特別的去修改快取相關的程式碼,那麼你可以是用虛擬快取,要啟用虛擬快取,你只需按下面的例子去配置你的後端。

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
    }
}

自定義的快取:

雖然Django支援大量快取後端,但有時您可能需要使用自定義的快取後端。 要在Django中使用外部快取後端,請使用Python匯入路徑作為CACHES設定的BACKEND,如下所示:

CACHES = {
    'default': {
        'BACKEND': 'path.to.backend',
    }
}

快取引數:

每個快取後端可以被賦予額外的引數來控制快取行為。這些引數作為CACHES設定中的其他鍵提供。有效引數如下:
TIMEOUT:用於快取的預設超時時間(以秒為單位)。該引數預設為300秒(5分鐘)。您可以將TIMEOUT設定為無,以便預設情況下快取金鑰永不過期。值為0將導致金鑰立即過期(實際上“不快取”)。

OPTIONS:應該傳遞給快取後端的任何選項。有效選項的列表將隨每個後端而變化,而由第三方庫支援的快取後端將直接將其選項傳遞到基礎快取庫。
    實現自己的剔除策略(即,locmem,檔案系統和資料庫後端)的快取記憶體後端將遵循以下選項:
                MAX_ENTRIES:刪除舊值之前快取中允許的最大條目數。這個引數預設為300。
            CULL_FREQUENCY:達到MAX_ENTRIES時被剔除的條目的比例。實際比例為1 / CULL_FREQUENCY,因此在達到            MAX_ENTRIES時將CULL_FREQUENCY設定為2以剔除一半條目。這個引數應該是一個整數,預設為3。
對於CULL_FREQUENCY,值為0表示在達到MAX_ENTRIES時將轉儲整個快取。在某些後端(特別是資料庫),這會以更多的快取未命中為代價來快速剔除。
Memcached後端將OPTIONS的內容作為關鍵字引數傳遞給客戶端建構函式,允許更高階地控制客戶端行為。例如用法,見下文。

KEY_PREFIX:一個字串,將自動包含在Django伺服器使用的所有快取鍵中(預設預置)。

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
        'LOCATION': '/var/tmp/django_cache',
        'TIMEOUT': 60,
        'OPTIONS': {
            'MAX_ENTRIES': 1000
        }
    }
}

以下是一個基於python-memcached的後端物件大小限制為2MB的示例配置:

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
        'LOCATION': '127.0.0.1:11211',
        'OPTIONS': {
            'server_max_value_length': 1024 * 1024 * 2,
        }
    }
}

設定每個站點的快取:

        除了選擇和設定好前面的快取系統,還要做些其他額外的配置才行。

  快取設定完成後,使用快取的最簡單方法是快取整個網站。 您需要將'django.middleware.cache.UpdateCacheMiddleware'和'django.middleware.cache.FetchFromCacheMiddleware'新增到setting.py檔案中的MIDDLEWARE項設定中,如下例所示(設定中介軟體):

MIDDLEWARE = [
    'django.middleware.cache.UpdateCacheMiddleware',
    'django.middleware.common.CommonMiddleware'
     ........
    'django.middleware.cache.FetchFromCacheMiddleware',
]

    注意:UpdateCache中介軟體一定要放在第一位,Fetch中介軟體必須放最後(因為中介軟體的順序決定著執行的順序)

 然後,將以下必需設定新增到您的Django設定檔案中:
    CACHE_MIDDLEWARE_ALIAS ------------------用於儲存的快取記憶體別名。
    CACHE_MIDDLEWARE_SECONDS -------------每個頁面應該被快取的秒數,預設600。

    CACHE_MIDDLEWARE_KEY_PREFIX ----------如果使用相同的Django安裝跨多個站點共享快取,請將其設定為站 點 名稱或此Django例項唯一的其他字串,以防止發生重大沖突。 如果你不在乎,請使用空字串。如下例子:

CACHE_MIDDLEWARE_ALIAS = 'default'    #用來儲存的快取別名
CACHE_MIDDLEWARE_SECONDS = 0  #所有頁面預設快取時間,預設600
CACHE_MIDDLEWARE_KEY_PREFIX ='www.demo.com'  #關鍵的字首,當多個站點使用同一個配置的時候,這個可以設定可以避免發生衝突,一般設定為網站域名
CACHE_MIDDLEWARE_ANONYMOUS_ONLY = False #那麼只有匿名的請求會被快取,這是一個禁用快取非匿名使用者頁面的最簡單的做法,注意確保已經啟用了Django使用者認證中介軟體

每個檢視的快取:

    使用快取框架的更細化的方式是快取單個檢視的輸出。 django.views.decorators.cache定義了一個cache_page修飾器,它可以自動快取檢視的響應。 它很容易使用(請注意,為了便於閱讀,我們將其寫為60 * 15, 即15分鐘。還要記得要先匯入cache_page):

from django.views.decorators.cache import cache_page

@cache_page(60 * 15)
def my_view(request):
    ...
    cache_page(timeout, [cache=cache name], [key_prefix=key prefix])    cache_page只接受一個引數和兩個關鍵字引數,
  1.         timeout是快取時間,以秒為單位
  2.         cache:指定使用你的CACHES設定中的哪一個快取後端
  3.         key_prefix:指定快取字首,可以覆蓋在配置檔案中CACHE_MIDDLEWARE_KEY_PREFIX的值
    按照每個檢視的快取,就像每個站點的快取一樣,從URL中取消。 如果多個網址指向相同的檢視,則每個網址將分開快取。 繼續my_view示例,如果您的URLconf如下所示:
urlpatterns = [
    path('foo/<int:code>/', my_view),
]
    那麼請求/ foo / 1 /和/ foo / 23 /將分開快取,如您所料。 但是一旦請求了特定的URL(例如/ foo / 23 /),對該URL的後續請求將使用快取。
    cache_page還可以帶一個可選的關鍵字引數cache,當快取檢視結果時,它指示裝飾器使用特定的快取(來自CACHES設定)。 預設情況下,將使用預設快取,但您可以指定所需的任何快取:
@cache_page(60 * 15, cache="special_cache")
def my_view(request):
    ...
    也可以在每個檢視的基礎上覆蓋快取字首。 cache_page採用可選的關鍵字引數key_prefix,其工作方式與中介軟體的CACHE_MIDDLEWARE_KEY_PREFIX設定相同。 它可以像這樣使用:
@cache_page(60 * 15, key_prefix="site1")
def my_view(request):
    ...

在url配置檔案中指定每個檢視的快取:

    上一節中的示例對檢視進行了快取這一事實進行了硬編碼,因為cache_page會更改my_view函式。 這種方法將您的觀點與快取記憶體系統耦合在一起,由於多種原因,這並不理想。 例如,您可能希望在另一個無快取站點上重用檢視函式,或者您可能希望將檢視分發給可能希望在不快取的情況下使用它們的人員。 解決這些問題的方法是在URLconf中指定每個檢視快取,而不是在檢視函式本身旁邊。
    這樣做很簡單:只需在URLconf中引用檢視函式時,可以使用cache_page包裝檢視函式:
from django.views.decorators.cache import cache_page

urlpatterns = [
    path('foo/<int:code>/', cache_page(60 * 15)(my_view)),
]

模板片段快取:

    如果您在更多控制之後,還可以使用快取模板標記來快取模板片段。 要讓您的模板可以訪問此程式碼,請將{%load cache%}放在模板頂部附近。
{%cache%}模板標籤將塊的內容快取給定的時間量。 它至少需要兩個引數:以秒為單位的快取記憶體超時以及給出快取記憶體片段的名稱。 如果超時時間為無,片段將永久快取。 該名稱將被視為不使用變數。 例如:
{% load cache %}
{% cache 500 sidebar %}
    .. sidebar ..
{% endcache %}
    有時您可能想要快取片段的多個副本,具體取決於片段中出現的一些動態資料。 例如,您可能需要為您的網站的每個使用者使用上一個示例中使用的側欄的單獨快取副本。 通過向{%cache%}模板標籤傳遞一個或多個可能帶有或不帶過濾器的變數的附加引數來唯一標識快取片段:
{% load cache %}
{% cache 500 sidebar request.user.username %}
    .. sidebar for logged in user ..
{% endcache %}

底層快取API:

    有時候我們並不想快取整個檢視,只是想快取某個資料庫檢索的結果,事實上有時候快取整個渲染頁面並不會給你帶來太多好處。例如,也許您的網站包含一個檢視,其結果取決於幾個複雜的查詢,其結果會以不同的時間間隔進行更改。 在這種情況下,使用每個站點或每個檢視快取策略提供的整頁快取並不理想,因為您不希望快取整個結果(因為一些資料經常變化), 但你仍然想快取很少改變的結果。    對於這種情況,Django公開了一個簡單的底層快取API。 您可以使用此API將物件以任意級別的粒度儲存在快取中。 您可以快取任何可以安全序列化的Python物件:字串,字典,模型物件列表等。 
    訪問快取
        您可以通過類似dict的物件訪問在CACHES設定中配置的快取:django.core.cache.caches。 在同一執行緒中重複請求相同的別名將返回相同的物件。
>>> from django.core.cache import caches
>>> cache1 = caches['myalias']
>>> cache2 = caches['myalias']
>>> cache1 is cache2
True
    基本用法:
>>> cache.set('my_key', 'hello, world!', 30)
>>> cache.get('my_key')
'hello, world!'
鍵應該是一個str,並且值可以是任何可Python物件。
超時引數是可選的,預設為CACHES設定中相應後端的超時引數(如上所述)。 這是值應該儲存在快取中的秒數。 傳入無超時將永遠快取該值。 超時0不會快取該值。
如果該物件不存在於快取中,則cache.get()返回None:
>>> # Wait 30 seconds for 'my_key' to expire...
>>> cache.get('my_key')
None
只有在鍵不存在的情況下才能新增鍵,請使用add()方法。 它採用與set()相同的引數,但如果指定的鍵已經存在,它不會嘗試更新快取:
>>> cache.set('add_key', 'Initial value')
>>> cache.add('add_key', 'New value')
>>> cache.get('add_key')
'Initial value'
還有一個get_many()介面只能訪問一次快取。 get_many()返回一個字典,其中包含您要求的實際存在於快取中的所有金鑰(並且尚未過期):
>>> cache.set('a', 1)
>>> cache.set('b', 2)
>>> cache.set('c', 3)
>>> cache.get_many(['a', 'b', 'c'])
{'a': 1, 'b': 2, 'c': 3}
您可以使用delete()顯式刪除鍵。 這是清除特定物件快取的簡單方法:
>>> cache.delete('a')

快取關鍵子字首:

    快取鍵字首
        如果您在伺服器之間或生產環境與開發環境之間共享快取例項,則可能由另一臺伺服器使用一臺伺服器快取的資料。 如果伺服器之間快取資料的格式不同,這可能會導致一些非常難以診斷的問題。
        為了防止出現這種情況,Django提供了為伺服器使用的所有快取金鑰加字首的功能。 當儲存或檢索特定的快取鍵時,Django會自動為快取鍵新增KEY_PREFIX快取設定的值。
        通過確保每個Django例項具有不同的KEY_PREFIX,可以確保快取值中不會發生衝突。

“上流”快取:

    到目前為止,這個文件專注於快取你自己的資料,即服務端的快取。但另一種型別的快取也與Web開發密切相關:由“上流”快取執行的快取,這些系統甚至在請求到達您的網站之前就可以為使用者快取頁面。即讓在客戶端上實現快取。

下面是一些上流快取的例子:
  1. 你的ISP(因特網提供商)可能會快取一些頁面,比如你訪問   http://example.com/ 的時候,你的ISP可能沒有訪問到 example.com就把頁面返回給你了 
  2. 你的瀏覽器可能也會快取一些網頁
    上游快取是一個很好的效率提高,但不適合快取所有的網頁,特別是一些跟認證有關的頁面的快取可能會給網站帶來一些隱患,那該如何決定是否在上游快取呢?    幸運的是,HTTP為這個問題提供了一個解決方案。存在許多HTTP標頭以指示下游快取記憶體根據指定的變數來區分它們的快取記憶體內容,並且指示快取記憶體機構不快取記憶體特定頁面。我們將在後面的章節中檢視一些這些標題。

控制快取 使用其他頭部資訊:

        快取的其他問題是資料的隱私性以及資料應該儲存在快取級聯中的問題。
使用者通常面對兩種快取:他們自己的瀏覽器快取(私有快取)和他們的提供者快取(公共快取)。 公用快取由多個使用者使用並由其他人控制。 這會導致敏感資料出現問題 - 您不希望將您的銀行帳號儲存在公共快取中。 因此,Web應用程式需要一種方法來告訴快取哪些資料是私有的,哪些是公共的。
        解決方案是指示頁面的快取應該是“私人的”。要在Django中執行此操作,請使用cache_control()檢視修飾器。 例:
from django.views.decorators.cache import cache_control

@cache_control(private=True)
def my_view(request):
    ...
    這個裝飾器負責在幕後傳送適當的HTTP頭。
    請注意的是,快取控制設定“private”和“public”是互斥的。 如果設定“private”,裝飾器確保“public”指令被刪除(反之亦然)。 這兩個指令的一個例子是一個提供私人和公共條目的部落格網站。 公共條目可以快取在任何共享快取上。 以下程式碼使用patch_cache_control(),這是手動修改快取控制頭的方法(由cache_control()裝飾器內部呼叫):
from django.views.decorators.cache import patch_cache_control
from django.views.decorators.vary import vary_on_cookie

@vary_on_cookie
def list_blog_entries_view(request):
    if request.user.is_anonymous:
        response = render_only_public_entries()
        patch_cache_control(response, public=True)
    else:
        response = render_private_and_public_entries(request.user)
        patch_cache_control(response, private=True)

    return response
    您也可以用其他方式控制下游快取(有關HTTP快取的詳細資訊,請參閱RFC 7234)。 例如,即使您不使用Django的伺服器端快取框架,仍然可以通過max-age指令告訴客戶端快取檢視一段時間:
from django.views.decorators.cache import cache_control

@cache_control(max_age=3600)
def my_view(request):
    下面列舉cache_control接收到額引數:
  • public=True
  • private=True
  • no_cache=True
  • no_transform=True
  • must_revalidate=True
  • proxy_revalidate=True
  • max_age=num_seconds
  • s_maxage=num_seconds

MIDDLEWARE的順序:

    UpdateCacheMiddleware在響應階段執行,中介軟體以相反的順序執行,所以列表頂部的專案在響應階段最後執行。因此,您需要確保UpdateCacheMiddleware出現在可能會將某些內容新增到Vary標頭的任何其他中介軟體之前。以下中介軟體模組這樣做:
  • SessionMiddleware新增Cookie
  • GZipMiddleware添加了Accept-Encoding
  • LocaleMiddleware添加了Accept-Language
    另一方面,FetchFromCacheMiddleware在請求階段執行,中介軟體首先應用於中介軟體,因此列表頂部的專案在請求階段首先執行。 FetchFromCacheMiddleware還需要在其他中介軟體更新Vary頭後執行,因此FetchFromCacheMiddleware必須位於任何專案之後。