數(shù)據(jù)緩存(Data Caching)

2018-02-24 15:40 更新

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

數(shù)據(jù)緩存是指將一些 PHP 變量存儲(chǔ)到緩存中,使用時(shí)再?gòu)木彺嬷腥』?。它也是更高?jí)緩存特性的基礎(chǔ),例如查詢緩存內(nèi)容緩存。

如下代碼是一個(gè)典型的數(shù)據(jù)緩存使用模式。其中?$cache?指向緩存組件

// 嘗試從緩存中取回 $data 
$data = $cache->get($key);

if ($data === false) {

    // $data 在緩存中沒(méi)有找到,則重新計(jì)算它的值

    // 將 $data 存放到緩存供下次使用
    $cache->set($key, $data);
}

// 這兒 $data 可以使用了。

緩存組件

數(shù)據(jù)緩存需要緩存組件提供支持,它代表各種緩存存儲(chǔ)器,例如內(nèi)存,文件,數(shù)據(jù)庫(kù)。

緩存組件通常注冊(cè)為應(yīng)用程序組件,這樣它們就可以在全局進(jìn)行配置與訪問(wèn)。如下代碼演示了如何配置應(yīng)用程序組件?cache?使用兩個(gè)memcached?服務(wù)器:

'components' => [
    'cache' => [
        'class' => 'yii\caching\MemCache',
        'servers' => [
            [
                'host' => 'server1',
                'port' => 11211,
                'weight' => 100,
            ],
            [
                'host' => 'server2',
                'port' => 11211,
                'weight' => 50,
            ],
        ],
    ],
],

然后就可以通過(guò)?Yii::$app->cache?訪問(wèn)上面的緩存組件了。

由于所有緩存組件都支持同樣的一系列 API ,并不需要修改使用緩存的業(yè)務(wù)代碼就能直接替換為其他底層緩存組件,只需在應(yīng)用配置中重新配置一下就可以。例如,你可以將上述配置修改為使用 yii\caching\ApcCache:

'components' => [
    'cache' => [
        'class' => 'yii\caching\ApcCache',
    ],
],

Tip: 你可以注冊(cè)多個(gè)緩存組件,很多依賴緩存的類默認(rèn)調(diào)用名為?cache?的組件(例如 yii\web\UrlManager)。

支持的緩存存儲(chǔ)器

Yii 支持一系列緩存存儲(chǔ)器,概況如下:

  • yii\caching\ApcCache:使用 PHP?APC?擴(kuò)展。這個(gè)選項(xiàng)可以認(rèn)為是集中式應(yīng)用程序環(huán)境中(例如:?jiǎn)我环?wù)器,沒(méi)有獨(dú)立的負(fù)載均衡器等)最快的緩存方案。
  • yii\caching\DbCache:使用一個(gè)數(shù)據(jù)庫(kù)的表存儲(chǔ)緩存數(shù)據(jù)。要使用這個(gè)緩存,你必須創(chuàng)建一個(gè)與 yii\caching\DbCache::cacheTable 對(duì)應(yīng)的表。
  • yii\caching\DummyCache: 僅作為一個(gè)緩存占位符,不實(shí)現(xiàn)任何真正的緩存功能。這個(gè)組件的目的是為了簡(jiǎn)化那些需要查詢緩存有效性的代碼。例如,在開(kāi)發(fā)中如果服務(wù)器沒(méi)有實(shí)際的緩存支持,用它配置一個(gè)緩存組件。一個(gè)真正的緩存服務(wù)啟用后,可以再切換為使用相應(yīng)的緩存組件。兩種條件下你都可以使用同樣的代碼?Yii::$app->cache->get($key)?嘗試從緩存中取回?cái)?shù)據(jù)而不用擔(dān)心Yii::$app->cache?可能是?null
  • yii\caching\FileCache:使用標(biāo)準(zhǔn)文件存儲(chǔ)緩存數(shù)據(jù)。這個(gè)特別適用于緩存大塊數(shù)據(jù),例如一個(gè)整頁(yè)的內(nèi)容。
  • yii\caching\MemCache:使用 PHP?memcache?和?memcached?擴(kuò)展。這個(gè)選項(xiàng)被看作分布式應(yīng)用環(huán)境中(例如:多臺(tái)服務(wù)器,有負(fù)載均衡等)最快的緩存方案。
  • yii\redis\Cache:實(shí)現(xiàn)了一個(gè)基于?Redis?鍵值對(duì)存儲(chǔ)器的緩存組件(需要 redis 2.6.12 及以上版本的支持 )。
  • yii\caching\WinCache:使用 PHP?WinCache另可參考)擴(kuò)展.
  • yii\caching\XCache:使用 PHP?XCache擴(kuò)展。
  • yii\caching\ZendDataCache:使用?Zend Data Cache?作為底層緩存媒介。

