— 实现读取缓存在数据访问层中抽离策略 一. 背景 使用数据缓存在web工程中是一个非常有意义的策略,不仅仅可以减少数据库负载,而且当数据缓存在内存中,能大大提高了的读取速度。 在WEB设计中,我们往往忽略一个重要的信息 – Key,每当我们需要缓存的时候
— 实现读取缓存在数据访问层中抽离策略
使用数据缓存在web工程中是一个非常有意义的策略,不仅仅可以减少数据库负载,而且当数据缓存在内存中,能大大提高了的读取速度。
在WEB设计中,我们往往忽略一个重要的信息 – Key,每当我们需要缓存的时候,只是单纯的对一个Key进行SET或者GET操作,时间长了,也许就出现了以下的问题:
这就造成了我们平时常见的一种场景,一位同事查到原因,高喊:
“Shit!为什么生产环境上的数据总是测试有问题,测试环境都好好的,线上一定是有缓存,但是我们怎么把它清除掉!“
先来看一个简单的例子:获取服务器的数据列表
// 这是一个读取服务器相关信息的数据访问类
Class ServerStore {
// 根据数量获取最新的测试服务器列表数据
public function fetchTestingList($limit);
}看到这个类基本所有人都知道fetchTestingList的方法体要这么写:
// 这是一个读取服务器相关信息的数据访问类
Class ServerStore {
// 根据数量获取最新的测试服务器列表数据
public function fetchTestingList($limit) {
// OK,开始找Cache
$cache_key = "ServerTestingList:{$limit}";
if ( FALSE !== $data_from_cache = Cache::getInstance()->get($cache_key) ) {
return $data_from_cache;
}
// Cache没找到,找数据表吧
$data = $this->db->select('id','name','time')
->from('server')
->limit($limit)
->fetchRow();
// 写缓存,给个600秒意思下吧
Cache::getInstance()->set($cache_key, $data, 600);
return $data;
}
}现在我们调用这个方法获取数据:
$ServerStore = new ServeStore(); $list = $ServerStore->fetchTestingList(4);
这个对象我们统一称之为Store(数据访问) 对象
看似习以为常的方法,却隐藏的几个重要的问题:
这个例子确实是开了一个玩笑,但事实上我们的生产环境上经常出现这样的问题,并不是因为我们的程序逻辑出错了,而是由于我们在一开始设计上就没有考虑到扩展性。
为什么我们将缓存写在Store对象内,如果写在Store对象外头可以吗?形如这般:
$ServerStore->ttl(600)->fetchTestingList(4); $ServerStore->nocache()->fetchTestingList(4);
那么这应该不是一个Store对象可以做到方式,我们不妨将它叫做Service(数据服务)对象:
$ServerService->ttl(600)->fetchTestingList(4); $ServerService->nocache()->fetchTestingList(4);
我们可以猜想到:
根据我们的猜想,画出UML图:

为了保证通用性我们将Cache和Service都继承一个抽象基类:

一切看似逻辑没什么异常了,可是我们Key的组合怎么处理呢?怎么创建这个Key?
先来处理第一个问题,从一开始的设计方案看,ServerStore中每个方法只会存在一个Key,也就是说这个Store中有多少个方法体,对应的就应该有多少个Key。
为此我们可以建立一个ServerCacheKey(数据缓存键)对象,里面的每个方法都应该匹配ServerStore中对应的方法。那么我们不妨设计一个接口iServer,然而ServerCacheKey,ServerCache以及ServerStore实现这个接口,通过这样的实现ServerCacheKey将也有fetchTodayTestingList这个方法,自然能通过对应的参数组合出需要的Key。
集企业自助建站、网络营销、商品推广于一体的系统 功能说明: 1、系统采用Microsoft SQL Server大型数据库支持,查询数据库用的全是存储过程,速度和性能极好。开发环境是vs.net,采用4层结构,具有很好的可维护性和可扩冲性。 2、用户注册和登陆 未注册用户只具备浏览商品、新闻和留言功能;要采购商品,需接受服务协议并填写相关注册信息成为正式用户后方可进行,以尽可能减少和避免无效
0

?????? 那么第二个问题,创建这个Key的工作可能出现的位置在:
有了接口的帮助,那么实现接口的类,它们的类名,方法,参数就被统一了,为此我们再建立一个CacheKeyCreater类通过这一些列的规则就可以创建指定的Key。
注:ServerCache可以继承抽象类Call魔术自动通过CacheKeyCreater实现的接口方法,进行读取数据缓存方法,因此我们这里移除ServerCache实现iServer的过程。

