Heim  >  Artikel  >  php教程  >  PHP中常用的缓存技术介绍

PHP中常用的缓存技术介绍

WBOY
WBOYOriginal
2016-05-25 16:43:35948Durchsuche

数据缓存:这里所说的数据缓存是指数据库查询缓存,每次访问页面的时候,都会先检测相应的缓存数据是否存在,如果不存在,就连接数据库,得到数据,并把查询结果序列化后保存到文件中,以后同样的查询结果就直接从缓存文件中获得,代码如下:

<?php
$sql = &#39;SELECT * FROM users&#39;;
$key = md5($sql); //memcached 对象标识符
if (!($datas = $mc->get($key))) {
    //    在 memcached 中未获取到缓存数据,则使用数据库查询获取记录集。
    echo "\n" . str_pad(&#39;Read datas from MySQL.&#39;, 60, &#39;_&#39;) . "\n";
    $conn = mysql_connect(&#39;localhost&#39;, &#39;test&#39;, &#39;test&#39;);
    mysql_select_db(&#39;test&#39;);
    $result = mysql_query($sql);
    while ($row = mysql_fetch_object($result)) $datas[] = $row;
    //    将数据库中获取到的结果集数据保存到 memcached 中,以供下次访问时使用。
    $mc->add($key, $datas);
} else {
    echo "\n" . str_pad(&#39;Read datas from memcached.&#39;, 60, &#39;_&#39;) . "\n";
}
var_dump($datas);
?>

页面缓存:每次访问页面的时候,都会先检测相应的缓存页面文件是否存在,如果不存在,就连接数据库,得到数据,显示页面并同时生成缓存页面文件,这样下次访问的时候页面文件就发挥作用了,模板引擎和网上常见的一些缓存类通常有此功能,代码如下:

<?php
define(&#39;DIRECTORY_SEPARATOR&#39;, &#39;/&#39;);
define(&#39;FOPEN_WRITE_CREATE_DESTRUCTIVE&#39;, &#39;wb&#39;);
define(&#39;FOPEN_WRITE_CREATE&#39;, &#39;ab&#39;);
define(&#39;DIR_WRITE_MODE&#39;, 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(&#39;file_get_contents&#39;)) {
            return file_get_contents($file);
        }
        if (!$fp = @fopen($file, FOPEN_READ)) {
            return FALSE;
        }
        flock($fp, LOCK_SH); //读取之前加上共享锁
        $data = &#39;&#39;;
        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&#39;re on a Unix server with safe_mode off we call is_writable
        if (DIRECTORY_SEPARATOR == &#39;/&#39; AND @ini_get("safe_mode") == FALSE) {
            return is_writable($file);
        }
        // For windows servers and safe_mode "on" installations we&#39;ll actually
        // write a file then read it.  Bah...
        if (is_dir($file)) {
            $file = rtrim($file, &#39;/&#39;) . &#39;/&#39; . 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, &#39;cache/&#39;);
$cache->set(&#39;test&#39;, &#39;this is a test.&#39;);
print $cache->get(&#39;test&#39;);
/* 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,
                    &#39;addServer&#39;
                ) , $v);
            }
            //如果只有一个memcache服务器
            
        } else {
            call_user_func_array(array(
                $mc,
                &#39;addServer&#39;
            ) , $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(
    &#39;ActionName&#39; => array(
        &#39;静态规则&#39;,
        &#39;静态缓存有效期&#39;,
        &#39;附加规则&#39;
    ) , //第一种
    &#39;ModuleName:ActionName&#39; => array(
        &#39;静态规则&#39;,
        &#39;静态缓存有效期&#39;,
        &#39;附加规则&#39;
    ) , //第二种
    &#39;*&#39; => array(
        &#39;静态规则&#39;,
        &#39;静态缓存有效期&#39;,
        &#39;附加规则&#39;
    ) , //第三种
    …更多操作的静态规则
)
?>

第一种是定义全局的操作静态规则,例如定义所有的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(
    &#39;getHtml&#39; => array(
        &#39;{:action}&#39;, -1
    ) , //-1表示永久缓存  );
     ?>

SMARTY缓存,代码如下:

<?php
    require (&#39;./smarty/Smarty.class.php&#39;);
    $smarty = new Smarty;
    $smarty->caching = true;
    if (!$smarty->is_cached(&#39;index.tpl&#39;)) {
        // No cache available, do variable assignments here.
        $contents = get_database_contents();
        $smarty->assign($contents);
    }
    $smarty->display(&#39;index.tpl&#39;);
?>


本文地址:

转载随意,但请附上文章地址:-)

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn