【问题标题】:How do you use memcached with Prepared Statements?您如何将 memcached 与 Prepared Statements 一起使用?
【发布时间】:2009-12-06 20:00:00
【问题描述】:

缓存常规 SQL 查询非常简单。

public function query($sql) {

    if( $result = cache::get(sha1($sql)) ) {
        return $result;
    }

    $result = $this->connection->query($sql);
    cache::set(sha1($sql), $result);
    return $result;
}

但是你如何缓存带有准备好的语句的查询,因为你不知道查询将是什么,直到语句已经准备好然后数据被绑定?

$sth = $dbh->prepare('SELECT * FROM table WHERE id = ?');

...later...

$sth->bindParam(1, $id);
$sth->execute();

我觉得这是一个由两部分组成的答案:首先,语句缓存在每页内存中(如 $this->statements[] 数组),因为数据库资源 ID 不会持续很长时间并且不能存储在文件或任何东西中。

其次,在执行语句之前(),我们通过散列用于创建语句的 sql(使用PDOStatement::queryString 很容易)加上给定参数的散列来查看 memcached/filecache 的结果。麻烦在于在语句对象中找到参数。

当然,这只是一种想法,可能还有更好的解决方案。

【问题讨论】:

    标签: php memcached prepared-statement


    【解决方案1】:

    好吧,您必须将每个参数的值添加到缓存键中。像这样的:

    public function stmt($sql, $params) {
    
        $cache_key = sha1($sql . serialize($params));
    
        if( $result = cache::get($cache_key) ) {
            return $result;
        }
    
        $sth = $this->connection->prepare($sql);
    
        $i = 0;
        foreach ($params as &$param)
        {
            $sth->bindParam(++$i, $param);
            $sth->execute();
        }
        unset($param)
    
        // fetch all the rows into $result
    
        cache::set($cache_key, $result);
        return $result;
    }
    
    $obj->stmt('SELECT * FROM table WHERE id = ?', array(&$id));
    

    我会把它留给你来适应你的需要。您必须获取行并将它们存储在一个数组中。


    这是您必须使用的包装器:

    class stmt
    {
        protected $sth, $sql, $cache, $params = array();
    
        public function __construct($dbh, $sql)
        {
            $this->sth = $dbh->prepare($sql);
            $this->sql = $sql;
        }
    
        public function bindParam($param, &$var)
        {
            $this->params[$param] =& $var;
            return $this->sth->bindParam($param, $var);
    
            // or, if you want to support all the args
            $args = func_get_args();
            $args[1] =& $var;
    
            return call_user_func_array(array($this->sth, 'bindParam'), $args);
        }
    
        public function execute(array $params = null)
        {
            $str = serialize(isset($params) ? $params : $this->params);
            $cache_key = sha1($this->sql . $str);
    
            // insert cache logic here...
    
            if (isset($params))
            {
                $this->stmt->execute($params);
            }
            else
            {
                $this->stmt->execute();
            }
    
            $this->cache = $this->stmt->fetchAll();
    
            // save cache here
        }
    
        public function fetch()
        {
            return array_shift($this->cache);
        }
    }
    

    您必须匹配您计划使用的每个 PDOStatement 方法。 PDO::FETCH_INTO 也很难实现。我的建议:专注于你自己的使用。也许您甚至不必在 dbh 级别实现缓存,而是可以只在需要的地方添加缓存功能。

    无论如何,请记住,您编写的代码越多,您需要维护的代码就越多,您就越有可能在应用程序中引入错误。所以要小心缓存层的成本/收益分析,因为它会为了自己的利益而过于聪明:)

    【讨论】:

    • 不错的开始,但是大多数情况下,首先创建语句(从 sql)然后添加参数(而不是同时添加)?
    • 在这种情况下,你几乎完蛋了。 :) 我认为您不能从语句中获取原始 SQL,因此您必须创建一个将原始 SQL 和准备好的语句存储在一起的语句包装器。根据我的经验,我发现扩展或环绕 MySQLi 更难,因为绑定参数和获取结果的方式,并且因为大多数参数是通过引用传递的,这就是我现在更喜欢使用 PDO 的原因。
    • 哦,我没有注意到它实际上是 PDO 的方法签名。我有自己的 stmt() 包装器,所以我第一眼就认不出它们了。
    • PDOStatement::queryString 包含准备好的 SQL,因此可能有一些方法可以使用它。
    • 很好,因为您可以扩展 PDO 和 MySQLi 语句,我希望您可以覆盖 execute()bind*() 方法来进行检查,然后调用 parent::*()。我会试试这个。
    猜你喜欢
    • 2015-03-02
    • 2016-10-25
    • 2012-08-23
    • 1970-01-01
    • 2014-04-14
    • 1970-01-01
    • 2013-08-25
    • 2010-11-25
    • 1970-01-01
    相关资源
    最近更新 更多