Django的緩存框架

2021-10-19 19:32 更新

Django的緩存框架

動態(tài)網(wǎng)站的基本權(quán)衡是動態(tài)的。每次用戶請求頁面時,Web服務(wù)器都會進行各種計算-從數(shù)據(jù)庫查詢到模板呈現(xiàn)再到業(yè)務(wù)邏輯-創(chuàng)建站點訪問者可以看到的頁面。從處理開銷的角度來看,這比標準的從文件中讀取文件的服務(wù)器系統(tǒng)要貴得多。

對于大多數(shù)Web應(yīng)用程序而言,此開銷并不大。大多數(shù)Web應(yīng)用程序不是washingtonpost.com或slashdot.org; 它們是流量中等的中小型網(wǎng)站。但是對于中到高流量的站點,必須盡可能減少開銷。

那就是緩存的來源。

緩存某些內(nèi)容是為了保存昂貴的計算結(jié)果,因此您下次不必執(zhí)行計算。以下是一些偽代碼,用于說明如何將其應(yīng)用于動態(tài)生成的網(wǎng)頁:

given a URL, try finding that page in the cache
if the page is in the cache:
    return the cached page
else:
    generate the page
    save the generated page in the cache (for next time)
    return the generated page

Django帶有一個健壯的緩存系統(tǒng),可讓您保存動態(tài)頁面,因此不必為每個請求都計算它們。為了方便起見,Django提供了不同級別的緩存粒度:您可以緩存特定視圖的輸出,可以僅緩存難以生成的片段,或者可以緩存整個站點。

Django還可以與“下游”緩存(例如Squid和基于瀏覽器的緩存)配合使用。這些是您不直接控制的緩存類型,但是您可以向它們提供提示(通過HTTP標頭)有關(guān)站點的哪些部分以及應(yīng)該如何緩存的提示。

也可以看看

該緩存框架的設(shè)計理念, 解釋了一些框架的設(shè)計決策。

設(shè)置緩存

緩存系統(tǒng)需要少量設(shè)置。即,您必須告訴它緩存的數(shù)據(jù)應(yīng)該存放在哪里–無論是在數(shù)據(jù)庫中,在文件系統(tǒng)上還是直接在內(nèi)存中。這是一個影響緩存性能的重要決定。是的,某些緩存類型比其他類型更快。

您的緩存首選項進入CACHES設(shè)置文件中的設(shè)置。以下是的所有可用值的說明 CACHES。

Memcached

Memcached是Django原生支持的最快,最高效的緩存類型, 是一種完全基于內(nèi)存的緩存服務(wù)器,最初是為處理LiveJournal.com上的高負載而開發(fā)的,隨后由Danga Interactive開源。Facebook和Wikipedia等網(wǎng)站使用它來減少數(shù)據(jù)庫訪問并顯著提高網(wǎng)站性能。

Memcached作為守護程序運行,并分配了指定數(shù)量的RAM。它所做的只是提供一個用于添加,檢索和刪除緩存中數(shù)據(jù)的快速接口。所有數(shù)據(jù)都直接存儲在內(nèi)存中,因此沒有數(shù)據(jù)庫或文件系統(tǒng)使用的開銷。

本身安裝Memcached后,您需要安裝Memcached綁定。有幾種可用的Python Memcached綁定。兩種最常見的是python-memcached和pylibmc。

要將Memcached與Django結(jié)合使用,請執(zhí)行以下操作:

  • 設(shè)置BACKEND為 django.core.cache.backends.memcached.MemcachedCache或 django.core.cache.backends.memcached.PyLibMCCache(取決于您選擇的內(nèi)存緩存綁定)
  • 設(shè)置LOCATION為ip:port值,其中ip是Memcached守護程序的IP地址,port是運行Memcached的端口,或者設(shè)置為unix:path值,其中 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',
    }
}

在此示例中,可以/tmp/memcached.sock使用python-memcached綁定通過本地Unix套接字文件使用Memcached :

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

使用pylibmc綁定時,請勿包括unix:/前綴:

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

Memcached的一項出色功能是能夠在多個服務(wù)器上共享緩存。這意味著您可以在多臺計算機上運行Memcached守護程序,并且該程序會將計算機組視為單個 緩存,而無需在每臺計算機上重復緩存值。要利用此功能,請將所有服務(wù)器地址包含在中 LOCATION,以分號或逗號分隔的字符串或列表的形式。

在此示例中,緩存在IP地址為172.19.26.240和172.19.26.242且均在端口11211上運行的Memcached實例之間共享:

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.42(端口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',
        ]
    }
}

關(guān)于Memcached的最后一點是基于內(nèi)存的緩存有一個缺點:由于緩存的數(shù)據(jù)存儲在內(nèi)存中,因此如果服務(wù)器崩潰,數(shù)據(jù)將丟失。顯然,內(nèi)存不是用于永久性數(shù)據(jù)存儲的,因此不要依賴基于內(nèi)存的緩存作為唯一的數(shù)據(jù)存儲。毫無疑問,任何 Django緩存后端都不應(yīng)該用于永久存儲-它們都旨在作為緩存而非存儲的解決方案-但我們在此指出這一點是因為基于內(nèi)存的緩存特別臨時。

數(shù)據(jù)庫高速緩存

Django可以將其緩存的數(shù)據(jù)存儲在您的數(shù)據(jù)庫中。如果您擁有快速索引良好的數(shù)據(jù)庫服務(wù)器,則此方法效果最佳。