Tip: 你可以在同一個(gè)應(yīng)用程序中使用不同的緩存存儲(chǔ)器。一個(gè)常見(jiàn)的策略是使用基于內(nèi)存的緩存存儲(chǔ)器存儲(chǔ)小而常用的數(shù)據(jù)(例如:統(tǒng)計(jì)數(shù)據(jù)),使用基于文件或數(shù)據(jù)庫(kù)的緩存存儲(chǔ)器存儲(chǔ)大而不太常用的數(shù)據(jù)(例如:網(wǎng)頁(yè)內(nèi)容)。

緩存 API

所有緩存組件都有同樣的基類 yii\caching\Cache ,因此都支持如下 API:

  • yii\caching\Cache::get():通過(guò)一個(gè)指定的鍵(key)從緩存中取回一項(xiàng)數(shù)據(jù)。如果該項(xiàng)數(shù)據(jù)不存在于緩存中或者已經(jīng)過(guò)期/失效,則返回值 false。
  • yii\caching\Cache::set():將一項(xiàng)數(shù)據(jù)指定一個(gè)鍵,存放到緩存中。
  • yii\caching\Cache::add():如果緩存中未找到該鍵,則將指定數(shù)據(jù)存放到緩存中。
  • yii\caching\Cache::mget():通過(guò)指定的多個(gè)鍵從緩存中取回多項(xiàng)數(shù)據(jù)。
  • yii\caching\Cache::mset():將多項(xiàng)數(shù)據(jù)存儲(chǔ)到緩存中,每項(xiàng)數(shù)據(jù)對(duì)應(yīng)一個(gè)鍵。
  • yii\caching\Cache::madd():將多項(xiàng)數(shù)據(jù)存儲(chǔ)到緩存中,每項(xiàng)數(shù)據(jù)對(duì)應(yīng)一個(gè)鍵。如果某個(gè)鍵已經(jīng)存在于緩存中,則該項(xiàng)數(shù)據(jù)會(huì)被跳過(guò)。
  • yii\caching\Cache::exists():返回一個(gè)值,指明某個(gè)鍵是否存在于緩存中。
  • yii\caching\Cache::delete():通過(guò)一個(gè)鍵,刪除緩存中對(duì)應(yīng)的值。
  • yii\caching\Cache::flush():刪除緩存中的所有數(shù)據(jù)。

有些緩存存儲(chǔ)器如 MemCache,APC 支持以批量模式取回緩存值,這樣可以節(jié)省取回緩存數(shù)據(jù)的開(kāi)支。 yii\caching\Cache::mget() 和 yii\caching\Cache::madd() API提供對(duì)該特性的支持。如果底層緩存存儲(chǔ)器不支持該特性,Yii 也會(huì)模擬實(shí)現(xiàn)。

由于 yii\caching\Cache 實(shí)現(xiàn)了 PHP?ArrayAccess?接口,緩存組件也可以像數(shù)組那樣使用,下面是幾個(gè)例子:

$cache['var1'] = $value1;  // 等價(jià)于: $cache->set('var1', $value1);
$value2 = $cache['var2'];  // 等價(jià)于: $value2 = $cache->get('var2');

緩存鍵

