資料快取

資料快取即儲存一些 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, 支援以批次模式取得多個快取值。這可以減少取得快取資料時帶來的開銷。Yii 提供了一個名為 mget() 的方法來完成此功能。如果底層快取儲存裝置不支援此功能,mget() 依然可以模擬實現它。

要從快取中清除一個快取值,調用 delete(); 要清除快取中的所有資料,調用 flush()。 當調用 flush() 時一定要小心,因為它會同時清除其他應用程式中的快取。

提示: 由於 CCache 實現了 ArrayAccess,快取元件也可以像一個陣列一樣使用。下面是幾個例子:

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

快取相依性

除了過期設置,快取資料也可能會因為相依性條件發生變化而失效。例如,如果我們快取了某些文件的內容,而這些文件發生了改變,我們就應該讓快取的資料失效, 並從文件中讀取最新內容而不是從快取中讀取。

我們將一個相依性關係顯示為一個 CCacheDependency 或其子類別的實體。 當調用 set() 時,我們連同要快取的資料將其一同傳入。

// 此值將在 30 秒後失效
// 也可能因相依性的文件發生了變化而更快失效
Yii::app()->cache->set($id, $value, 30, new CFileCacheDependency('FileName'));

現在如果我們通過調用 get() 從快取中取得 $value ,相依性關係將被檢查,如果發生改變,我們將會得到一個 false 值,表示資料需要被重新產生。

下面是可用的快取相依性的簡要說明:

查詢快取

從版本 1.1.7,Yii 開始支援查詢快取。建立在資料快取上,查詢快取儲存資料庫查詢的結果在快取中,藉此省下未來同樣的資料庫查詢要求的時間,直接由快取提供。

提示: 某些資料庫(例如:MySQL)也支援查詢快取的功能。跟 MySQL 相比,Yii 提供更有彈性且更有效率的查詢快取。

啟用查詢快取

要啟用查詢快取,確認 CDbConnection::queryCacheID 指定到一個有效的快取應用程式元件(預設是 cache)。

一起使用資料存取物件和查詢快取

要使用查詢快取,呼叫 CDbConnection::cache() 方法當我們進行資料庫查詢。如下所示:

$sql = 'SELECT * FROM tbl_post LIMIT 20';
$dependency = new CDbCacheDependency('SELECT MAX(update_time) FROM tbl_post');
$rows = Yii::app()->db->cache(1000, $dependency)->createCommand($sql)->queryAll();

當執行上述述句,Yii 會先檢查快取是否包含有正要執行的 SQL 述句有效的快取結果。藉由下述三個條件驗證:

如果上述條件都符合,快取結果會被直接從快取中回傳。否則,SQL 述句會被送到資料庫執行,執行結果會被儲存在快取中再回傳。

一起使用 ActiveRecord 和查詢快取

查詢快取可以跟 Active Record 一起使用。為此,我們呼叫一個相似的 CActiveRecord::cache() 方法如下:

$dependency = new CDbCacheDependency('SELECT MAX(update_time) FROM tbl_post');
$posts = Post::model()->cache(1000, $dependency)->findAll();
// 關聯式 AR 查詢
$posts = Post::model()->cache(1000, $dependency)->with('author')->findAll();

這裡的 cache() 其實就是 CDbConnection::cache()。從內部觀察,當執行 AR 產生的 SQL 述句,Yii 會嘗試使用我們上述的查詢快取。

快取多個查詢

預設,每次我們呼叫 cache() 方法(不論 CDbConnectionCActiveRecord),他會把下個要執行的 SQL 查詢標記快取。其他的 SQL 查詢就不會被快取,除非我們再次呼叫 cache()。例如,

$sql = 'SELECT * FROM tbl_post LIMIT 20';
$dependency = new CDbCacheDependency('SELECT MAX(update_time) FROM tbl_post');
 
$rows = Yii::app()->db->cache(1000, $dependency)->createCommand($sql)->queryAll();
// 查詢快取不會被使用
$rows = Yii::app()->db->createCommand($sql)->queryAll();

藉由提供額外的 $queryCount 參數給 cache() 方法,我們可以強迫多個查詢使用查詢快取。下面的例子,當我們呼叫 cache(),我們指定下兩個查詢也要被快取:

// ...
$rows = Yii::app()->db->cache(1000, $dependency, 2)->createCommand($sql)->queryAll();
// 查詢快取會被使用
$rows = Yii::app()->db->createCommand($sql)->queryAll();

如你所知,當進行關聯式 AR 查詢,多個 SQL 查詢是有可能被執行的(藉由檢查 log messages)。例如,如果 PostComment 的關係是 HAS_MANY,那麼下列的程式碼會實際上執行了兩個查詢:

$posts = Post::model()->with('comments')->findAll(array(
    'limit'=>20,
));

如果我們使用查詢快取如下,那只有第一個查詢會被快取:

$posts = Post::model()->cache(1000, $dependency)->with('comments')->findAll(array(
    'limit'=>20,
));

為了快取這兩個查詢,我們需要提供額外的參數,說明有多少個資料庫查詢要快取:

$posts = Post::model()->cache(1000, $dependency, 2)->with('comments')->findAll(array(
    'limit'=>20,
));

限制

快取查詢沒辦法運作在包含有資源句柄的結果。例如,當使用 BLOB 欄位類型,某些資料庫會回傳包含有資源句柄的欄位資料。

某些快取裝置有大小限制。例如,memcache 限制每個項目最大的容量為 1MB。因此,如果查詢結果的大小超出這個限制,快取會失敗。

$Id$