要將數(shù)據(jù)庫表用作緩存后端:

  • 設(shè)置BACKEND于 django.core.cache.backends.db.DatabaseCache
  • 設(shè)置LOCATION為tablename,數(shù)據(jù)庫表的名稱。該名稱可以是您想要的任何名稱,只要它是數(shù)據(jù)庫中尚未使用的有效表名即可。

在此示例中,緩存表的名稱為my_cache_table:

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
        'LOCATION': 'my_cache_table',
    }
}

創(chuàng)建緩存表

在使用數(shù)據(jù)庫緩存之前,必須使用以下命令創(chuàng)建緩存表:

python manage.py createcachetable

這會在您的數(shù)據(jù)庫中創(chuàng)建一個表,該表的格式與Django的數(shù)據(jù)庫緩存系統(tǒng)期望的格式相同。該表的名稱取自 LOCATION。

如果使用多個數(shù)據(jù)庫緩存,請createcachetable為每個緩存創(chuàng)建一個表。

如果您使用多個數(shù)據(jù)庫,請createcachetable遵循allow_migrate()數(shù)據(jù)庫路由器的 方法(請參見下文)。

像一樣migrate,createcachetable不會觸摸現(xiàn)有表格。它只會創(chuàng)建丟失的表。

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

多個數(shù)據(jù)庫

如果將數(shù)據(jù)庫緩存與多個數(shù)據(jù)庫一起使用,則還需要為數(shù)據(jù)庫緩存表設(shè)置路由說明。為了進行路由,數(shù)據(jù)庫高速緩存表CacheEntry在名為的應(yīng)用程序中顯示為名為的模型 django_cache。該模型不會出現(xiàn)在模型緩存中,但是可以將模型詳細信息用于路由目的。

例如,以下路由器會將所有緩存讀取操作定向到cache_replica,并將所有寫入操作定向到 cache_primary。緩存表將僅同步到 cache_primary:

class CacheRouter:
    """A router to control all database cache operations"""

    def db_for_read(self, model, **hints):
        "All cache read operations go to the replica"
        if model._meta.app_label == 'django_cache':
            return 'cache_replica'
        return None

    def db_for_write(self, model, **hints):
        "All cache write operations go to primary"
        if model._meta.app_label == 'django_cache':
            return 'cache_primary'
        return None

    def allow_migrate(self, db, app_label, model_name=None, **hints):
        "Only install the cache model on primary"
        if app_label == 'django_cache':
            return db == 'cache_primary'
        return None

如果您沒有為數(shù)據(jù)庫緩存模型指定路由方向,則緩存后端將使用default數(shù)據(jù)庫。

當然,如果您不使用數(shù)據(jù)庫緩存后端,則無需擔心為數(shù)據(jù)庫緩存模型提供路由說明。

文件系統(tǒng)緩存

基于文件的后端將每個緩存值序列化并存儲為單獨的文件。要將此后端設(shè)置BACKEND為"django.core.cache.backends.filebased.FileBasedCache"并 設(shè)置到 LOCATION合適的目錄。例如,要在中存儲緩存的數(shù)據(jù)/var/tmp/django_cache,請使用以下設(shè)置:

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

如果您使用的是Windows,請將驅(qū)動器號放在路徑的開頭,如下所示:

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
        'LOCATION': 'c:/foo/bar',
    }
}

目錄路徑應(yīng)該是絕對的-也就是說,它應(yīng)該從文件系統(tǒng)的根目錄開始。是否在設(shè)置的末尾加斜杠都沒關(guān)系。

確保此設(shè)置指向的目錄存在,并且Web服務(wù)器在其下運行的系統(tǒng)用戶可以讀寫。繼續(xù)上面的示例,如果您的服務(wù)器以用戶身份運行apache,請確保該目錄/var/tmp/django_cache存在并且可由用戶讀取和寫入apache。

本地內(nèi)存緩存

如果未在設(shè)置文件中指定其他緩存,則這是默認緩存。如果您想要內(nèi)存中緩存的速度優(yōu)勢,但又不具備運行Memcached的功能,請考慮使用本地內(nèi)存緩存后端。該緩存是按進程(請參閱下文)并且是線程安全的。要使用它,請設(shè)置BACKEND為"django.core.cache.backends.locmem.LocMemCache"。例如:

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

高速緩存LOCATION用于標識各個內(nèi)存存儲。如果只有一個locmem緩存,則可以省略 LOCATION; 但是,如果您有多個本地內(nèi)存緩存,則需要至少為其分配一個名稱,以使它們分開。

緩存使用最近最少使用(LRU)淘汰策略。

請注意,每個進程都有其自己的專用緩存實例,這意味著不可能進行跨進程緩存。這顯然也意味著本地內(nèi)存緩存不是特別有效的內(nèi)存,因此對于生產(chǎn)環(huán)境而言,它可能不是一個好選擇。這對開發(fā)很好。

虛擬緩存(用于開發(fā))

最后,Django附帶了一個“虛擬”緩存,該緩存實際上并沒有緩存-它只是實現(xiàn)了緩存接口而無所事事。

如果您的生產(chǎn)站點在各個地方都使用了重型緩存,但是在開發(fā)/測試環(huán)境中卻不想緩存并且不想將代碼更改為后者的特殊情況,這將非常有用。要激活虛擬緩存,設(shè)置BACKEND如下:

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

使用自定義緩存后端

