Yii快速入門(四)緩存

緩存是用于提升網(wǎng)站性能的一種即簡單又有效的途徑。通過存儲相對靜態(tài)的數(shù)據(jù)至緩存以備所需,我們可以省去生成這些數(shù)據(jù)的時間。
在 Yii 中使用緩存主要包括配置和訪問緩存組件。如下的應用配置指定了一個使用兩臺緩存服務器的 memcache 緩存組件:

array(
    ......
    'components'=>array(
        ......
        'cache'=>array(
            'class'=>'system.caching.CMemCache',
            'servers'=>array(
                array('host'=>'server1', 'port'=>11211, 'weight'=>60),
                array('host'=>'server2', 'port'=>11211, 'weight'=>40),
            ),
        ),
    ),
);

程序運行的時候可以通過 Yii::app()->cache 來訪問緩存組件。
Yii 提供多種緩存組件以便在不同的媒介上存儲緩存數(shù)據(jù)。比如 CMemCache 組件封裝了 PHP memcache 擴展,它使用內存作為存儲緩存的媒介;CApcCache 組件封裝了 PHP APC 擴展;CDbCache 組件在數(shù)據(jù)庫里存儲緩存數(shù)據(jù)。下面是各種緩存組件的簡要說明:

CMemCache: 使用 PHP memcache 擴展。
CApcCache: 使用 PHP APC 擴展。
CXCache:   使用 PHP XCache 擴展。
CDbCache:  使用一張數(shù)據(jù)庫表來存儲緩存數(shù)據(jù)。它默認在運行時目錄建立并使用一個 SQLite3 數(shù)據(jù)庫,
           你可以通過設置 connectionID 屬性顯式地指定一個數(shù)據(jù)庫給它使用。

提示: 因為所有這些緩存組件都從同一個基礎類 CCache 擴展而來,不需要修改使用緩存的代碼即可在不同的緩存組件之間切換。
緩存可以在不同的級別使用。在最低級別,我們使用緩存來存儲單個數(shù)據(jù),比如一個變量,我們把它叫做 數(shù)據(jù)緩存。往上一級,我們緩存一個由視圖腳本生成的頁面片斷。在最高級別,我們存儲整個頁面以便需要的時候直接從緩存讀取。
接下來我們將闡述如何在這些級別上使用緩存。
注意: 按定義來講,緩存是一個不穩(wěn)定的存儲媒介,它不保證緩存一定存在——不管該緩存是否過期。所以,不要使用緩存進行持久存儲(比如,不要使用緩存來存儲 SESSION 數(shù)據(jù))。

一、數(shù)據(jù)緩存

數(shù)據(jù)緩存也就是在緩存中存儲一些 PHP 變量,過一會再取出來。緩存基礎類 CCache 提供了兩個最常用的方法:set()get()
要在緩存中存儲變量 $value,我們選擇一個唯一 ID 并調用 set() 來存儲它:
Yii::app()->cache->set($id, $value);
被緩存的數(shù)據(jù)會一直保留在緩存中,直到因一些緩存策略而被刪除(比如緩存空間滿了,刪除最舊的數(shù)據(jù))。要改變這一行為,我們還可以在調用 set() 時加一個過期參數(shù),這樣數(shù)據(jù)過一段時間就會自動從緩存中清除。

// 在緩存中保留該值最多 30 秒
Yii::app()->cache->set($id, $value, 30);

當我們稍后需要訪問該變量時(不管是不是同一 Web 請求),我們調用 get() (傳入 ID)來從緩存中獲取它。如果返回值為 false,說明該緩存不可用,需要我們重新生成它。

$value=Yii::app()->cache->get($id);
if($value===false)
{
    // 因為在緩存中沒找到,重新生成 $value
    // 再緩存一下以備下次使用
    // Yii::app()->cache->set($id,$value);
}

