資料快取 ============ 資料快取即儲存一些 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。因此,如果查詢結果的大小超出這個限制,快取會失敗。