資料快取
============

資料快取即儲存一些 PHP 變數到快取中，以後再從快取中取出來。出於此目的，快取元件的基礎類別 [CCache]
提供了兩個最常用的方法： [set()|CCache::set] 和 [get()|CCache::get]。

要在快取中儲存一個變數 `$value` ，我們選擇一個唯一 ID 並調用 [set()|CCache::set] 儲存它：

~~~
[php]
Yii::app()->cache->set($id, $value);
~~~

快取的資料將一直留在快取中，除非它由於某些快取策略（例如快取空間已滿，舊的資料被刪除）而被清除。
要改變這種行為，我們可以在調用  [set()|CCache::set]  的同時提供一個過期參數，這樣在設定的時間段之後，快取資料將被清除：

~~~
[php]
// 值 $value 在快取中最多保留 30 秒
Yii::app()->cache->set($id, $value, 30);
~~~

稍後當我們需要存取此變數時（在同一個或不同的 Web 請求中），就可以通過 ID 調用 [get()|CCache::get] 從快取中將其取回。
如果返回的是 false，表示此值在快取中不可用，我們應該重新產生它。

~~~
[php]
$value=Yii::app()->cache->get($id);
if($value===false)
{
	// 因為在快取中沒找到 $value ，重新產生它 ，
	// 並將它存入快取以備以後使用：
	// Yii::app()->cache->set($id,$value);
}
~~~

為要存入快取的變數選擇 ID 時，要確保此 ID 對應用程式中所有其他存入快取的變數是唯一的。
而在不同的應用程式之間，這個 ID 不需要是唯一的。快取元件具有足夠的智慧區分不同應用程式中的 ID。

一些快取儲存裝置，例如 MemCache, APC, 支援以批次模式取得多個快取值。這可以減少取得快取資料時帶來的開銷。Yii 提供了一個名為 [mget()|CCache::mget] 的方法來完成此功能。如果底層快取儲存裝置不支援此功能，[mget()|CCache::mget] 依然可以模擬實現它。

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

> Tip|提示: 由於 [CCache] 實現了 `ArrayAccess`，快取元件也可以像一個陣列一樣使用。下面是幾個例子：
> ~~~
> [php]
> $cache=Yii::app()->cache;
> $cache['var1']=$value1;  // 相當於: $cache->set('var1',$value1);
> $value2=$cache['var2'];  // 相當於: $value2=$cache->get('var2');
> ~~~

快取相依性
----------------

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

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

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

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

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

   - [CFileCacheDependency]: 如果文件的最後修改時間發生改變，則相依性改變。

   - [CDirectoryCacheDependency]: 如果目錄和其子目錄中的文件發生改變，則相依性改變。

   - [CDbCacheDependency]: 如果指定 SQL 語句的查詢結果發生改變，則相依性改變。

   - [CGlobalStateCacheDependency]: 如果指定的全域狀態發生改變，則相依性改變。全域狀態是應用程式中的一個跨請求、跨會話的變數。它是通過 [CApplication::setGlobalState()] 定義的。

   - [CChainedCacheDependency]: 如果鏈中的任何相依性發生改變，則此相依性改變。

   - [CExpressionDependency]: 如果指定的 PHP 表達式的結果發生改變，則相依性改變。


查詢快取
-------------

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

> Info|提示: 某些資料庫（例如：[MySQL](http://dev.mysql.com/doc/refman/5.1/en/query-cache.html)）也支援查詢快取的功能。跟 MySQL 相比，Yii 提供更有彈性且更有效率的查詢快取。


### 啟用查詢快取

要啟用查詢快取，確認 [CDbConnection::queryCacheID] 指定到一個有效的快取應用程式元件（預設是 `cache`）。


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

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

~~~
[php]
$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 述句索引的一個項目。
- 如果該項目還沒過期（少於 1000 秒，從他被儲存在快取開始）
- 如果相依性還沒被改變（最大的 `update_time` 值跟查詢結果被儲存在快取時一樣）

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


### 一起使用 ActiveRecord 和查詢快取

查詢快取可以跟 [Active Record](/doc/guide/database.ar) 一起使用。為此，我們呼叫一個相似的 [CActiveRecord::cache()] 方法如下：

~~~
[php]
$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()` 方法（不論 [CDbConnection] 或 [CActiveRecord]），他會把下個要執行的 SQL 查詢標記快取。其他的 SQL 查詢就不會被快取，除非我們再次呼叫 `cache()`。例如，

~~~
[php]
$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()`，我們指定下兩個查詢也要被快取：

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

如你所知，當進行關聯式 AR 查詢，多個 SQL 查詢是有可能被執行的（藉由檢查 [log messages](/doc/guide/topics.logging)）。例如，如果 `Post` 和 `Comment` 的關係是 `HAS_MANY`，那麼下列的程式碼會實際上執行了兩個查詢：

- 首先選擇文章，限制數量 20；
- 再來選擇前面所選文章的評論。

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

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

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

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

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


### 限制

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

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


<div class="revision">$Id$</div>