為一個要緩存的變量選擇 ID 時,確保該 ID 在應用中是唯一的。不必保證 ID 在跨應用的情況下保證唯一,因為緩存組件有足夠的智能來區(qū)分不同應用的緩存 ID。
要從緩存中刪除一個緩存值,調用 delete();要清空所有緩存,調用 flush()。調用 flush() 時要非常小心,因為它會把其它應用的緩存也清空。
提示: 因為 CCache 實現(xiàn)了 ArrayAccess 接口,可以像數(shù)組一樣使用緩存組件。例如:

    $cache=Yii::app()->cache;
    $cache['var1']=$value1;  // 相當于: $cache->set('var1',$value1);
    $value2=$cache['var2'];  // 相當于: $value2=$cache->get('var2');
緩存依賴

除了過期設置,緩存數(shù)據(jù)還會因某些依賴條件發(fā)生改變而失效。如果我們緩存了某文件的內容,而該文件后來又被更新了,我們應該讓緩存中的拷貝失效,從文件中讀取最新內容(而不是從緩存)。
我們把一個依賴關系表現(xiàn)為一個 CCacheDependency 或它的子類的實例,調用 set() 的時候把依賴實例和要緩存的數(shù)據(jù)一起傳入。

// 緩存將在 30 秒后過期
// 也可能因依賴的文件有更新而更快失效
Yii::app()->cache->set($id, $value, 30, new CFileCacheDependency('FileName'));

如果我們現(xiàn)在調用 get() 從緩存中獲取 $value,緩存組件將檢查依賴條件。如果有變,我們會得到 false 值——數(shù)據(jù)需要重新生成。
下面是可用的緩存依賴的簡要說明:

CFileCacheDependency:        該依賴因文件的最近修改時間發(fā)生改變而改變。
CDirectoryCacheDependency:   該依賴因目錄(或其子目錄)下的任何文件發(fā)生改變而改變。
CDbCacheDependency:          該依賴因指定的 SQL 語句的查詢結果發(fā)生改變而改變。
CGlobalStateCacheDependency: 該依賴因指定的全局狀態(tài)值發(fā)生改變而改變。
                             全局狀態(tài)是應用中跨請求、跨 SESSION 的持久變量,
                             它由 CApplication::setGlobalState() 來定義。
CChainedCacheDependency:     該依賴因依賴鏈中的任何一環(huán)發(fā)生改變而改變。

二、片段緩存(Fragment Caching)

片段緩存指緩存網(wǎng)頁某片段。例如,如果一個頁面在表中顯示每年的銷售摘要,我們可以存儲此表在緩存中,減少每次請求需要重新產生的時間。
要使用片段緩存,在控制器視圖腳本中調用CController::beginCache()CController::endCache() 。這兩種方法開始和結束包括的頁面內容將被緩存。類似data caching ,我們需要一個編號,識別被緩存的片段。

...別的HTML內容...
<?php if($this->beginCache($id)) { ?>
...被緩存的內容...
<?php $this->endCache(); } ?>
...別的HTML內容...

在上面的,如果beginCache() 返回false,緩存的內容將此地方自動插入; 否則,在if語句內的內容將被執(zhí)行并在endCache()觸發(fā)時緩存。

1. 緩存選項(Caching Options)

當調用beginCache(),可以提供一個數(shù)組由緩存選項組成的作為第二個參數(shù),以自定義片段緩存。事實上為了方便,beginCache()endCache()方法是 COutputCache widget的包裝。因此COutputCache的所有屬性都可以在緩存選項中初始化。

2. 有效期(Duration)

也許是最常見的選項是duration,指定了內容在緩存中多久有效。和CCache::set()過期參數(shù)有點類似。下面的代碼緩存內容片段最多一小時:

...其他HTML內容...
<?php if($this->beginCache($id, array('duration'=>3600))) { ?>
...被緩存的內容...
<?php $this->endCache(); } ?>
...其他HTML內容...

如果我們不設定期限,它將默認為60 ,這意味著60秒后緩存內容將無效。

3. 依賴(Dependency)

data caching ,內容片段被緩存也可以有依賴。例如,文章的內容被顯示取決于文章是否被修改。 要指定一個依賴,我們建立了dependency選項,可以是一個實現(xiàn)ICacheDependency的對象或可用于生成依賴對象的配置數(shù)組。下面的代碼指定片段內容取決lastModified` 列的值是否變化:

...其他HTML內容...
<?php if($this->beginCache($id, array('dependency'=>array(
        'class'=>'system.caching.dependencies.CDbCacheDependency',
        'sql'=>'SELECT MAX(lastModified) FROM Post')))) { ?>
...被緩存的內容...
<?php $this->endCache(); } ?>
...其他HTML內容...

4. 變化(Variation)

緩存的內容可根據(jù)一些參數(shù)變化。例如,每個人的檔案都不一樣。緩存的檔案內容將根據(jù)每個人ID變化。這意味著,當調用beginCache()時將用不同的ID。
COutputCache內置了這一特征,程序員不需要編寫根據(jù)ID變動內容的模式。以下是摘要。

varyByRoute:   設置此選項為true ,緩存的內容將根據(jù)route變化。
               因此,每個控制器和行動的組合將有一個單獨的緩存內容。
varyBySession: 設置此選項為true ,緩存的內容將根據(jù)session ID變化。
               因此,每個用戶會話可能會看到由緩存提供的不同內容。
varyByParam:   設置此選項的數(shù)組里的名字,緩存的內容將根據(jù)GET參數(shù)的值變動。
               例如,如果一個頁面顯示文章的內容根據(jù)id的GET參數(shù),我們可以指定varyByParam為array('id'),
               以使我們能夠緩存每篇文章內容。如果沒有這樣的變化,我們只能能夠緩存某一文章。

5. 請求類型(Request Types)

有時候,我們希望片段緩存只對某些類型的請求啟用。例如,對于某張網(wǎng)頁上顯示表單,我們只想要緩存initially requested表單(通過GET請求)。任何隨后顯示(通過POST請求)的表單將不被緩存,因為表單可能包含用戶輸入。要做到這一點,我們可以指定requestTypes 選項:

...其他HTML內容...
<?php if($this->beginCache($id, array('requestTypes'=>array('GET')))) { ?>
...被緩存的內容...
<?php $this->endCache(); } ?>
...其他HTML內容...

6. 嵌套緩存(Nested Caching)

片段緩存可以嵌套。就是說一個緩存片段附在一個更大的片段緩存里。例如,意見緩存在內部片段緩存,而且它們一起在外部緩存中在文章內容里緩存。

...其他HTML內容...
<?php if($this->beginCache($id1)) { ?>
...外部被緩存內容...
    <?php if($this->beginCache($id2)) { ?>
    ...內部被緩存內容...
    <?php $this->endCache(); } ?>
...外部被緩存內容...
<?php $this->endCache(); } ?>
...其他HTML內容...

嵌套緩存可以設定不同的緩存選項。例如, 在上面的例子中內部緩存和外部緩存可以設置時間長短不同的持續(xù)值。當數(shù)據(jù)存儲在外部緩存無效,內部緩存仍然可以提供有效的內部片段。 然而,反之就不行了。如果外部緩存包含有效的數(shù)據(jù), 它會永遠保持緩存副本,即使內容中的內部緩存已經(jīng)過期。

三、頁面緩存

頁面緩存指的是緩存整個頁面的內容。頁面緩存可以發(fā)生在不同的地方。例如,通過選擇適當?shù)捻撁骖^,客戶端的瀏覽器可能會緩存網(wǎng)頁瀏覽有限時間。 Web應用程序本身也可以在緩存中存儲網(wǎng)頁內容。 在本節(jié)中,我們側重于后一種辦法。
頁面緩存可以被看作是 片段緩存 (/doc/guide/caching.fragment)一個特殊情況 。由于網(wǎng)頁內容是往往通過應用布局來生成,如果我們只是簡單的在布局中調用 beginCache()endCache(),將無法正常工作。這是因為布局在CController::render()方法里的加載是在頁面內容產生之后。
緩存整個頁面,我們應該跳過產生網(wǎng)頁內容的動作執(zhí)行。我們可以使用 COutputCache 作為動作 過濾器 (/doc/guide/basics.controller#filter)來完成這一任務。下面的代碼演示如何配置緩存過濾器:

public function filters()
{
    return array(
        array(
            'system.web.widgets.COutputCache',
            'duration'=>100,
            'varyByParam'=>array('id'),
        ),
    );
}

上述過濾器配置會使過濾器適用于控制器中的所有行動。我們可能會限制它在一個或幾個行動通過使用插件操作器。更多的細節(jié)中可以看過濾器(/doc/guide/basics.controller#filter) 。
提示:我們可以使用 COutputCache 作為一個過濾器,因為它從CFilterWidget繼承過來 ,這意味著它是一個工具(widget)和一個過濾器。事實上, widge的工作方式和過濾器非常相似:工具widget (過濾器filter)是在action動作里的內容執(zhí)行前執(zhí)行,在執(zhí)行后結束。

四、動態(tài)內容(Dynamic Content)

當使用fragment cachingpage caching,我們常常遇到的這樣的情況整個部分的輸出除了個別地方都是靜態(tài)的。例如,幫助頁可能會顯示靜態(tài)的幫助信息,而用戶名稱顯示的是當前用戶的。
解決這個問題,我們可以根據(jù)用戶名匹配緩存內容,但是這將是我們寶貴空間一個巨大的浪費,因為緩存除了用戶名其他大部分內容是相同的。我們還可以把網(wǎng)頁切成幾個片段并分別緩存,但這種情況會使頁面和代碼變得非常復雜。更好的方法是使用由 CController 提供的動態(tài)內容dynamic content功能 。
動態(tài)內容是指片段輸出即使是在片段緩存包括的內容中也不會被緩存。即使是包括的內容是從緩存中取出,為了使動態(tài)內容在所有時間是動態(tài)的,每次都得重新生成。出于這個原因,我們要求動態(tài)內容通過一些方法或函數(shù)生成。
調用CController::renderDynamic()在你想的地方插入動態(tài)內容。

...別的HTML內容...
<?php if($this->beginCache($id)) { ?>
...被緩存的片段內容...
    <?php $this->renderDynamic($callback); ?>
...被緩存的片段內容...
<?php $this->endCache(); } ?>
...別的HTML內容...

在上面的, $callback指的是有效的PHP回調。它可以是指向當前控制器類的方法或者全局函數(shù)的字符串名。它也可以是一個數(shù)組名指向一個類的方法。其他任何的參數(shù),將傳遞到renderDynamic()方法中。回調將返回動態(tài)內容而不是僅僅顯示它。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,698評論 6 539
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,202評論 3 426
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,742評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,580評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,297評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,688評論 1 327
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,693評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,875評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,438評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,183評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,384評論 1 372
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,931評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,612評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,022評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,297評論 1 292
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,093評論 3 397
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,330評論 2 377

推薦閱讀更多精彩內容

  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,816評論 18 139
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,702評論 25 708
  • 發(fā)現(xiàn) 關注 消息 iOS 第三方庫、插件、知名博客總結 作者大灰狼的小綿羊哥哥關注 2017.06.26 09:4...
    肇東周閱讀 12,161評論 4 61
  • 1.NSURLConnection使用 1.1 NSURLConnection同步請求(GET) (1)步驟1. ...
    小羊快跑閱讀 598評論 1 1
  • 簡書,我來了。
    PickNote閱讀 222評論 0 0