盡管Django開箱即用地支持許多緩存后端,但有時您可能希望使用自定義的緩存后端。要使用Django的外部緩存后端,使用Python導入路徑作為 BACKEND該的CACHES設(shè)置,如下所示:

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

如果要構(gòu)建自己的后端,則可以將標準緩存后端用作參考實現(xiàn)。您將django/core/cache/backends/在Django源代碼的目錄中找到代碼 。

注意:如果沒有真正令人信服的理由,例如不支持它們的主機,則應(yīng)堅持使用Django隨附的緩存后端。他們已經(jīng)過充分的測試并且有據(jù)可查。

緩存參數(shù)

可以為每個緩存后端提供其他參數(shù)來控制緩存行為。這些參數(shù)作為設(shè)置中的其他鍵提供 CACHES。有效參數(shù)如下:

  • TIMEOUT:用于緩存的默認超時(以秒為單位)。此參數(shù)默認為300秒(5分鐘)。您可以設(shè)置TIMEOUT為None默認情況下,緩存鍵永不過期。值的值0使鍵立即過期(有效地是“不緩存”)。
  • OPTIONS:應(yīng)傳遞到緩存后端的所有選項。有效選項的列表將隨每個后端而有所不同,并且由第三方庫支持的緩存后端會將其選項直接傳遞給基礎(chǔ)緩存庫。實現(xiàn)自己的撲殺戰(zhàn)略(即緩存后端locmem,filesystem以及database后端)將履行下列選項:MAX_ENTRIES:刪除舊值之前,緩存中允許的最大條目數(shù)。此參數(shù)默認為300。CULL_FREQUENCY:MAX_ENTRIES到達時被剔除的條目分數(shù)。實際比例為 ,因此設(shè)置為在達到時剔除一半條目。此參數(shù)應(yīng)為整數(shù),默認為。1 / CULL_FREQUENCYCULL_FREQUENCY2MAX_ENTRIES3值0for CULL_FREQUENCY表示MAX_ENTRIES到達時將轉(zhuǎn)儲整個緩存。在一些后端(database尤其是)這使得撲殺多 以更高速緩存未命中的代價更快。Memcached后端將OPTIONS as關(guān)鍵字參數(shù)的內(nèi)容傳遞給客戶端構(gòu)造函數(shù),從而可以更高級地控制客戶端行為。有關(guān)用法示例,請參見下文。
  • KEY_PREFIX:一個字符串,它將自動包含在Django服務(wù)器使用的所有緩存鍵中(默認為前綴)。有關(guān)更多信息,請參見緩存文檔。
  • VERSION:Django服務(wù)器生成的緩存鍵的默認版本號。有關(guān)更多信息,請參見緩存文檔。
  • KEY_FUNCTION 一個字符串,其中包含指向函數(shù)的虛線路徑,該函數(shù)定義了如何將前綴,版本和鍵組成最終的緩存鍵。有關(guān) 更多信息,請參見緩存文檔。

在此示例中,文件系統(tǒng)后端的超時時間為60秒,最大容量為1000:

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,
        }
    }
}

這是pylibmc基于基礎(chǔ)的后端的示例配置,該配置啟用了二進制協(xié)議,SASL身份驗證和ketama行為模式:

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
        'LOCATION': '127.0.0.1:11211',
        'OPTIONS': {
            'binary': True,
            'username': 'user',
            'password': 'pass',
            'behaviors': {
                'ketama': True,
            }
        }
    }
}

每個站點的緩存

一旦設(shè)置了緩存,使用緩存的最簡單方法就是緩存整個站點。您需要將'django.middleware.cache.UpdateCacheMiddleware'和 添加 'django.middleware.cache.FetchFromCacheMiddleware'到 MIDDLEWARE設(shè)置中,如以下示例所示:

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

注意

不,這不是輸入錯誤:“更新”中間件必須位于列表的第一位,“獲取”中間件必須位于最后。細節(jié)有些晦澀,但是如果您想了解完整的故事,請參閱下面的MIDDLEWARE順序。

然后,將以下必需設(shè)置添加到Django設(shè)置文件中:

  • CACHE_MIDDLEWARE_ALIAS –用于存儲的緩存別名。
  • CACHE_MIDDLEWARE_SECONDS –每個頁面應(yīng)緩存的秒數(shù)。
  • CACHE_MIDDLEWARE_KEY_PREFIX–如果使用同一Django安裝在多個站點之間共享緩存,請將其設(shè)置為站點名稱或該Django實例唯一的其他字符串,以防止按鍵沖突。如果您不在乎,請使用空字符串。

FetchFromCacheMiddleware緩存狀態(tài)為200的GET和HEAD響應(yīng),其中請求和響應(yīng)標頭允許。對具有不同查詢參數(shù)的相同URL請求的響應(yīng)被視為唯一頁面,并分別進行緩存。該中間件期望用與相應(yīng)的GET請求相同的響應(yīng)頭來應(yīng)答HEAD請求。在這種情況下,它可以為HEAD請求返回緩存的GET響應(yīng)。

此外,會UpdateCacheMiddleware自動在每個標題中設(shè)置一些標題 HttpResponse:

  • 將Expires標題設(shè)置為當前日期/時間加上定義的 CACHE_MIDDLEWARE_SECONDS。
  • Cache-Control再次設(shè)置,將頁眉設(shè)置為頁面的最長使用期限CACHE_MIDDLEWARE_SECONDS。

有關(guān)中間件的更多信息,請參見中間件。

如果視圖設(shè)置了自己的緩存到期時間(即max-age,其Cache-Control標題中有一個部分),則頁面將一直緩存到到期時間,而不是CACHE_MIDDLEWARE_SECONDS。使用裝飾器 django.views.decorators.cache可以輕松設(shè)置視圖的過期時間(使用cache_control()裝飾器)或禁用視圖的緩存(使用 never_cache()裝飾器)。有關(guān)這些裝飾器的更多信息,請參見“ 使用其他標頭”部分。

如果USE_I18N設(shè)置為,True則生成的緩存鍵將包括活動語言的名稱-另請參見 Django如何發(fā)現(xiàn)語言首選項。這使您可以輕松地緩存多語言站點,而不必自己創(chuàng)建緩存密鑰。

緩存鍵還包括積極的語言時, USE_L10N設(shè)置為True與當前時區(qū)時USE_TZ被設(shè)置為True。

每視圖緩存

django.views.decorators.cache.cache_page()

使用緩存框架的更精細的方法是緩存單個視圖的輸出。django.views.decorators.cache定義一個cache_page 裝飾器,該裝飾器將自動為您緩存視圖的響應(yīng):

from django.views.decorators.cache import cache_page

@cache_page(60 * 15)
def my_view(request):
    ...

cache_page有一個參數(shù):緩存超時(以秒為單位)。在上面的示例中,my_view()視圖結(jié)果將被緩存15分鐘。(請注意,我們出于可讀性的目的編寫了該代碼。它將被評估為– 15分鐘乘以每分鐘60秒。)60 * 1560 * 15900

與每個站點的緩存一樣,每個視圖的緩存也是從URL鍵入的。如果多個URL指向同一視圖,則每個URL將被分別緩存。繼續(xù)該my_view示例,如果您的URLconf如下所示:

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

然后,您所期望的/foo/1/和的請求/foo/23/將分別進行緩存。但是,一旦/foo/23/請求了特定的URL(例如),則對該URL的后續(xù)請求將使用緩存。

cache_page也可以采用可選的關(guān)鍵字參數(shù),cache該參數(shù)指示裝飾器CACHES在緩存視圖結(jié)果時使用特定的緩存(來自您的 設(shè)置)。默認情況下, default將使用緩存,但是您可以指定所需的任何緩存:

@cache_page(60 * 15, cache="special_cache")
def my_view(request):
    ...

您還可以基于每個視圖覆蓋緩存前綴。cache_page 帶有一個可選的關(guān)鍵字參數(shù),key_prefix其工作方式CACHE_MIDDLEWARE_KEY_PREFIX 與中間件的設(shè)置相同??梢赃@樣使用:

@cache_page(60 * 15, key_prefix="site1")
def my_view(request):
    ...

的key_prefix和cache參數(shù)可以被同時指定。該 key_prefix參數(shù)和KEY_PREFIX 規(guī)定下,CACHES將串聯(lián)。

在URLconf中指定每個視圖的緩存

上一節(jié)中的示例已經(jīng)硬編碼了緩存視圖的事實,因為該視圖cache_page改變了my_view功能。這種方法將您的視圖耦合到高速緩存系統(tǒng),由于多種原因,這種方法并不理想。例如,您可能想在另一個無緩存的站點上重用視圖功能,或者可能希望將視圖分發(fā)給可能希望在不被緩存的情況下使用它們的人。這些問題的解決方案是在URLconf中指定每個視圖的緩存,而不是在視圖函數(shù)本身旁邊。

您可以通過cache_page在URLconf中引用視圖函數(shù)時將其包裝在一起來實現(xiàn)。這是之前的舊版URLconf:

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

這是同一件事,my_view包裹在其中cache_page:

from django.views.decorators.cache import cache_page

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

模板片段緩存

如果您希望獲得更多控制權(quán),則還可以使用cachetemplate標簽緩存模板片段。要讓您的模板訪問此標簽,請放在 模板頂部附近。{% load cache %}

該模板標簽緩存塊中的內(nèi)容給定的時間量。它至少需要兩個參數(shù):高速緩存超時(以秒為單位)和提供高速緩存片段的名稱。如果超時為,則片段將永遠被緩存。名稱將按原樣使用,請勿使用變量。例如:{% cache %}None

{% load cache %}
{% cache 500 sidebar %}
    .. sidebar ..
{% endcache %}

有時,您可能希望根據(jù)片段內(nèi)顯示的一些動態(tài)數(shù)據(jù)來緩存片段的多個副本。例如,您可能想要為站點中的每個用戶提供上一個示例中使用的側(cè)邊欄的單獨的緩存副本。為此,請將一個或多個其他參數(shù)(可以是帶或不帶過濾器的變量)傳遞給模板標記,以唯一地標識緩存片段:{% cache %}

{% load cache %}
{% cache 500 sidebar request.user.username %}
    .. sidebar for logged in user ..
{% endcache %}

如果USE_I18N將設(shè)置為True每個站點,則中間件緩存將 尊重活動語言。對于cache模板標記,您可以使用模板中可用的特定于 翻譯的變量之一來獲得相同的結(jié)果:

{% load i18n %}
{% load cache %}

{% get_current_language as LANGUAGE_CODE %}

{% cache 600 welcome LANGUAGE_CODE %}
    {% trans "Welcome to example.com" %}
{% endcache %}

緩存超時可以是模板變量,只要模板變量解析為整數(shù)值即可。例如,如果將模板變量 my_timeout設(shè)置為value 600,那么以下兩個示例是等效的:

{% cache 600 sidebar %} ... {% endcache %}
{% cache my_timeout sidebar %} ... {% endcache %}

此功能有助于避免模板中的重復。您可以在一個位置的變量中設(shè)置超時,然后重用該值。

默認情況下,緩存標簽將嘗試使用名為“ template_fragments”的緩存。如果不存在這樣的緩存,它將退回到使用默認緩存。您可以選擇與using關(guān)鍵字參數(shù)一起使用的備用緩存后端,該參數(shù)必須是標記的最后一個參數(shù)。

{% cache 300 local-thing ...  using="localcache" %}

指定未配置的緩存名稱被視為錯誤。

django.core.cache.utils.make_template_fragment_keyfragment_name,vary_on =無

如果要獲取用于緩存片段的緩存密鑰,可以使用 make_template_fragment_key。fragment_name與cachetemplate標簽的第二個參數(shù)相同;vary_on是傳遞給標記的所有其他參數(shù)的列表。該功能對于使緩存項無效或覆蓋很有用,例如:

>>> from django.core.cache import cache
>>> from django.core.cache.utils import make_template_fragment_key
# cache key for {% cache 500 sidebar username %}
>>> key = make_template_fragment_key('sidebar', [username])
>>> cache.delete(key) # invalidates cached template fragment

低級緩存API

有時,緩存整個渲染的頁面并不會帶來太多好處,實際上,這會帶來不便。

例如,也許您的站點包含一個視圖,該視圖的結(jié)果取決于幾個昂貴的查詢,這些查詢的結(jié)果以不同的間隔更改。在這種情況下,使用每個站點或每個視圖緩存策略提供的全頁緩存并不理想,因為您不想緩存整個結(jié)果(因為某些數(shù)據(jù)經(jīng)常更改),但您仍想緩存很少更改的結(jié)果。

對于此類情況,Django會公開一個低級緩存API。您可以使用此API以所需的任意粒度級別將對象存儲在緩存中。您可以緩存可以安全腌制的任何Python對象:字符串,字典,模型對象列表等。(可以對大多數(shù)常見的Python對象進行腌制;有關(guān)腌制的更多信息,請參考Python文檔。)

訪問緩存

django.core.cache.caches

您可以CACHES通過類似dict的對象訪問在設(shè)置中配置的緩存:django.core.cache.caches。在同一線程中重復請求相同的別名將返回相同的對象。

>>> from django.core.cache import caches
>>> cache1 = caches['myalias']
>>> cache2 = caches['myalias']
>>> cache1 is cache2
True

如果指定的鍵不存在,InvalidCacheBackendError將引發(fā)。

為了提供線程安全,將為每個線程返回不同的緩存后端實例。

django.core.cache.cache

作為一種快捷方式,默認高速緩存可用于 django.core.cache.cache

>>> from django.core.cache import cache

此對象等效于caches['default']。

基本用法

基本界面是:

cache.setkeyvalue,timeout = DEFAULT_TIMEOUT,version = None
>>> cache.set('my_key', 'hello, world!', 30)
cache.getkey,default = None,version = None
>>> cache.get('my_key')
'hello, world!'

key應(yīng)該是str,并且value可以是任何可挑選的Python對象。

該timeout參數(shù)是可選的,并且默認為設(shè)置中timeout相應(yīng)后端的參數(shù)CACHES(如上所述)。這是該值應(yīng)存儲在緩存中的秒數(shù)。傳遞 Nonefor timeout將永遠緩存該值。一個timeout的0 將不緩存值。

如果對象在緩存中不存在,則cache.get()返回None:

>>> # Wait 30 seconds for 'my_key' to expire...
>>> cache.get('my_key')
None

我們建議不要將文字值存儲None在緩存中,因為您將無法區(qū)分存儲的None值和返回值為的緩存未命中None。

cache.get()可以default爭論。這指定了如果對象在緩存中不存在則返回哪個值:

>>> cache.get('my_key', 'has expired')
'has expired'
cache.addkey,valuetimeout = DEFAULT_TIMEOUT,version = None

若要僅在密鑰尚不存在時添加密鑰,請使用add()方法。它使用與相同的參數(shù)set(),但是如果指定的鍵已經(jīng)存在,它將不會嘗試更新緩存:

>>> cache.set('add_key', 'Initial value')
>>> cache.add('add_key', 'New value')
>>> cache.get('add_key')
'Initial value'

如果您需要知道是否add()在緩存中存儲了值,則可以檢查返回值。True如果存儲了值,它將返回, False否則返回。

cache.get_or_setkeydefault,timeout = DEFAULT_TIMEOUTversion = None

如果要獲取鍵的值,或者如果鍵不在緩存中則要設(shè)置值,則可以使用該get_or_set()方法。它使用與相同的參數(shù),get() 但默認設(shè)置為該鍵的新緩存值,而不是返回:

>>> cache.get('my_new_key')  # returns None
>>> cache.get_or_set('my_new_key', 'my new value', 100)
'my new value'

您還可以將任何callable作為默認值傳遞:

>>> import datetime
>>> cache.get_or_set('some-timestamp-key', datetime.datetime.now)
datetime.datetime(2014, 12, 11, 0, 15, 49, 457920)
cache.get_manykeys,version = None

還有一個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}
cache.set_manydict超時

要更有效地設(shè)置多個值,請使用set_many()傳遞鍵值對字典:

>>> cache.set_many({'a': 1, 'b': 2, 'c': 3})
>>> cache.get_many(['a', 'b', 'c'])
{'a': 1, 'b': 2, 'c': 3}

像一樣cache.set(),set_many()帶有一個可選timeout參數(shù)。

在支持的后端(memcached)上,set_many()返回未能插入的密鑰列表。

cache.deletekeyversion = None

您可以顯式刪除鍵,delete()以清除特定對象的緩存:

>>> cache.delete('a')
cache.delete_manykeys,version = None

如果要一次清除一堆鍵,delete_many()可以列出要清除的鍵列表:

>>> cache.delete_many(['a', 'b', 'c'])
cache.clear()

最后,如果要刪除緩存中的所有鍵,請使用 cache.clear()。注意這一點。clear()將從 緩存中刪除所有內(nèi)容,而不僅僅是您的應(yīng)用程序設(shè)置的鍵。

>>> cache.clear()
cache.touchkey,timeout = DEFAULT_TIMEOUTversion = None

cache.touch()為密鑰設(shè)置新的到期時間。例如,要將密鑰更新為從現(xiàn)在起10秒鐘過期:

>>> cache.touch('a', 10)
True

與其他方法一樣,該timeout參數(shù)是可選的,并且默認為設(shè)置中TIMEOUT相應(yīng)后端的 選項CACHES。

touch()True如果按鍵被成功觸摸,F(xiàn)alse 則返回;否則返回。

cache.incrkey,delta = 1,version = None
cache.decrkey,delta = 1version = None

您也可以分別使用incr()或decr()方法遞增或遞減已存在的鍵 。默認情況下,現(xiàn)有的高速緩存值將遞增或遞減1??梢酝ㄟ^為遞增/遞減調(diào)用提供參數(shù)來指定其他遞增/遞減值。如果您嘗試增加或減少不存在的緩存鍵,將引發(fā)ValueError:

>>> cache.set('num', 1)
>>> cache.incr('num')
2
>>> cache.incr('num', 10)
12
>>> cache.decr('num')
11
>>> cache.decr('num', 5)
6

注意:incr()/ decr()方法不保證是原子的。在那些支持原子增量/減量的后端(最值得注意的是,內(nèi)存緩存的后端)上,增量和減量操作將是原子的。但是,如果后端本身不提供增量/減量操作,則將使用兩步檢索/更新來實現(xiàn)。

cache.close()

close()如果由緩存后端實現(xiàn),則可以關(guān)閉與緩存的連接。

>>> cache.close()

注意:對于不實現(xiàn)close方法的緩存,它是無操作的。

緩存鍵前綴

如果要在服務(wù)器之間或生產(chǎn)環(huán)境與開發(fā)環(huán)境之間共享緩存實例,則一臺服務(wù)器緩存的數(shù)據(jù)可能會被另一臺服務(wù)器使用。如果服務(wù)器之間的緩存數(shù)據(jù)格式不同,則可能導致某些很難診斷的問題。

為防止這種情況,Django提供了為服務(wù)器使用的所有緩存鍵添加前綴的功能。保存或檢索特定的緩存鍵后,Django將自動為緩存鍵添加KEY_PREFIX緩存設(shè)置的值 。

通過確保每個Django實例都有一個不同的 KEY_PREFIX,您可以確保緩存值不會發(fā)生沖突。

緩存版本

更改使用緩存值的運行代碼時,可能需要清除所有現(xiàn)有的緩存值。最簡單的方法是刷新整個緩存,但這會導致仍然有效且有用的緩存值丟失。

Django提供了一種更好的方法來定位各個緩存值。Django的緩存框架具有系統(tǒng)范圍的版本標識符,該標識符使用VERSION緩存設(shè)置指定。此設(shè)置的值會自動與緩存前綴和用戶提供的緩存鍵組合在一起,以獲取最終的緩存鍵。

默認情況下,任何密鑰請求都將自動包含站點默認的緩存密鑰版本。但是,原始緩存功能都包含一個version參數(shù),因此您可以指定要設(shè)置或獲取的特定緩存鍵版本。例如:

>>> # Set version 2 of a cache key
>>> cache.set('my_key', 'hello world!', version=2)
>>> # Get the default version (assuming version=1)
>>> cache.get('my_key')
None
>>> # Get version 2 of the same key
>>> cache.get('my_key', version=2)
'hello world!'

可以使用incr_version()和decr_version()方法對特定鍵的版本進行遞增和遞減。這樣可以使特定的鍵更改為新版本,而其他鍵不受影響。繼續(xù)前面的示例:

>>> # Increment the version of 'my_key'
>>> cache.incr_version('my_key')
>>> # The default version still isn't available
>>> cache.get('my_key')
None
# Version 2 isn't available, either
>>> cache.get('my_key', version=2)
None
>>> # But version 3 *is* available
>>> cache.get('my_key', version=3)
'hello world!'

緩存鍵轉(zhuǎn)換

如前兩節(jié)所述,用戶不能完全原樣使用用戶提供的緩存密鑰,而是將其與緩存前綴和密鑰版本結(jié)合使用以提供最終的緩存密鑰。默認情況下,這三個部分使用冒號連接起來以生成最終字符串:

def make_key(key, key_prefix, version):
    return '%s:%s:%s' % (key_prefix, version, key)

如果要以不同的方式組合各部分,或?qū)ψ罱K鍵進行其他處理(例如,獲取鍵部分的哈希摘要),則可以提供自定義鍵功能。