为了方便程序实现和调试,以下使用ThinkPHP框架模拟程序,将App下Lib目录结构做如下规划:
/Lib/Core:核心类文件,CacheKeyCreater, BaseService, BaseCache等放置处
/Lib/Interface: 接口类放置处
/Lib/Service: 服务类放置处
/Lib/Store: 数据访问类放置处
/Lib/Cache: 数据缓存类放置处
/Lib/CacheKey: 数据缓存键类放置处
使用ThinkPHP自带的D函数自动装载对应目录下类库,部分类库需要增加自定义的autoload功能。
// 服务器列表接口
Interface iServer {
// 获取今日开服列表
public function fetchTodayOpeningList($limit);
// 获取今日测试列表
public function fetchTodayTestingList($limit);
}/**
* APP Cache Key生成类
*
* @category Core
* @package Core
* @author Lancer <lancer.he@gmail.com>
* @since 2014-03-06
*/
Final Class CacheKeyCreater {
public static function create($class, $func, $args) {
$cachekey_class = $class . 'CacheKey';
return call_user_func_array(array($cachekey_class, $func), $args);
}
}/**
* APP Cache层抽象基类
*
* @category Core
* @package Core
* @author Lancer <lancer.he@gmail.com>
* @since 2014-03-06
*/
Abstract Class BaseCache {
public function __call($func, $args) {
$class = str_replace('Cache', '', get_class($this) );
$key = CacheKeyCreater::create($class, $func, $args);
return Cache::getInstance()->get($key);
}
}/**
* APP Service层抽象基类,自动选择Cache层/Store层
*
* @category Core
* @package Core
* @author Lancer <lancer.he@gmail.com>
* @since 2014-03-06
*/
Abstract Class BaseService {
protected $_cache_handler;
protected $_db_handler;
protected $_class_name;
protected $_layer_cache_tag = 'Cache';//Cache类放在Cache目录下
protected $_layer_store_tag = 'Store';//Store类放在Store对象下
public function __construct() {
$this->_class_name = str_replace('Service', '', get_class($this) );
$this->_cache_handler = D($this->_class_name, $this->_layer_cache_tag);
$this->_db_handler = D($this->_class_name, $this->_layer_store_tag);
}
public function ttl($ttl) {
$this->_cache_handler->ttl = $ttl;
return $this;
}
public function nocache() {
return $this->_db_handler;
}
public function __call($func, $args) {
$data_from_cache = call_user_func_array(array($this->_cache_handler, $func), $args);
if ( FALSE !== $data_from_cache )
return $data_from_cache;
$data = call_user_func_array(array($this->_db_handler, $func), $args);
$key = CacheKeyCreater::create($this->_class_name, $func, $args);
Cache::getInstance()->set($key, $data, $this->_cache_handler->ttl );
return $data;
}
}/**
* 服务器数据读取层
*
* @category Cache
* @package Core
* @author Lancer <lancer.he@gmail.com>
* @since 2014-03-02
*/
Class ServerStore implements iServer{
public function fetchTodayOpeningList($limit=4) {
$condition = array('status' => 1, 'audit' => 1 'opendate' => date('Y-m-d') );
return D('Server')->order('opentime DESC')
->where($condition)
->limit($limit);
->getField('id, game_id, name, opentime, statusval');
}
public function fetchTodayTestingList($limit=4) {
$condition = array('status' => 0, 'audit' => 1 'opendate' => date('Y-m-d') );
$pagination = array('limit' => 4);
return D('Server')->order('opentime DESC')
->where($condition)
->limit($limit);
->getField('id, game_id, name, opentime, statusval');
}
}/**
* 服务器列表缓存键类
*
* @category CacheKey
* @package Extend
* @author Lancer <lancer.he@gmail.com>
* @since 2014-03-02
*/
Class ServerCacheKey implements iServer {
public function fetchTodayOpeningList($limit) {
return 'ServerTodayOpeningList:' . $limit;
}
public function fetchTodayTestingList($limit) {
return 'ServerTodayTestingList:' . $limit;
}
}Class ServerCache extends BaseCache {}
Class ServerService extends BaseService {}测试过程:将设计的程序,我们大胆的在Action下做一个测试:
Class TestAction extends BaseAction {
public function cachetest() {
D('Server', 'Service')->ttl(600)->fetchTodayTestingList(4);
D('Server', 'Service')->nocache()->fetchTodayOpeningList(4);
$this->show(' ');
}
}测试结果:访问页面后在Memadmin查询:
测试结果:利用ThinkPHP自带的追踪SQL记录来查看下效果:
确实只有一条SQL查询,证明我们的设计和程序逻辑是没有问题。
通过设计模式分离出一个数据缓存层,解决了以下的问题:
原文地址:服务端需要一个新的分层 -“数据缓存层”, 感谢原作者分享。
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号