php做API后 由于API变动不大(题库),缓存用什么方案解决?

php做API后 由于API变动不大(题库),缓存用什么方案解决?

如题

阅读 4.2k
5 个回答

那就题库使用缓存,好多啊memcache,redis

正好有一个api通过redis缓存的接口:

api代码:

//这里是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);
            }
        }
    }
}

参见代码:

RedisUtils
ApiController

模板缓存
1、twig
2、..

数据缓存
1、redis
2、memcache

业务场景上,个人建议使用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 可以主动清缓存,但并不适合用于所有场景)。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题