存儲(chǔ)在緩存中的每項(xiàng)數(shù)據(jù)都通過(guò)鍵作唯一識(shí)別。當(dāng)你在緩存中存儲(chǔ)一項(xiàng)數(shù)據(jù)時(shí),必須為它指定一個(gè)鍵,稍后從緩存中取回?cái)?shù)據(jù)時(shí),也需要提供相應(yīng)的鍵。

你可以使用一個(gè)字符串或者任意值作為一個(gè)緩存鍵。當(dāng)鍵不是一個(gè)字符串時(shí),它將會(huì)自動(dòng)被序列化為一個(gè)字符串。

定義一個(gè)緩存鍵常見(jiàn)的一個(gè)策略就是在一個(gè)數(shù)組中包含所有的決定性因素。例如,yii\db\Schema 使用如下鍵存儲(chǔ)一個(gè)數(shù)據(jù)表的結(jié)構(gòu)信息。

[
    __CLASS__,              // 結(jié)構(gòu)類名
    $this->db->dsn,         // 數(shù)據(jù)源名稱
    $this->db->username,    // 數(shù)據(jù)庫(kù)登錄用戶名
    $name,                  // 表名
];

如你所見(jiàn),該鍵包含了可唯一指定一個(gè)數(shù)據(jù)庫(kù)表所需的所有必要信息。

當(dāng)同一個(gè)緩存存儲(chǔ)器被用于多個(gè)不同的應(yīng)用時(shí),應(yīng)該為每個(gè)應(yīng)用指定一個(gè)唯一的緩存鍵前綴以避免緩存鍵沖突??梢酝ㄟ^(guò)配置 yii\caching\Cache::keyPrefix 屬性實(shí)現(xiàn)。例如,在應(yīng)用配置中可以編寫(xiě)如下代碼:

'components' => [
    'cache' => [
        'class' => 'yii\caching\ApcCache',
        'keyPrefix' => 'myapp',       // 唯一鍵前綴
    ],
],

為了確?;ネㄐ?,此處只能使用字母和數(shù)字。

緩存過(guò)期

默認(rèn)情況下,緩存中的數(shù)據(jù)會(huì)永久存留,除非它被某些緩存策略強(qiáng)制移除(例如:緩存空間已滿,最老的數(shù)據(jù)會(huì)被移除)。要改變此特性,你可以在調(diào)用 yii\caching\Cache::set() 存儲(chǔ)一項(xiàng)數(shù)據(jù)時(shí)提供一個(gè)過(guò)期時(shí)間參數(shù)。該參數(shù)代表這項(xiàng)數(shù)據(jù)在緩存中可保持有效多少秒。當(dāng)你調(diào)用 yii\caching\Cache::get() 取回?cái)?shù)據(jù)時(shí),如果它已經(jīng)過(guò)了超時(shí)時(shí)間,該方法將返回 false,表明在緩存中找不到這項(xiàng)數(shù)據(jù)。例如:

// 將數(shù)據(jù)在緩存中保留 45 秒
$cache->set($key, $data, 45);

sleep(50);

$data = $cache->get($key);
if ($data === false) {
    // $data 已過(guò)期,或者在緩存中找不到
}

緩存依賴

除了超時(shí)設(shè)置,緩存數(shù)據(jù)還可能受到緩存依賴的影響而失效。例如,yii\caching\FileDependency 代表對(duì)一個(gè)文件修改時(shí)間的依賴。這個(gè)依賴條件發(fā)生變化也就意味著相應(yīng)的文件已經(jīng)被修改。因此,緩存中任何過(guò)期的文件內(nèi)容都應(yīng)該被置為失效狀態(tài),對(duì) yii\caching\Cache::get() 的調(diào)用都應(yīng)該返回 false。

緩存依賴用 yii\caching\Dependency 的派生類所表示。當(dāng)調(diào)用 yii\caching\Cache::set() 在緩存中存儲(chǔ)一項(xiàng)數(shù)據(jù)時(shí),可以同時(shí)傳遞一個(gè)關(guān)聯(lián)的緩存依賴對(duì)象。例如:

// 創(chuàng)建一個(gè)對(duì) example.txt 文件修改時(shí)間的緩存依賴
$dependency = new \yii\caching\FileDependency(['fileName' => 'example.txt']);

