php做API后 由于API变动不大(题库),缓存用什么方案解决?
如题
正好有一个api通过redis缓存的接口:
//这里是api接口
/**
* @Route("/id={id}/blog", methods={"GET"}, name="bloggetinfo")
* @param int $id
* @return stdclass
*/
public function bloggetinfoAction($id = 1)
{
return $this->redisUtils->getCache(RedisUtils::$CACHEKEYS['ARTICLE']['ID'],'Souii\Controllers\ApiController::bloggetinfo',$id);
}
//这个是如果不缓存时,具体的业务逻辑
public static function bloggetinfo($id)
{
$atricle = Article::findFirst(
array(
"id = :id:",
'bind' => array('id' => $id)
)
);
$map = Tags::getAll();
$ret = [];
$tags = explode(',', $atricle->tags);
foreach ($tags as $tag) {
if (!empty($tag)) {
$ret[] = $map[$tag]['name'];
}
}
$atricle->tags = implode(',', $ret);
return $atricle;
}
<?php
namespace Souii\Redis;
/**
* Created by PhpStorm.
* User: zx
* Date: 2015/8/26
* Time: 9:27
*/
use Phalcon\Mvc\User\Component;
class RedisUtils extends Component{
/**
* redis的key前缀
* @var array
*/
public static $CACHEKEYS = array(
'ARTICLE' =>array(
'PAGE:TAG:CATE'=>'h:cache:article:cache',
'ID'=>'h:cache:article:id',
'TAG'=>'h:cache:article:tag',
'CATE'=>'h:cache:article:cate',
),
'SYSTEMS' =>array(
'KEY'=>'h:cache:systems:key'
),
'CATEGORY' => array(
'ALL'=>'h:cache:category:all'
),
'TAGS'=>array(
'ALL'=>'h:cache:tags:all'
)
);
public static $WEIXIN = array(
'USER' => 'h:weixin:user:',
);
/**
* 获取redis缓存
* @param $prefix
* @param $callback
* @return mixed
*/
public function getCache($prefix,$callback){
/**
* 获取函数参数值
*/
$param = func_get_args();
/**
* 先获取关键参数$prefix和callback,在获取动态参数
*/
$prefix = array_shift($param);
$callback = array_shift($param);
$key = implode(':',$param);
//尝试从redis获取参数,如果不存在,调用自定义callback函数获取
$ret = json_decode($this->redis->hGet($prefix,$key),true);
if(!$ret){
$ret = call_user_func_array($callback,$param);
$this->redis->hset($prefix,$key,json_encode($ret));
}
return $ret;
}
/**
* 清理一张表的缓存
* @param $tableName
*/
public function deleteTableCache($tableName){
$tableName = strtoupper($tableName);
if(isset(RedisUtils::$CACHEKEYS[$tableName])){
foreach(RedisUtils::$CACHEKEYS[$tableName] as $key=>$value){
$this->redis->del($value);
}
}
}
}
业务场景上,个人建议使用redis hash 的数据结构来存储
Hash相关操作
hSet
$redis->hSet('h', 'key1', 'hello');
向名称为h的hash中添加元素key1—>hello
hGet
$redis->hGet('h', 'key1');
返回名称为h的hash中key1对应的value(hello)
hLen
$redis->hLen('h');
返回名称为h的hash中元素个数
hDel
$redis->hDel('h', 'key1');
删除名称为h的hash中键为key1的域
hKeys
$redis->hKeys('h');
返回名称为key的hash中所有键
hVals
$redis->hVals('h')
返回名称为h的hash中所有键对应的value
hGetAll
$redis->hGetAll('h');
返回名称为h的hash中所有的键(field)及其对应的value
hExists
$redis->hExists('h', 'a');
名称为h的hash中是否存在键名字为a的域
hIncrBy
$redis->hIncrBy('h', 'x', 2);
将名称为h的hash中x的value增加2
hMset
$redis->hMset('user:1', array('name' => 'Joe', 'salary' => 2000));
向名称为key的hash中批量添加元素
hMGet
$redis->hmGet('h', array('field1', 'field2'));
返回名称为h的hash中field1,field2对应的value
可以分为两部分,一种是代码层业务缓存,这个前面很多同学做了回答,不再描述。其实还有一种其他的策略对于很多高频读低频写,或者一些实时性要求不高的数据,完全可以上 CDN来做。
这比起后端的缓存无论是 redis cache 还是 local cache 都要好得多。
后端自己的缓存本身有许多限制,redis cache 可能会有单点过热的问题,而 local cache 无法存储大量数据。
而且即便后端缓存,它只是降低了底层服务和数据库的压力而已,对于服务器的 QPS、连接数、带宽这些东西并没有帮助。
如果使用 CDN 缓存,后端服务器的压力就几乎可以忽略,而且如果缓存策略设置的好还可以让数据有缓冲,即便后端服务挂掉,在缓存的时间内大部分用户可以不会受影响。
让 API 上 CDN 其实很简单,对 CDN 使用「自有源」把 CDN 源站设置到 API 服务的域名上,并且设置上一定的缓存策略即可。但还是有很多需要注意的点。
首先,使用 CDN 来缓存的内容应该是公共内容,不涉及鉴权等行为,没有鉴权机制,我们不能把用户隐私的数据直接暴露出去。
CDN 缓存的 API 数据都是相对静态,不应该是随机生成的内容,也不应该是千人千面的内容。
内容不能有实时性需求,应该允许一个时间段内容忍缓存的,因为 CDN 上的缓存是无法实时更新,只有等缓存过期之后才更新(虽然一般的 CDN 也有 API 可以主动清缓存,但并不适合用于所有场景)。
1 回答4k 阅读✓ 已解决
3 回答1.8k 阅读✓ 已解决
2 回答2.2k 阅读✓ 已解决
2 回答3.1k 阅读
1 回答1.9k 阅读✓ 已解决
1 回答1.4k 阅读✓ 已解决
2 回答2.2k 阅读
那就题库使用缓存,好多啊memcache,redis