PHP中常用的缓存技术介绍
数据缓存:这里所说的数据缓存是指数据库查询缓存,每次访问页面的时候,都会先检测相应的缓存数据是否存在,如果不存在,就连接数据库,得到数据,并把查询结果序列化后保存到文件中,以后同样的查询结果就直接从缓存文件中获得,代码如下:
<?php
$sql = 'SELECT * FROM users';
$key = md5($sql); //memcached 对象标识符
if (!($datas = $mc->get($key))) {
// 在 memcached 中未获取到缓存数据,则使用数据库查询获取记录集。
echo "\n" . str_pad('Read datas from MySQL.', 60, '_') . "\n";
$conn = mysql_connect('localhost', 'test', 'test');
mysql_select_db('test');
$result = mysql_query($sql);
while ($row = mysql_fetch_object($result)) $datas[] = $row;
// 将数据库中获取到的结果集数据保存到 memcached 中,以供下次访问时使用。
$mc->add($key, $datas);
} else {
echo "\n" . str_pad('Read datas from memcached.', 60, '_') . "\n";
}
var_dump($datas);
?>页面缓存:每次访问页面的时候,都会先检测相应的缓存页面文件是否存在,如果不存在,就连接数据库,得到数据,显示页面并同时生成缓存页面文件,这样下次访问的时候页面文件就发挥作用了,模板引擎和网上常见的一些缓存类通常有此功能,代码如下:
<?php
define('DIRECTORY_SEPARATOR', '/');
define('FOPEN_WRITE_CREATE_DESTRUCTIVE', 'wb');
define('FOPEN_WRITE_CREATE', 'ab');
define('DIR_WRITE_MODE', 0777);
class FileCache {
/**
* 缓存路径
*
* @access private
* @var string
*/
private $_cache_path;
/**
* 缓存过期时间,单位是秒second
*
* @access private
* @var int
*/
private $_cache_expire;
/**
* 解析函数,设置缓存过期实践和存储路径
*
* @access public
* @return void
*/
public function __construct($expire, $cache_path) {
$this->_cache_expire = $expire;
$this->_cache_path = $cache_path;
}
/**
* 缓存文件名
*
* @access public
* @param string $key
* @return void
*/
private function _file($key) {
return $this->_cache_path . md5($key);
}
/**
* 设置缓存
*
* @access public
* @param string $key 缓存的唯一键
* @param string $data 缓存的内容
* @return bool
*/
public function set($key, $data) {
$value = serialize($data);
$file = $this->_file($key);
return $this->write_file($file, $value);
}
/**
* 获取缓存
*
* @access public
* @param string $key 缓存的唯一键
* @return mixed
*/
public function get($key) {
$file = $this->_file($key);
/** 文件不存在或目录不可写 */
if (!file_exists($file) || !$this->is_really_writable($file)) {
return false;
}
/** 缓存没有过期,仍然可用 */
if (time() < (filemtime($file) + $this->_cache_expire)) {
$data = $this->read_file($file);
if (FALSE !== $data) {
return unserialize($data);
}
return FALSE;
}
/** 缓存过期,删除之 */
@unlink($file);
return FALSE;
}
function read_file($file) {
if (!file_exists($file)) {
return FALSE;
}
if (function_exists('file_get_contents')) {
return file_get_contents($file);
}
if (!$fp = @fopen($file, FOPEN_READ)) {
return FALSE;
}
flock($fp, LOCK_SH); //读取之前加上共享锁
$data = '';
if (filesize($file) > 0) {
$data = & fread($fp, filesize($file));
}
flock($fp, LOCK_UN); //释放锁
fclose($fp);
return $data;
}
function write_file($path, $data, $mode = FOPEN_WRITE_CREATE_DESTRUCTIVE) {
if (!$fp = @fopen($path, $mode)) {
return FALSE;
}
flock($fp, LOCK_EX);
fwrite($fp, $data);
flock($fp, LOCK_UN);
fclose($fp);
return TRUE;
}
function is_really_writable($file) //兼容各平台判断文件是否有写入权限
{
// If we're on a Unix server with safe_mode off we call is_writable
if (DIRECTORY_SEPARATOR == '/' AND @ini_get("safe_mode") == FALSE) {
return is_writable($file);
}
// For windows servers and safe_mode "on" installations we'll actually
// write a file then read it. Bah...
if (is_dir($file)) {
$file = rtrim($file, '/') . '/' . md5(rand(1, 100));
if (($fp = @fopen($file, FOPEN_WRITE_CREATE)) === FALSE) {
return FALSE;
}
fclose($fp);
@chmod($file, DIR_WRITE_MODE);
@unlink($file);
return TRUE;
} elseif (($fp = @fopen($file, FOPEN_WRITE_CREATE)) === FALSE) {
return FALSE;
}
fclose($fp);
return TRUE;
}
}
$cache = new FileCache(30, 'cache/');
$cache->set('test', 'this is a test.');
print $cache->get('test');
/* End of file FlieCache.php */
?>内存缓存:Memcached是高性能的,分布式的内存对象缓存系统,用于在动态应用中减少数据库负载,提升访问速度.
dbcached 是一款基于 Memcached 和 NMDB 的分布式 key-value 数据库内存缓存系统.
以上的缓存技术虽然能很好的解决频繁查询数据库的问题,但其缺点在在于数据无时效性,下面我给出我在项目中常用的方法,代码如下:
<?php
class MemcacheModel {
private $mc = null;
/**
* 构造方法,用于添加服务器并创建memcahced对象
*/
function __construct() {
$params = func_get_args();
$mc = new Memcache;
//如果有多个memcache服务器
if (count($params) > 1) {
foreach ($params as $v) {
call_user_func_array(array(
$mc,
'addServer'
) , $v);
}
//如果只有一个memcache服务器
} else {
call_user_func_array(array(
$mc,
'addServer'
) , $params[0]);
}
$this->mc = $mc;
}
/**
* 获取memcached对象
* @return object memcached对象
*/
function getMem() {
return $this->mc;
}
/**
* 检查mem是否连接成功
* @return bool 连接成功返回true,否则返回false
*/
function mem_connect_error() {
$stats = $this->mc->getStats();
if (emptyempty($stats)) {
return false;
} else {
return true;
}
}
private function addKey($tabName, $key) {
$keys = $this->mc->get($tabName);
if (emptyempty($keys)) {
$keys = array();
}
//如果key不存在,就添加一个
if (!in_array($key, $keys)) {
$keys[] = $key; //将新的key添加到本表的keys中
$this->mc->set($tabName, $keys, MEMCACHE_COMPRESSED, 0);
return true; //不存在返回true
} else {
return false; //存在返回false
}
}
/**
* 向memcache中添加数据
* @param string $tabName 需要缓存数据表的表名
* @param string $sql 使用sql作为memcache的key
* @param mixed $data 需要缓存的数据
*/
function addCache($tabName, $sql, $data) {
$key = md5($sql);
//如果不存在
if ($this->addKey($tabName, $key)) {
$this->mc->set($key, $data, MEMCACHE_COMPRESSED, 0);
}
}
/**
* 获取memcahce中保存的数据
* @param string $sql 使用SQL的key
* @return mixed 返回缓存中的数据
*/
function getCache($sql) {
$key = md5($sql);
return $this->mc->get($key);
}
/**
* 删除和同一个表相关的所有缓存
* @param string $tabName 数据表的表名
*/
function delCache($tabName) {
$keys = $this->mc->get($tabName);
//删除同一个表的所有缓存
if (!emptyempty($keys)) {
foreach ($keys as $key) {
$this->mc->delete($key, 0); //0 表示立刻删除
}
}
//删除表的所有sql的key
$this->mc->delete($tabName, 0);
}
/**
* 删除单独一个语句的缓存
* @param string $sql 执行的SQL语句
*/
function delone($sql) {
$key = md5($sql);
$this->mc->delete($key, 0); //0 表示立刻删除
}
}
?>时间触发缓存:检查文件是否存在并且时间戳小于设置的过期时间,如果文件修改的时间戳比当前时间戳减去过期时间戳大,那么就用缓存,否则更新缓存.
设定时间内不去判断数据是否要更新,过了设定时间再更新缓存,以上只适合对时效性要求不高的情况下使用,否则请看下面.
内容触发缓存:当插入数据或更新数据时,强制更新缓存.
在这里我们可以看到,当有大量数据频繁需要更新时,最后都要涉及磁盘读写操作,怎么解决呢?我在日常项目中,通常并不缓存所有内容,而是缓存一部分不经常变的内容来解决,但在大负荷的情况下,最好要用共享内存做缓存系统.
到这里PHP缓存也许有点解决方案了,但其缺点是,因为每次请求仍然要经过PHP解析,在大负荷的情况下效率问题还是比效严重,在这种情况下,也许会用到静态缓存.
静态缓存:这里所说的静态缓存是指HTML缓存,HTML缓存一般是无需判断数据是否要更新的,因为通常在使用HTML的场合一般是不经常变动内容的页面,数据更新的时候把HTML也强制更新一下就可以了.
也有像thinkphp的静态缓存,ThinkPHP官方手册写道静态规则的定义有三种方式,代码如下:
<?php Return Array( 'ActionName' => array( '静态规则', '静态缓存有效期', '附加规则' ) , //第一种 'ModuleName:ActionName' => array( '静态规则', '静态缓存有效期', '附加规则' ) , //第二种 '*' => array( '静态规则', '静态缓存有效期', '附加规则' ) , //第三种 …更多操作的静态规则 ) ?>
第一种是定义全局的操作静态规则,例如定义所有的read操作的静态规则为:'read'=>array('{id}','60').
其中,{id} 表示取$_GET['id'] 为静态缓存文件名,第二个参数表示缓存60秒.
第二种是定义某个模块的操作的静态规则,例如,我们需要定义Blog模块的read操作进行静态缓存.
'Blog:read'=>array('{id}',-1).
第三种方式是定义全局的静态缓存规则,这个属于特殊情况下的使用,任何模块的操作都适用,例如:
'*'=>array('{$_SERVER.REQUEST_URI|md5}'),根据当前的URL进行缓存。
我这里在静态缓存规则文件htmls.php中写,代码如下:
<?php
return array(
'getHtml' => array(
'{:action}', -1
) , //-1表示永久缓存 );
?>SMARTY缓存,代码如下:
<?php
require ('./smarty/Smarty.class.php');
$smarty = new Smarty;
$smarty->caching = true;
if (!$smarty->is_cached('index.tpl')) {
// No cache available, do variable assignments here.
$contents = get_database_contents();
$smarty->assign($contents);
}
$smarty->display('index.tpl');
?>本文地址:http://www.phprm.com/develop/fs4049.html
转载随意,但请附上文章地址:-)