緩存是用於提升網站性能的一種即簡單又有效的途徑。稍微有點規模的網站都會通過存儲相對靜態的數據至緩存以備所需,這樣我們可以省去從數據庫查詢然後生成這些數據的時間,通過減輕數據庫的壓力從而提升網站的性能。
Yii作為一個強大的php開源框架,再緩存這塊,YII對各種流行的緩存都提供了接口,我們可以根據實際的需要使用不同的緩存。
yii的緩存相關組件存放在yii/framework/caching的目錄內,通過查看該目錄,我們可以知道yii所支持的都有哪些緩存。下面我們對相關文件做一些簡單的介紹:
CMemCache: 使用 PHP memcache 擴展.
CApcCache: 使用 PHP APC 擴展.
CXCache: 使用 PHP XCache 擴展。注意,這個是從 1.0.1 版本開始支持的。
CEAcceleratorCache: 使用 PHP EAccelerator 擴展.
CDbCache: 使用一個數據表存儲緩存數據。默認情況下,它將創建並使用在 runtime 目錄下的一個 SQLite3 數據庫。 你也可以通過設置其 connectionID 屬性指定一個給它使用的數據庫。
CZendDataCache: 使用 Zend Data Cache 作為後台緩存媒介。注意,這個是從 1.0.4 版本開始支持的。
CFileCache: 使用文件存儲緩存數據。這個特別適合用於存儲大塊數據(例如頁面)。注意,這個是從 1.0.6 版本開始支持的。
CDummyCache: 目前 dummy 緩存並不實現緩存功能。此組件的目的是用於簡化那些需要檢查緩存可用性的代碼。例如,在開發階段或者服務器尚未支持實際的緩存功能,我們可以使用此緩存組件。當啟用了實際的緩存支持後,我們可以切換到使用相應的緩存組件。在這兩種情況中,我們可以使用同樣的代碼Yii::app()->cache->get($key) 獲取數據片段而不需要擔心 Yii::app()->cache 可能會是 null。此組件從 1.0.5 版開始支持。
提示: 由於所有的這些緩存組件均繼承自同樣的基類 CCache,因此無需改變使用緩存的那些代碼就可以切換到使用另一種緩存方式。
在 Yii 中使用緩存主要包括配置和訪問緩存組件 。 下面以分別以使用 memcache 緩存組件和文件緩存組件為例做二個說明:
(1)memcache 緩存示例:
如下的應用配置指定了一個使用兩台緩存服務器的 memcache 緩存組件:
array( ...... 'components'=>array( ...... 'phpernote_cache'=>array( 'class'=>'system.caching.CMemCache', 'servers'=>array( array('host'=>'10.201.1.101', 'port'=>11211, 'weight'=>60), array('host'=>'10.201.1.102', 'port'=>11211, 'weight'=>40), ), ), ), );
程序運行的時候可以再控制器方法中通過 Yii::app()->phpernote_cache 來訪問緩存組件,例如:
Yii::app ()->phpernote_cache->set($key,$value,$expire);
Yii::app()->phpernote_cache->add($id,$value);
Yii::app ()->phpernote_cache->get($key);
(2)文件緩存示例:
1. 在config文件加入緩存配置信息
array( ...... 'components'=>array( ...... 'cache'=>array( 'class'=>'system.caching.CFileCache', 'directoryLevel'=>2 ), ), ), );
以上配置中 directoryLevel 設置的是緩存文件的目錄深度;如果緩存頁面特別多,這個值需要設置大點,否則每個目錄下的頁面會很多。
2. 在要做緩存的控制器裡定義過濾器。
public function filters(){ return array( array( 'COutputCache + post, list', 'duration'=>3600, 'varyByParam'=>array('id','page'), 'dependency'=>array( 'class'=>'CDbCacheDependency', 'sql'=>'SELECT MAX(id) FROM phpernote_article', ) ); }
COutputCache 是用於處理緩存的類,如果只填'COutputCache',則控制器裡所有action都會通過緩存過濾,定義'COutputCache + post, list',表示只對以下方法進行緩存:actionPost, actionList 。
duration 是緩存的時間,單位是秒,
varyByParam 是指定一系列GET參數名稱列表, 使用相應的值去確定緩存內容的版本,即同一個action用於區分是不同頁面的的參數,此處我以id和page來區分不同頁面。
除varyByParam以外,還可以采用其他的條件來區分頁面:
varyByExpression:指定緩存內容通過自定義的PHP表達式的結果而變化
varyByRoute:指定緩存內容基於請求的路由不同而變化 (controller 和 action)
varyBySession:指定是否緩存內容. 因用戶session不同而變化
dependency'指定緩存失效依賴關系:可指定文件或數據庫;本示例指定的是數據庫依賴 CDbCacheDependency ,通過數據表的某個值的變化來確定緩存是否失效。例如,如果在表中新增了一條記錄,即使緩存才過了5分鐘(<3600),仍然判斷為失效,從而查詢數據庫,生成整個頁面,再次緩存;
提示:yii也可以支持Redis,需要裝一個插件:http://www.yiibase.com/download/view/32.html
緩存可以在不同的級別使用。在最低級別,可用來緩存單個數據(數據緩存)。往上一級,我們緩存一個由視圖腳本生成的頁面片斷(片段緩存)。在最高級別,可存儲整個頁面以便需要的時候直接從緩存讀取。本文說明頁面緩存的配置及實現效果;
數據緩存
數據緩存即存儲一些 PHP 變量到緩存中,以後再從緩存中取出來。出於此目的,緩存組件的基類 CCache 提供了兩個最常用的方法: set() 和 get()。
要在緩存中存儲一個變量 $value ,我們選擇一個唯一 ID 並調用 set() 存儲它:
Yii::app()->cache->set($id, $value);緩存的數據將一直留在緩存中,除非它由於某些緩存策略(例如緩存空間已滿,舊的數據被刪除)而被清除。 要改變這種行為,我們可以在調用 set() 的同時提供一個過期參數,這樣在設定的時間段之後,緩存數據將被清除:
// 值$value 在緩存中最多保留30秒
Yii::app()->cache->set($id, $value, 30);稍後當我們需要訪問此變量時(在同一個或不同的 Web 請求中),就可以通過 ID 調用 get() 從緩存中將其取回。 如果返回的是 false,表示此值在緩存中不可用,我們應該重新生成它。
$value=Yii::app()->cache->get($id); if($value===false){ // 因為在緩存中沒找到 $value ,重新生成它 , // 並將它存入緩存以備以後使用: // Yii::app()->cache->set($id,$value); }
為要存入緩存的變量選擇 ID 時,要確保此 ID 對應用中所有其他存入緩存的變量是唯一的。而在不同的應用之間,這個 ID 不需要是唯一的。緩存組件具有足夠的智慧區分不同應用中的 ID。
一些緩存存儲器,例如 MemCache, APC, 支持以批量模式獲取多個緩存值。這可以減少獲取緩存數據時帶來的開銷。從版本 1.0.8 起,Yii 提供了一個新的名為 mget() 的方法。它可以利用此功能。如果底層緩存存儲器不支持此功能,mget() 依然可以模擬實現它。
要從緩存中清除一個緩存值,調用 delete(); 要清楚緩存中的所有數據,調用 flush()。 當調用 flush() 時一定要小心,因為它會同時清除其他應用中的緩存。
提示: 由於 CCache 實現了 ArrayAccess,因此緩存組件也可以像一個數組一樣使用。下面是幾個例子:
$cache=Yii::app()->cache; $cache['username']=$value1; // 相當於: $cache->set('username',$value1); $value2=$cache['username']; // 相當於: $value2=$cache->get('username');
1. 緩存依賴
除了過期設置,緩存數據也可能會因為依賴條件發生變化而失效。例如,如果我們緩存了某些文件的內容,而這些文件發生了改變,我們就應該讓緩存的數據失效,並從文件中讀取最新內容而不是從緩存中讀取。
我們將一個依賴關系表現為一個 CCacheDependency 或其子類的實例。 當調用 set() 時,我們連同要緩存的數據將其一同傳入。如下面這段代碼的意思就是此值將在30秒後失效,但如果依賴的文件發生了變化則立即失效:
Yii::app()->cache->set($id, $value, 30, new CFileCacheDependency('FileName'));
現在如果我們通過調用get() 從緩存中獲取 $value ,依賴關系將被檢查,如果發生改變,我們將會得到一個 false 值,表示數據需要被重新生成。
如下是可用的緩存依賴的簡要說明:
CFileCacheDependency: 如果文件的最後修改時間發生改變,則依賴改變。
CDirectoryCacheDependency: 如果目錄和其子目錄中的文件發生改變,則依賴改變。
CDbCacheDependency: 如果指定 SQL 語句的查詢結果發生改變,則依賴改變。
CGlobalStateCacheDependency: 如果指定的全局狀態發生改變,則依賴改變。全局狀態是應用中的一個跨請求,跨會話的變量。它是通過 CApplication::setGlobalState() 定義的。
CChainedCacheDependency: 如果鏈中的任何依賴發生改變,則此依賴改變。
CExpressionDependency: 如果指定的 PHP 表達式的結果發生改變,則依賴改變。此類從版本 1.0.4 起可用。
片段緩存(Fragment Caching)
片段緩存指緩存網頁某片段。例如,如果一個頁面在表中顯示每年的銷售摘要,我們可以存儲此表在緩存中,減少每次請求需要重新產生的時間。
要使用片段緩存,在控制器視圖腳本中調用 CController::beginCache() 和 CController::endCache() 。這兩種方法開始和結束包括的頁面內容將被緩存。類似data caching ,我們需要一個編號,識別被緩存的片段。例如下面這段代碼如果beginCache() 返回false,緩存的內容將此地方自動插入; 否則,在if語句內的內容將被執行並在endCache()觸發時緩存。
...... <?php if($this->beginCache($id)) { ?> ...被緩存的內容... <?php $this->endCache(); } ?> ......
1. 緩存選項(Caching Options)
當調用beginCache(),可以提供一個數組由緩存選項組成的作為第二個參數,以自定義片段緩存。事實上為了方便,beginCache() 和endCache()方法是[ COutputCache ]widget的包裝。因此COutputCache的所有屬性都可以在緩存選項中初始化。
有效期(Duration)
也許是最常見的選項是duration,指定了內容在緩存中多久有效。和CCache::set()過期參數有點類似。下面的代碼緩存內容片段最多一小時:
...... <?php if($this->beginCache($id, array('duration'=>3600))) { ?> ...被緩存的內容... <?php $this->endCache(); } ?> ......
如果我們不設定期限,它將默認為60 ,這意味著60秒後緩存內容將無效。
依賴(Dependency)
像data caching ,內容片段被緩存也可以有依賴。例如,文章的內容被顯示取決於文章是否被修改。
要指定一個依賴,我們建立了dependency選項,可以是一個實現[ICacheDependency]的對象或可用於生成依賴對象的配置數組。下面的代碼指定片段內容取決於lastModified 列的值是否變化:
...... <?php if($this->beginCache($id, array('dependency'=>array( 'class'=>'system.caching.dependencies.CDbCacheDependency', 'sql'=>'SELECT MAX(lastModified) FROM Post')))) { ?> ...被緩存的內容... <?php $this->endCache(); } ?> ......
變化(Variation)
緩存的內容可根據一些參數變化。例如,每個人的檔案都不一樣。緩存的檔案內容將根據每個人ID變化。這意味著,當調用beginCache()時將用不同的ID。
COutputCache內置了這一特征,程序員不需要編寫根據ID變動內容的模式。以下是摘要。
varyByRoute: 設置此選項為true ,緩存的內容將根據route變化。因此,每個控制器和行動的組合將有一個單獨的緩存內容。
varyBySession: 設置此選項為true ,緩存的內容將根據session ID變化。因此,每個用戶會話可能會看到由緩存提供的不同內容。
varyByParam: 設置此選項的數組裡的名字,緩存的內容將根據GET參數的值變動。例如,如果一個頁面顯示文章的內容根據id的GET參數,我們可以指定varyByParam為array('id'),以使我們能夠緩存每篇文章內容。如果沒有這樣的變化,我們只能能夠緩存某一文章。
有時候,我們希望片段緩存只對某些類型的請求啟用。例如,對於某張網頁上顯示表單,我們只想要緩存initially requested表單(通過GET請求)。任何隨後顯示(通過POST請求)的表單將不被緩存,因為表單可能包含用戶輸入。要做到這一點,我們可以指定 requestTypes 選項:
...... <?php if($this->beginCache($id, array('requestTypes'=>array('GET')))) { ?> ...被緩存的內容... <?php $this->endCache(); } ?> ......
2. 嵌套緩存(Nested Caching)
片段緩存可以嵌套。就是說一個緩存片段附在一個更大的片段緩存裡。例如,意見緩存在內部片段緩存,而且它們一起在外部緩存中在文章內容裡緩存。
...... <?php if($this->beginCache($id1)) { ?> ...外部被緩存內容... <?php if($this->beginCache($id2)) { ?> ...內部被緩存內容... <?php $this->endCache(); } ?> ...外部被緩存內容... <?php $this->endCache(); } ?> ......
嵌套緩存可以設定不同的緩存選項。例如,在上面的例子中內部緩存和外部緩存可以設置時間長短不同的持續值。當數據存儲在外部緩存無效,內部緩存仍然可以提供有效的內部片段。然而,反之就不行了。如果外部緩存包含有效的數據, 它會永遠保持緩存副本,即使內容中的內部緩存已經過期。
頁面緩存
頁面緩存指的是緩存整個頁面的內容。頁面緩存可以發生在不同的地方。例如,通過選擇適當的頁面頭,客戶端的浏覽器可能會緩存網頁浏覽有限時間。Web應用程序本身也可以在緩存中存儲網頁內容。 在本節中,我們側重於後一種辦法。
頁面緩存可以被看作是 片段緩存一個特殊情況 。 由於網頁內容是往往通過應用布局來生成,如果我們只是簡單的在布局中調用beginCache() 和endCache(),將無法正常工作。這是因為布局在CController::render()方法裡的加載是在頁面內容產生之後。
如果想要緩存整個頁面,我們應該跳過產生網頁內容的動作執行。我們可以使用COutputCache作為動作 過濾器來完成這一任務。下面的代碼演示如何配置緩存過濾器:
public function filters(){ return array( array( 'COutputCache', 'duration'=>100, 'varyByParam'=>array('id'), ), ); }
上述過濾器配置會使過濾器適用於控制器中的所有行動。 我們可能會限制它在一個或幾個行動通過使用插件操作器。 更多的細節中可以看過濾器。
提示: 我們可以使用COutputCache作為一個過濾器,因為它從CFilterWidget繼承過來, 這意味著它是一個工具(widget)和一個過濾器。事實上,widget的工作方式和過濾器非常相似。工具widget (過濾器filter)是在action動作裡的內容執行前執行,在執行後結束。
6.緩存的使用:動態內容(Dynamic Content)
當使用fragment caching或page caching,我們常常遇到的這樣的情況 整個部分的輸出除了個別地方都是靜態的。例如,幫助頁可能會顯示靜態的幫助 信息,而用戶名稱顯示的是當前用戶的。
解決這個問題,我們可以根據用戶名匹配緩存內容,但是這將是我們寶貴空間一個巨大的浪費,因為緩存除了用戶名其他大部分內容是相同的。我們還可以把網頁切成幾個片段並分別緩存,但這種情況會使頁面和代碼變得非常復雜。更好的方法是使用由[ CController ]提供的動態內容dynamic content功能 。
動態內容是指片段輸出即使是在片段緩存包括的內容中也不會被緩存。即使是包括的內容是從緩存中取出,為了使動態內容在所有時間是動態的,每次都得重新生成。出於這個原因,我們要求 動態內容通過一些方法或函數生成。
調用CController::renderDynamic()在你想的地方插入動態內容。
...... <?php if($this->beginCache($id)) { ?> ...被緩存的片段內容... <?php $this->renderDynamic($callback); ?> ...被緩存的片段內容... <?php $this->endCache(); } ?> ......
在上面的,$callback指的是有效的PHP回調。它可以是指向當前控制器類的方法或者全局函數的字符串名。也可以是一個數組名指向一個類的方法。其他任何的參數,將傳遞到 renderDynamic()方法中。回調將返回動態內容而不是僅僅顯示它。