的KEY_FUNCTION高速緩存設(shè)置指定匹配的原型的功能的虛線路徑 make_key()的上方。如果提供,將使用此自定義按鍵功能代替默認的按鍵組合功能。

緩存鍵警告

Memcached是最常用的生產(chǎn)緩存后端,它不允許長度超過250個字符或包含空格或控制字符的緩存鍵,并且使用此類鍵會導致異常。為了鼓勵緩存可移植的代碼并最大程度地減少令人不快的意外,django.core.cache.backends.base.CacheKeyWarning如果使用的鍵會導致memcached錯誤,則其他內(nèi)置的緩存后端會發(fā)出警告()。

如果您使用的生產(chǎn)后端可以接受更廣泛的鍵范圍(自定義后端或非內(nèi)存緩存的內(nèi)置后端之一),并且希望在沒有警告的情況下使用更大范圍的鍵,則可以CacheKeyWarning在此代碼中保持靜音management您之一的模塊 INSTALLED_APPS:

import warnings

from django.core.cache import CacheKeyWarning

warnings.simplefilter("ignore", CacheKeyWarning)

如果您想為內(nèi)置后端之一提供自定義密鑰驗證邏輯,則可以對其進行子類化,僅覆蓋validate_key 方法,并按照說明使用自定義緩存后端。例如,要為locmem后端執(zhí)行此操作,請將以下代碼放在模塊中:

from django.core.cache.backends.locmem import LocMemCache

class CustomLocMemCache(LocMemCache):
    def validate_key(self, key):
        """Custom validation, raising exceptions or warnings as needed."""
        ...

…并在設(shè)置的BACKEND一部分中使用指向該類的虛線Python路徑 CACHES。

下游緩存

到目前為止,本文檔的重點是緩存您自己的數(shù)據(jù)。但是另一種類型的緩存也與Web開發(fā)有關(guān):由“下游”緩存執(zhí)行的緩存。這些系統(tǒng)甚至可以在請求到達您的網(wǎng)站之前為用戶緩存頁面。

以下是下游緩存的一些示例:

  • 您的ISP可能會緩存某些頁面,因此,如果您從https://example.com/請求一個頁面 ,您的ISP將向您發(fā)送該頁面,而無需直接訪問example.com。example.com的維護者不了解這種緩存。ISP位于example.com和您的Web瀏覽器之間,透明地處理所有緩存。
  • 您的Django網(wǎng)站可能位于代理緩存(例如Squid Web代理緩存(http://www.squid-cache.org/))之后,該緩存可以緩存頁面以提高性能。在這種情況下,每個請求首先將由代理處理,并且僅在需要時才將其傳遞給您的應(yīng)用程序。
  • 您的Web瀏覽器也緩存頁面。如果網(wǎng)頁發(fā)出適當?shù)臉祟},您的瀏覽器將使用本地緩存的副本來對該頁面的后續(xù)請求,甚至無需再次聯(lián)系該網(wǎng)頁以查看其是否已更改。

下游緩存可以極大地提高效率,但是卻存在危險:許多網(wǎng)頁的內(nèi)容基于身份驗證和許多其他變量而有所不同,并且僅基于URL盲目保存頁面的緩存系統(tǒng)可能會將不正確或敏感的數(shù)據(jù)暴露給后續(xù)這些頁面的訪問者。

例如,如果您使用Web電子郵件系統(tǒng),則“收件箱”頁面的內(nèi)容取決于登錄的用戶。如果ISP盲目緩存了您的站點,則第一個通過該ISP登錄的用戶將擁有其用戶。特定的收件箱頁面已緩存,以供該站點的后續(xù)訪問者使用。那不酷。

幸運的是,HTTP提供了解決此問題的方法。存在許多HTTP標頭,以指示下游緩存根據(jù)指定的變量來區(qū)分其緩存內(nèi)容,并告知緩存機制不要緩存特定頁面。我們將在以下各節(jié)中介紹其中的一些標題。

使用Vary標題

的Vary報頭定義哪些請求頭的高速緩存機制建立其緩存鍵時應(yīng)該考慮到。例如,如果網(wǎng)頁的內(nèi)容取決于用戶的語言偏好,則稱該頁面“隨語言而異”。

默認情況下,Django的緩存系統(tǒng)使用請求的標準URL(例如,)創(chuàng)建其緩存密鑰 "https://www.example.com/stories/2005/?order_by=author"。這意味著對該URL的每個請求都將使用相同的緩存版本,而不管用戶代理差異(例如Cookie或語言偏好設(shè)置)如何。但是,如果此頁面根據(jù)請求標頭(例如Cookie,語言或用戶代理)的不同而產(chǎn)生不同的內(nèi)容,則需要使用Vary 標頭來告訴緩存機制頁面輸出取決于那些的東西。

要在Django中執(zhí)行此操作,請使用方便的 django.views.decorators.vary.vary_on_headers()視圖裝飾器,如下所示:

from django.views.decorators.vary import vary_on_headers

@vary_on_headers('User-Agent')
def my_view(request):
    ...

在這種情況下,緩存機制(例如Django自己的緩存中間件)將為每個唯一的用戶代理緩存頁面的單獨版本。

使用vary_on_headers裝飾器而不是手動設(shè)置Vary標頭(使用類似的東西 )的好處是,裝飾器將添加到 標頭(可能已經(jīng)存在),而不是從頭開始設(shè)置它并可能覆蓋其中的任何內(nèi)容。response['Vary'] = 'user-agent'Vary

