使用memcache處理快取的三種方案

2021-09-07 05:54:40 字數 4020 閱讀 7938

這篇文章主要討論的問題是:如何為專案設計乙個完整而簡潔的快取系統。只講做法,不講原理。在我們專案中,使用到了三種方法,來保證了快取系統的有效簡潔。

1) 第一種,最常見的方式 讀取資料的主要步驟如下:

1)先從快取中獲取資料(如果在快取中獲取到,則直接返回已獲取的資料)

2)如果獲取不到,再從資料庫裡面讀取相應的資料

3)  把獲取到的資料加入快取中

注意:這種方式是在model層,也就是業務處理層加入的。

例項**如下:

public static function getcombatpowerrank()

$list = array();

// 遍歷所有使用者分庫,執行清理

for ($i = 1; $i <= dist_user_db_num; $i++)

}// 儲存到快取中

f('memcache')->set($cachekey, $list, c('rank_cache_time'));

return $list;

}

這種方式確實很好理解,有乙個弊端就是,所有的快取都需要手動的加上以上快取的**,需要修改函式的內部**。請注意,我們在專案中加入快取的時間是專案完成的差不多了,也就是說需要有很多這樣的「讀取類」函式加入快取,如果全是以上這種加入快取方式的話,需要修改很多函式的內部**,那絕對是乙個複雜而容易遺漏的苦力活。如果一不小心,就會出現錯誤。有沒有好的方式可以集中的給某些函式加入這樣的快取系統呢(如果有的話,絕對是乙個福音,哈哈)

2)第二種方式 ,在dao層集中處理。在解釋這種方法之前,我先簡要說明一下我們的需求,便於更好理解為什麼我可以這麼做。

在我們的遊戲專案中,有一部分資料時靜態資源資料,這種資料時配置好的,不會經常變動,每個使用者需要的都一樣。例如各種角色類的基礎的屬性,船隻的基礎屬性等。這類資料涉及到的操作一般是讀:把一張表全部讀出來,獲取根據某個條件讀取相應的內容。既然操作單一,我們就直接在dao層處理這類方法的快取。做法就是給每乙個dao類裡面的函式加入快取。

不改變方法的內部**,卻可以給每個方法加入快取,php魔術方法__call()就可以實現,如果物件呼叫某個方法,而這個方法又不存在,那麼就會呼叫到這個魔術方法了,具體實現**如下:

/**

* 呼叫魔術方法

** @param string $method

* @param mixed $args

* @return mixed

*/public function __call($method, $args)

$cachekey = md5($this->_dbname . ':' . $this->_tablename . ':' . $method . ':' . serialize($args));

$data = $this->_cache->get($cachekey);

if ($data === false)

return $data;

}

**執行機制: 比如說有這樣的乙個呼叫關係:dao('static_ship')->get(),但是在static_ship這個類中沒有get()這個方法,於是程式就會執行__call(),在這個類中,有乙個這樣的方法__cache__get()這樣的乙個方法,於是我就執行了這個方法,並且把這個函式的資料快取起來了。這樣就達到了我們的目的,不改變函式內部的**,把函式的結果快取起來。

3)集中處理和使用者有關的資料的快取。如果大家細心的話,可以發現方法2中快取的鍵值設計並不針對某乙個使用者。

$cachekey = md5($this->_dbname . ':' . $this->_tablename . ':' . $method . ':' . serialize($args)); 注意,這個鍵值的設計主要由庫名,表名,方法名,引數,需要注意的是庫名,因為如果資料庫涉及到分布式處理,就需要定位到相應的庫名中。如果需要快取的資料和使用者有關係,我該如何設計呢。

這個處理方式還是需要結合需求,在我們專案中,需要讀取「我的船」相應的資料。比如

1)我需要讀取我的船的攻擊力:getshipfieldbyusershipid($uid, $shipid, attack)

2) 我需要讀取船的防禦力 :getshipfieldbyusershipid($uid, $shipid, defence)

3) 讀取我的船的航海速度:getshipfieldbyusershipid($uid, $shipid, speed)

這個時候,有兩種sql查詢方法:

1) uid = $uid and shipid = $shipid and field=$field

2)   $data = " uid = $uid and shipid = $shipid " 然後再這個$data陣列中,返回相應的$data[$field].

你可能會覺得第二種方法會獲取到一些無用的資料,不好。但是,事實上,第二種方法比第一種方法好,因為他可以使用索引查詢,這個屬於sql優化的,暫且不討論,第二個原因是便於方法可以加入快取,查詢條件越「統一」,越容易加入快取。第三種做法也是在dao層中實現,快取方式正是基於查詢條件高度統一的原則:

public function getfield($pk, $field)

$data = $this->get($pk);

return isset($data[$field]) ? $data[$field] : null;

}

get方法的主要**如下:

/**

* 根據主鍵 fetchrow

** @param mixed $pk

* @return array

*/public function get($pk)

$cachekey = $this->_getrowcachekey($pk);

// 保證相同的靜態記錄只讀取一遍

if (isset($this->_rowdatas[$cachekey]))

$row = $this->_cache->get($cachekey);

if ($row === false)

return $row;

}

獲取快取鍵值的方法_getrowcachekey()實現方式如下:

// 獲取單條記錄快取key

protected function _getrowcachekey($pk)

else

return md5($this->_dbname . ':' . $this->_tablename . ':get:' . $pkstring);

}

保證查詢條件的高度統一,根據查詢的條件設定快取,就是第三中做法的精髓了。

快取系統需要注意的幾點:

1) 注意快取系統的關聯性,如果資料發生了變化,一定要更新快取 

2)如果被快取的資料和使用者有關,一定要把$cachekey處理好,保證每個使用者資料不會被其它使用者串改。特別需要注意的是分庫的時候uid=1可不止乙個哦

3)如果有必要的話,可以做乙個快取命中率的統計,統計哪些庫的那些表被哪些函式操作的次數

4) 如果某些表的資料頻繁的被修改,可以不需要快取,如果使用者的行文記錄表,_iscached 這個屬性就是用來控制是否需要快取。

見如下**:

/**

* 刪除(根據主鍵)

** @param mixed $pk

* @param array $extrawhere 格外的where條件

* @return bool

*/public function deletebypk($pk, array $extrawhere = array())

if (! $result = $this->where($where)->delete())

// 清理快取

if ($this->_iscached)

// 統計memcache讀寫次數

dao('massive_memcacherecord')->mark($this->_dbname, $this->_tablename, __method__, 1);

return $result;

}

使用memcache處理快取的三種方案

這篇文章主要討論的問題是 如何為專案設計乙個完整而簡潔的快取系統。只講做法,不講原理。在我們專案中,使用到了三種方法,來保證了快取系統的有效簡潔。1 第一種,最常見的方式 讀取資料的主要步驟如下 1 先從快取中獲取資料 如果在快取中獲取到,則直接返回已獲取的資料 2 如果獲取不到,再從資料庫裡面讀取...

rails使用memcache快取session

前提條件 安裝了memcache server以及rails的客戶端memcache client 修改environment.rb 找到config.action controller.session store active record store改為 config.action contro...

Memcache快取 總結

一.memcache快取 概念 memcache是乙個高效能的分布式的記憶體物件快取系統,通過在記憶體裡維護乙個統一的巨大的hash表 到記憶體中,然後從記憶體中訪問,從而大大提高讀取速度 應用 memcache快取系統最主要的就是為了提高動態網頁的應用,分擔資料庫檢索的壓力,對於 流量比較大 的,...