# 數據緩存
數據緩存是指將一些 PHP 變量存儲到緩存中,使用時再從緩存中取回。它也是更高級緩存特性的基礎,例如[查詢緩存](http://www.yiichina.com/doc/guide/2.0/caching-data#query-caching)和[內容緩存](http://www.yiichina.com/doc/guide/2.0/caching-content)。
如下代碼是一個典型的數據緩存使用模式。其中?`$cache`?指向[緩存組件](http://www.yiichina.com/doc/guide/2.0/caching-data#cache-components):
~~~
// 嘗試從緩存中取回 $data
$data = $cache->get($key);
if ($data === false) {
// $data 在緩存中沒有找到,則重新計算它的值
// 將 $data 存放到緩存供下次使用
$cache->set($key, $data);
}
// 這兒 $data 可以使用了。
~~~
## 緩存組件
數據緩存需要**緩存組件**提供支持,它代表各種緩存存儲器,例如內存,文件,數據庫。
緩存組件通常注冊為應用程序組件,這樣它們就可以在全局進行配置與訪問。如下代碼演示了如何配置應用程序組件?`cache`?使用兩個[memcached](http://memcached.org/)?服務器:
~~~
'components' => [
'cache' => [
'class' => 'yii\caching\MemCache',
'servers' => [
[
'host' => 'server1',
'port' => 11211,
'weight' => 100,
],
[
'host' => 'server2',
'port' => 11211,
'weight' => 50,
],
],
],
],
~~~
然后就可以通過?`Yii::$app->cache`?訪問上面的緩存組件了。
由于所有緩存組件都支持同樣的一系列 API ,并不需要修改使用緩存的業務代碼就能直接替換為其他底層緩存組件,只需在應用配置中重新配置一下就可以。例如,你可以將上述配置修改為使用 yii\caching\ApcCache:
~~~
'components' => [
'cache' => [
'class' => 'yii\caching\ApcCache',
],
],
~~~
> Tip: 你可以注冊多個緩存組件,很多依賴緩存的類默認調用名為?`cache`?的組件(例如 yii\web\UrlManager)。
### 支持的緩存存儲器
Yii 支持一系列緩存存儲器,概況如下:
* yii\caching\ApcCache:使用 PHP?[APC](http://php.net/manual/en/book.apc.php)?擴展。這個選項可以認為是集中式應用程序環境中(例如:單一服務器,沒有獨立的負載均衡器等)最快的緩存方案。
* yii\caching\DbCache:使用一個數據庫的表存儲緩存數據。要使用這個緩存,你必須創建一個與 yii\caching\DbCache::cacheTable 對應的表。
* yii\caching\DummyCache: 僅作為一個緩存占位符,不實現任何真正的緩存功能。這個組件的目的是為了簡化那些需要查詢緩存有效性的代碼。例如,在開發中如果服務器沒有實際的緩存支持,用它配置一個緩存組件。一個真正的緩存服務啟用后,可以再切換為使用相應的緩存組件。兩種條件下你都可以使用同樣的代碼?`Yii::$app->cache->get($key)`?嘗試從緩存中取回數據而不用擔心`Yii::$app->cache`?可能是?`null`。
* yii\caching\FileCache:使用標準文件存儲緩存數據。這個特別適用于緩存大塊數據,例如一個整頁的內容。
* yii\caching\MemCache:使用 PHP?[memcache](http://php.net/manual/en/book.memcache.php)?和?[memcached](http://php.net/manual/en/book.memcached.php)?擴展。這個選項被看作分布式應用環境中(例如:多臺服務器,有負載均衡等)最快的緩存方案。
* yii\redis\Cache:實現了一個基于?[Redis](http://redis.io/)?鍵值對存儲器的緩存組件(需要 redis 2.6.12 及以上版本的支持 )。
* yii\caching\WinCache:使用 PHP?[WinCache](http://iis.net/downloads/microsoft/wincache-extension)([另可參考](http://php.net/manual/en/book.wincache.php))擴展.
* yii\caching\XCache:使用 PHP?[XCache](http://xcache.lighttpd.net/)擴展。
* yii\caching\ZendDataCache:使用?[Zend Data Cache](http://files.zend.com/help/Zend-Server-6/zend-server.htm#data_cache_component.htm)?作為底層緩存媒介。
> Tip: 你可以在同一個應用程序中使用不同的緩存存儲器。一個常見的策略是使用基于內存的緩存存儲器存儲小而常用的數據(例如:統計數據),使用基于文件或數據庫的緩存存儲器存儲大而不太常用的數據(例如:網頁內容)。
## 緩存 API
所有緩存組件都有同樣的基類 yii\caching\Cache ,因此都支持如下 API:
* yii\caching\Cache::get():通過一個指定的鍵(key)從緩存中取回一項數據。如果該項數據不存在于緩存中或者已經過期/失效,則返回值 false。
* yii\caching\Cache::set():將一項數據指定一個鍵,存放到緩存中。
* yii\caching\Cache::add():如果緩存中未找到該鍵,則將指定數據存放到緩存中。
* yii\caching\Cache::mget():通過指定的多個鍵從緩存中取回多項數據。
* yii\caching\Cache::mset():將多項數據存儲到緩存中,每項數據對應一個鍵。
* yii\caching\Cache::madd():將多項數據存儲到緩存中,每項數據對應一個鍵。如果某個鍵已經存在于緩存中,則該項數據會被跳過。
* yii\caching\Cache::exists():返回一個值,指明某個鍵是否存在于緩存中。
* yii\caching\Cache::delete():通過一個鍵,刪除緩存中對應的值。
* yii\caching\Cache::flush():刪除緩存中的所有數據。
有些緩存存儲器如 MemCache,APC 支持以批量模式取回緩存值,這樣可以節省取回緩存數據的開支。 yii\caching\Cache::mget() 和 yii\caching\Cache::madd() API提供對該特性的支持。如果底層緩存存儲器不支持該特性,Yii 也會模擬實現。
由于 yii\caching\Cache 實現了 PHP?`ArrayAccess`?接口,緩存組件也可以像數組那樣使用,下面是幾個例子:
~~~
$cache['var1'] = $value1; // 等價于: $cache->set('var1', $value1);
$value2 = $cache['var2']; // 等價于: $value2 = $cache->get('var2');
~~~
### 緩存鍵
存儲在緩存中的每項數據都通過鍵作唯一識別。當你在緩存中存儲一項數據時,必須為它指定一個鍵,稍后從緩存中取回數據時,也需要提供相應的鍵。
你可以使用一個字符串或者任意值作為一個緩存鍵。當鍵不是一個字符串時,它將會自動被序列化為一個字符串。
定義一個緩存鍵常見的一個策略就是在一個數組中包含所有的決定性因素。例如,yii\db\Schema 使用如下鍵存儲一個數據表的結構信息。
~~~
[
__CLASS__, // 結構類名
$this->db->dsn, // 數據源名稱
$this->db->username, // 數據庫登錄用戶名
$name, // 表名
];
~~~
如你所見,該鍵包含了可唯一指定一個數據庫表所需的所有必要信息。
當同一個緩存存儲器被用于多個不同的應用時,應該為每個應用指定一個唯一的緩存鍵前綴以避免緩存鍵沖突。可以通過配置 yii\caching\Cache::keyPrefix 屬性實現。例如,在應用配置中可以編寫如下代碼:
~~~
'components' => [
'cache' => [
'class' => 'yii\caching\ApcCache',
'keyPrefix' => 'myapp', // 唯一鍵前綴
],
],
~~~
為了確保互通性,此處只能使用字母和數字。
### 緩存過期
默認情況下,緩存中的數據會永久存留,除非它被某些緩存策略強制移除(例如:緩存空間已滿,最老的數據會被移除)。要改變此特性,你可以在調用 yii\caching\Cache::set() 存儲一項數據時提供一個過期時間參數。該參數代表這項數據在緩存中可保持有效多少秒。當你調用 yii\caching\Cache::get() 取回數據時,如果它已經過了超時時間,該方法將返回 false,表明在緩存中找不到這項數據。例如:
~~~
// 將數據在緩存中保留 45 秒
$cache->set($key, $data, 45);
sleep(50);
$data = $cache->get($key);
if ($data === false) {
// $data 已過期,或者在緩存中找不到
}
~~~
### 緩存依賴
除了超時設置,緩存數據還可能受到**緩存依賴**的影響而失效。例如,yii\caching\FileDependency 代表對一個文件修改時間的依賴。這個依賴條件發生變化也就意味著相應的文件已經被修改。因此,緩存中任何過期的文件內容都應該被置為失效狀態,對 yii\caching\Cache::get() 的調用都應該返回 false。
緩存依賴用 yii\caching\Dependency 的派生類所表示。當調用 yii\caching\Cache::set() 在緩存中存儲一項數據時,可以同時傳遞一個關聯的緩存依賴對象。例如:
~~~
// 創建一個對 example.txt 文件修改時間的緩存依賴
$dependency = new \yii\caching\FileDependency(['fileName' => 'example.txt']);
// 緩存數據將在30秒后超時
// 如果 example.txt 被修改,它也可能被更早地置為失效狀態。
$cache->set($key, $data, 30, $dependency);
// 緩存會檢查數據是否已超時。
// 它還會檢查關聯的依賴是否已變化。
// 符合任何一個條件時都會返回 false。
$data = $cache->get($key);
~~~
下面是可用的緩存依賴的概況:
* yii\caching\ChainedDependency:如果依賴鏈上任何一個依賴產生變化,則依賴改變。
* yii\caching\DbDependency:如果指定 SQL 語句的查詢結果發生了變化,則依賴改變。
* yii\caching\ExpressionDependency:如果指定的 PHP 表達式執行結果發生變化,則依賴改變。
* yii\caching\FileDependency:如果文件的最后修改時間發生變化,則依賴改變。
* yii\caching\GroupDependency:將一項緩存數據標記到一個組名,你可以通過調用 yii\caching\GroupDependency::invalidate() 一次性將相同組名的緩存全部置為失效狀態。
## 查詢緩存
查詢緩存是一個建立在數據緩存之上的特殊緩存特性。它用于緩存數據庫查詢的結果。
查詢緩存需要一個 yii\db\Connection 和一個有效的?`cache`?應用組件。查詢緩存的基本用法如下,假設?`$db`?是一個 yii\db\Connection 實例:
~~~
$duration = 60; // 緩存查詢結果60秒
$dependency = ...; // 可選的緩存依賴
$db->beginCache($duration, $dependency);
// ...這兒執行數據庫查詢...
$db->endCache();
~~~
如你所見,`beginCache()`?和?`endCache()`?中間的任何查詢結果都會被緩存起來。如果緩存中找到了同樣查詢的結果,則查詢會被跳過,直接從緩存中提取結果。
查詢緩存可以用于?[ActiveRecord](http://www.yiichina.com/doc/guide/2.0/db-active-record)?和?[DAO](http://www.yiichina.com/doc/guide/2.0/db-dao)。
> Info: 有些 DBMS (例如:[MySQL](http://dev.mysql.com/doc/refman/5.1/en/query-cache.html))也支持數據庫服務器端的查詢緩存。你可以選擇使用任一查詢緩存機制。上文所述的查詢緩存的好處在于你可以指定更靈活的緩存依賴因此可能更加高效。
### 配置
查詢緩存有兩個通過 yii\db\Connection 設置的配置項:
* yii\db\Connection::queryCacheDuration: 查詢結果在緩存中的有效期,以秒表示。如果在調用 yii\db\Connection::beginCache() 時傳遞了一個顯式的時值參數,則配置中的有效期時值會被覆蓋。
* yii\db\Connection::queryCache: 緩存應用組件的 ID。默認為?`'cache'`。只有在設置了一個有效的緩存應用組件時,查詢緩存才會有效。
### 限制條件
當查詢結果中含有資源句柄時,查詢緩存無法使用。例如,在有些 DBMS 中使用了?`BLOB`?列的時候,緩存結果會為該數據列返回一個資源句柄。
有些緩存存儲器有大小限制。例如,memcache 限制每條數據最大為 1MB。因此,如果查詢結果的大小超出了該限制,則會導致緩存失敗。
- 介紹(Introduction)
- 關于 Yii(About Yii)
- 從 Yii 1.1 升級(Upgrading from Version 1.1)
- 入門(Getting Started)
- 安裝 Yii(Installing Yii)
- 運行應用(Running Applications)
- 第一次問候(Saying Hello)
- 使用 Forms(Working with Forms)
- 玩轉 Databases(Working with Databases)
- 用 Gii 生成代碼(Generating Code with Gii)
- 更上一層樓(Looking Ahead)
- 應用結構(Application Structure)
- 結構概述(Overview)
- 入口腳本(Entry Scripts)
- 應用(Applications)
- 應用組件(Application Components)
- 控制器(Controllers)
- 模型(Models)
- 視圖(Views)
- 模塊(Modules)
- 過濾器(Filters)
- 小部件(Widgets)
- 前端資源(Assets)
- 擴展(Extensions)
- 請求處理(Handling Requests)
- 運行概述(Overview)
- 引導(Bootstrapping)
- 路由引導與創建 URL(Routing and URL Creation)
- 請求(Requests)
- 響應(Responses)
- Sessions and Cookies
- 錯誤處理(Handling Errors)
- 日志(Logging)
- 關鍵概念(Key Concepts)
- 組件(Components)
- 屬性(Properties)
- 事件(Events)
- 行為(Behaviors)
- 配置(Configurations)
- 別名(Aliases)
- 類自動加載(Class Autoloading)
- 服務定位器(Service Locator)
- 依賴注入容器(Dependency Injection Container)
- 配合數據庫工作(Working with Databases)
- 數據庫訪問(Data Access Objects): 數據庫連接、基本查詢、事務和模式操作
- 查詢生成器(Query Builder): 使用簡單抽象層查詢數據庫
- 活動記錄(Active Record): 活動記錄對象關系映射(ORM),檢索和操作記錄、定義關聯關系
- 數據庫遷移(Migrations): 在團體開發中對你的數據庫使用版本控制
- Sphinx
- Redis
- MongoDB
- ElasticSearch
- 接收用戶數據(Getting Data from Users)
- 創建表單(Creating Forms)
- 輸入驗證(Validating Input)
- 文件上傳(Uploading Files)
- 收集列表輸入(Collecting Tabular Input)
- 多模型同時輸入(Getting Data for Multiple Models)
- 顯示數據(Displaying Data)
- 格式化輸出數據(Data Formatting)
- 分頁(Pagination)
- 排序(Sorting)
- 數據提供器(Data Providers)
- 數據小部件(Data Widgets)
- 操作客戶端腳本(Working with Client Scripts)
- 主題(Theming)
- 安全(Security)
- 認證(Authentication)
- 授權(Authorization)
- 處理密碼(Working with Passwords)
- 客戶端認證(Auth Clients)
- 安全領域的最佳實踐(Best Practices)
- 緩存(Caching)
- 概述(Overview)
- 數據緩存(Data Caching)
- 片段緩存(Fragment Caching)
- 分頁緩存(Page Caching)
- HTTP 緩存(HTTP Caching)
- RESTful Web 服務
- 快速入門(Quick Start)
- 資源(Resources)
- 控制器(Controllers)
- 路由(Routing)
- 格式化響應(Response Formatting)
- 授權驗證(Authentication)
- 速率限制(Rate Limiting)
- 版本化(Versioning)
- 錯誤處理(Error Handling)
- 開發工具(Development Tools)
- 調試工具欄和調試器(Debug Toolbar and Debugger)
- 使用 Gii 生成代碼(Generating Code using Gii)
- TBD 生成 API 文檔(Generating API Documentation)
- 測試(Testing)
- 概述(Overview)
- 搭建測試環境(Testing environment setup)
- 單元測試(Unit Tests)
- 功能測試(Functional Tests)
- 驗收測試(Acceptance Tests)
- 測試夾具(Fixtures)
- 高級專題(Special Topics)
- 高級應用模版(Advanced Project Template)
- 從頭構建自定義模版(Building Application from Scratch)
- 控制臺命令(Console Commands)
- 核心驗證器(Core Validators)
- 國際化(Internationalization)
- 收發郵件(Mailing)
- 性能優化(Performance Tuning)
- 共享主機環境(Shared Hosting Environment)
- 模板引擎(Template Engines)
- 集成第三方代碼(Working with Third-Party Code)
- 小部件(Widgets)
- Bootstrap 小部件(Bootstrap Widgets)
- jQuery UI 小部件(jQuery UI Widgets)
- 助手類(Helpers)
- 助手一覽(Overview)
- Array 助手(ArrayHelper)
- Html 助手(Html)
- Url 助手(Url)