您可以將多個標頭傳遞給vary_on_headers():

@vary_on_headers('User-Agent', 'Cookie')
def my_view(request):
    ...

這告訴下游緩存在這兩者上有所不同,這意味著用戶代理和cookie的每種組合都將獲得自己的緩存值。例如,具有用戶代理Mozilla和cookie值foo=bar的請求將被認為不同于具有用戶代理Mozilla和cookie值 的請求foo=ham。

因為在cookie上進行更改非常普遍,所以有一個 django.views.decorators.vary.vary_on_cookie()裝飾器。這兩個視圖是等效的:

@vary_on_cookie
def my_view(request):
    ...

@vary_on_headers('Cookie')
def my_view(request):
    ...

您傳遞給的標頭vary_on_headers不區(qū)分大小寫; "User-Agent"和一樣"user-agent"。

您也可以django.utils.cache.patch_vary_headers()直接使用輔助功能。此函數(shù)設(shè)置或添加到。例如:Vary header

from django.shortcuts import render
from django.utils.cache import patch_vary_headers

def my_view(request):
    ...
    response = render(request, 'template_name', context)
    patch_vary_headers(response, ['Cookie'])
    return response

patch_vary_headers將HttpResponse實例作為第一個參數(shù),并將不區(qū)分大小寫的標頭名稱的列表/元組作為第二個參數(shù)。

有關(guān)Vary標頭的更多信息,請參見 官方Vary規(guī)格

控制緩存:使用其他頭文件

緩存的其他問題是數(shù)據(jù)的私密性以及應(yīng)在級聯(lián)緩存中存儲數(shù)據(jù)的位置的問題。

用戶通常面臨兩種緩存:他們自己的瀏覽器緩存(私有緩存)和提供者的緩存(公共緩存)。公共緩存由多個用戶使用,并由其他人控制。這就給敏感數(shù)據(jù)帶來了麻煩,例如,您不希望將銀行帳號存儲在公共緩存中。因此,Web應(yīng)用程序需要一種方法來告訴緩存哪些數(shù)據(jù)是私有數(shù)據(jù),哪些是公共數(shù)據(jù)。

解決方案是指示頁面的緩存應(yīng)為“專用”。要在Django中執(zhí)行此操作,請使用cache_control()視圖裝飾器。例:

from django.views.decorators.cache import cache_control

@cache_control(private=True)
def my_view(request):
    ...

該裝飾器負責在后臺發(fā)送適當?shù)腍TTP標頭。

請注意,緩存控制設(shè)置“專用”和“公用”是互斥的。裝飾器確保如果應(yīng)設(shè)置為“ private”,則刪除“ public”指令(反之亦然)。這兩個偽指令的示例用法是提供私有條目和公共條目的博客網(wǎng)站。公共條目可以緩存在任何共享緩存中。以下代碼使用 patch_cache_control()手動方式修改緩存控制標頭(由cache_control()裝飾器內(nèi)部調(diào)用 ):

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

您還可以通過其他方式控制下游緩存(請參見 RFC 7234,了解有關(guān)HTTP緩存的詳細信息)。例如,即使您不使用Django的服務(wù)器端緩存框架,您仍然可以使用以下命令告訴客戶端將視圖緩存一定的時間:最大年齡 指令:

from django.views.decorators.cache import cache_control

@cache_control(max_age=3600)
def my_view(request):
    ...

(如果你做使用緩存中間件,它已經(jīng)設(shè)置max-age與值CACHE_MIDDLEWARE_SECONDS的設(shè)置。在這種情況下,自定義max_age從 cache_control()裝飾將優(yōu)先考慮,并且頭部值將被正確地合并。)

任何有效的Cache-Control響應(yīng)指令在中均有效cache_control()。這里還有更多示例:

  • no_transform=True
  • must_revalidate=True
  • stale_while_revalidate=num_seconds

可以在IANA注冊中心中找到已知指令的完整列表 (請注意,并非所有指令都適用于響應(yīng))。

如果要使用標頭來完全禁用緩存,請 never_cache()使用視圖裝飾器來添加標頭,以確保瀏覽器或其他緩存不會緩存響應(yīng)。例:

from django.views.decorators.cache import never_cache

@never_cache
def myview(request):
    ...

MIDDLEWARE

如果您使用緩存中間件,則將每一半放在MIDDLEWARE設(shè)置中的正確位置上很重要。這是因為緩存中間件需要知道通過哪些頭來更改緩存存儲。中間件總是盡可能在Vary響應(yīng)頭中添加一些內(nèi)容。

UpdateCacheMiddleware在響應(yīng)階段運行,中間件以相反的順序運行,因此列表頂部的項目在響應(yīng)階段最后運行。因此,您需要確保它UpdateCacheMiddleware 出現(xiàn)在任何其他可能向Vary 標頭添加內(nèi)容的中間件之前。以下中間件模塊可以這樣做:

  • SessionMiddleware 添加 Cookie
  • GZipMiddleware 添加 Accept-Encoding
  • LocaleMiddleware 添加 Accept-Language

FetchFromCacheMiddleware另一方面,在請求階段運行,其中中間件首先應(yīng)用,因此列表頂部的項目在請求階段首先運行。該FetchFromCacheMiddleware還需要經(jīng)過其他中間件更新運行Vary頭,所以 FetchFromCacheMiddleware必須在后的是這樣做的任何項目。

詳情參考: https://docs.djangoproject.com/en/3.0/


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號