// 緩存數(shù)據(jù)將在30秒后超時(shí)
// 如果 example.txt 被修改,它也可能被更早地置為失效狀態(tài)。
$cache->set($key, $data, 30, $dependency);

// 緩存會(huì)檢查數(shù)據(jù)是否已超時(shí)。
// 它還會(huì)檢查關(guān)聯(lián)的依賴是否已變化。
// 符合任何一個(gè)條件時(shí)都會(huì)返回 false。
$data = $cache->get($key);

下面是可用的緩存依賴的概況:

  • yii\caching\ChainedDependency:如果依賴鏈上任何一個(gè)依賴產(chǎn)生變化,則依賴改變。
  • yii\caching\DbDependency:如果指定 SQL 語(yǔ)句的查詢結(jié)果發(fā)生了變化,則依賴改變。
  • yii\caching\ExpressionDependency:如果指定的 PHP 表達(dá)式執(zhí)行結(jié)果發(fā)生變化,則依賴改變。
  • yii\caching\FileDependency:如果文件的最后修改時(shí)間發(fā)生變化,則依賴改變。
  • yii\caching\GroupDependency:將一項(xiàng)緩存數(shù)據(jù)標(biāo)記到一個(gè)組名,你可以通過(guò)調(diào)用 yii\caching\GroupDependency::invalidate() 一次性將相同組名的緩存全部置為失效狀態(tài)。

查詢緩存

查詢緩存是一個(gè)建立在數(shù)據(jù)緩存之上的特殊緩存特性。它用于緩存數(shù)據(jù)庫(kù)查詢的結(jié)果。

查詢緩存需要一個(gè) yii\db\Connection 和一個(gè)有效的?cache?應(yīng)用組件。查詢緩存的基本用法如下,假設(shè)?$db?是一個(gè) yii\db\Connection 實(shí)例:

$duration = 60;     // 緩存查詢結(jié)果60秒
$dependency = ...;  // 可選的緩存依賴

$db->beginCache($duration, $dependency);

// ...這兒執(zhí)行數(shù)據(jù)庫(kù)查詢...

$db->endCache();

如你所見(jiàn),beginCache()?和?endCache()?中間的任何查詢結(jié)果都會(huì)被緩存起來(lái)。如果緩存中找到了同樣查詢的結(jié)果,則查詢會(huì)被跳過(guò),直接從緩存中提取結(jié)果。

查詢緩存可以用于?ActiveRecord?和?DAO。

Info: 有些 DBMS (例如:MySQL)也支持?jǐn)?shù)據(jù)庫(kù)服務(wù)器端的查詢緩存。你可以選擇使用任一查詢緩存機(jī)制。上文所述的查詢緩存的好處在于你可以指定更靈活的緩存依賴因此可能更加高效。

配置

查詢緩存有兩個(gè)通過(guò) yii\db\Connection 設(shè)置的配置項(xiàng):

  • yii\db\Connection::queryCacheDuration: 查詢結(jié)果在緩存中的有效期,以秒表示。如果在調(diào)用 yii\db\Connection::beginCache() 時(shí)傳遞了一個(gè)顯式的時(shí)值參數(shù),則配置中的有效期時(shí)值會(huì)被覆蓋。
  • yii\db\Connection::queryCache: 緩存應(yīng)用組件的 ID。默認(rèn)為?'cache'。只有在設(shè)置了一個(gè)有效的緩存應(yīng)用組件時(shí),查詢緩存才會(huì)有效。

限制條件

當(dāng)查詢結(jié)果中含有資源句柄時(shí),查詢緩存無(wú)法使用。例如,在有些 DBMS 中使用了?BLOB?列的時(shí)候,緩存結(jié)果會(huì)為該數(shù)據(jù)列返回一個(gè)資源句柄。

有些緩存存儲(chǔ)器有大小限制。例如,memcache 限制每條數(shù)據(jù)最大為 1MB。因此,如果查詢結(jié)果的大小超出了該限制,則會(huì)導(dǎo)致緩存失敗。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)