這篇文章主要討論的問題是:如何為專案設計乙個完整而簡潔的快取系統。只講做法,不講原理。在我們專案中,使用到了三種方法,來保證了快取系統的有效簡潔。
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()就可以實現,如果物件呼叫某個方法,而這個方法又不存在,那麼就會呼叫到這個魔術方法了,具體實現**如下:
/****執行機制: 比如說有這樣的乙個呼叫關係:dao('static_ship')->get(),但是在static_ship這個類中沒有get()這個方法,於是程式就會執行__call(),在這個類中,有乙個這樣的方法__cache__get()這樣的乙個方法,於是我就執行了這個方法,並且把這個函式的資料快取起來了。這樣就達到了我們的目的,不改變函式內部的**,把函式的結果快取起來。* 呼叫魔術方法
** @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;
}
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)get方法的主要**如下:$data = $this->get($pk);
return isset($data[$field]) ? $data[$field] : null;
}
/*** 根據主鍵 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快取系統最主要的就是為了提高動態網頁的應用,分擔資料庫檢索的壓力,對於 流量比較大 的,...