動態(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è)計決策。
緩存系統(tǒng)需要少量設(shè)置。即,您必須告訴它緩存的數(shù)據(jù)應(yīng)該存放在哪里–無論是在數(shù)據(jù)庫中,在文件系統(tǒng)上還是直接在內(nèi)存中。這是一個影響緩存性能的重要決定。是的,某些緩存類型比其他類型更快。
您的緩存首選項進入CACHES設(shè)置文件中的設(shè)置。以下是的所有可用值的說明 CACHES。
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í)行以下操作:
在此示例中,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)存的緩存特別臨時。
Django可以將其緩存的數(shù)據(jù)存儲在您的數(shù)據(jù)庫中。如果您擁有快速索引良好的數(shù)據(jù)庫服務(wù)器,則此方法效果最佳。
要將數(shù)據(jù)庫表用作緩存后端:
在此示例中,緩存表的名稱為my_cache_table:
CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.db.DatabaseCache', 'LOCATION': 'my_cache_table', } }
在使用數(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è)置路由說明。為了進行路由,數(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ù)庫緩存模型提供路由說明。
基于文件的后端將每個緩存值序列化并存儲為單獨的文件。要將此后端設(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。
如果未在設(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ā)很好。
最后,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è)置中的其他鍵提供 CACHES。有效參數(shù)如下:
在此示例中,文件系統(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è)置文件中:
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:
有關(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)。
上一節(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_key
(fragment_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
有時,緩存整個渲染的頁面并不會帶來太多好處,實際上,這會帶來不便。
例如,也許您的站點包含一個視圖,該視圖的結(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.
set
(key,value,timeout = DEFAULT_TIMEOUT,version = None)>>> cache.set('my_key', 'hello, world!', 30)
cache.
get
(key,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.
add
(key,value,timeout = 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_set
(key,default,timeout = DEFAULT_TIMEOUT,version = 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_many
(keys,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_many
(dict,超時)要更有效地設(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.
delete
(key,version = None)您可以顯式刪除鍵,delete()以清除特定對象的緩存:
>>> cache.delete('a')
cache.
delete_many
(keys,version = None)如果要一次清除一堆鍵,delete_many()可以列出要清除的鍵列表:
>>> cache.delete_many(['a', 'b', 'c'])
cache.
clear
()最后,如果要刪除緩存中的所有鍵,請使用 cache.clear()。注意這一點。clear()將從 緩存中刪除所有內(nèi)容,而不僅僅是您的應(yīng)用程序設(shè)置的鍵。
>>> cache.clear()
cache.
touch
(key,timeout = DEFAULT_TIMEOUT,version = 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.
incr
(key,delta = 1,version = None)cache.
decr
(key,delta = 1,version = 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!'
如前兩節(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)站之前為用戶緩存頁面。
以下是下游緩存的一些示例:
下游緩存可以極大地提高效率,但是卻存在危險:許多網(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報頭定義哪些請求頭的高速緩存機制建立其緩存鍵時應(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()。這里還有更多示例:
可以在IANA注冊中心中找到已知指令的完整列表 (請注意,并非所有指令都適用于響應(yīng))。
如果要使用標頭來完全禁用緩存,請 never_cache()使用視圖裝飾器來添加標頭,以確保瀏覽器或其他緩存不會緩存響應(yīng)。例:
from django.views.decorators.cache import never_cache @never_cache def myview(request): ...
如果您使用緩存中間件,則將每一半放在MIDDLEWARE設(shè)置中的正確位置上很重要。這是因為緩存中間件需要知道通過哪些頭來更改緩存存儲。中間件總是盡可能在Vary響應(yīng)頭中添加一些內(nèi)容。
UpdateCacheMiddleware在響應(yīng)階段運行,中間件以相反的順序運行,因此列表頂部的項目在響應(yīng)階段最后運行。因此,您需要確保它UpdateCacheMiddleware 出現(xiàn)在任何其他可能向Vary 標頭添加內(nèi)容的中間件之前。以下中間件模塊可以這樣做:
FetchFromCacheMiddleware另一方面,在請求階段運行,其中中間件首先應(yīng)用,因此列表頂部的項目在請求階段首先運行。該FetchFromCacheMiddleware還需要經(jīng)過其他中間件更新運行Vary頭,所以 FetchFromCacheMiddleware必須在后的是這樣做的任何項目。
詳情參考: https://docs.djangoproject.com/en/3.0/
更多建議: