【问题标题】:Efficiently use mysql Table to cache complex queries高效使用mysql表缓存复杂查询
【发布时间】:2012-08-27 14:01:57
【问题描述】:

我曾经在查询中有相当多的多个联接。

为了能够(至少)使用内置的 MySql Cache 功能,我编写了以下函数, 它只是将原始查询编码为 base64,检查它是否存在且未过期。

这极大地提高了性能,而且我的优势是可以在源代码中控制缓存时逐个查询。

但在繁忙时间,由于删除或选择时间过长,表格变得不可用。有什么建议可以让这个运行更快并避免前面提到的问题吗?

表:

CREATE TABLE `cachesql` (
`id` int(9) NOT NULL AUTO_INCREMENT,
`expire` int(15) NOT NULL,
`sql` text NOT NULL,
`data` mediumtext NOT NULL,
PRIMARY KEY (`id`,`sql`(360)),
KEY `sdata` (`sql`(767)) USING HASH
) ENGINE=InnoDB

功能:

    function fetchRows_cache($sql,$cachetime,$dba){
    // internal function (called by fetchRows)
    global $Site;
    $expire = 0;
    $this->connect($dba);

    // check if query is cached
    $this->q = mysql_query("SELECT `expire`,`data` from cachesql where `sql`='".base64_encode($sql)."' limit 1;", $this->con) OR $this->error(1, "query$".$sql."$".mysql_error());
    $this->r = mysql_fetch_assoc($this->q);
    $expire = $this->r['expire'];
    $data = $this->r['data'];

    if (($expire < time())||($cachetime =="0")) { // record expied or not there -> execute query and store
        $this->query("DELETE FROM `cachesql` WHERE `sql`='".base64_encode($sql)."'",$dba); // delete old cached entries

        $this->q = mysql_query($sql, $this->con) OR $this->error(1, "query$".$sql."$".mysql_error());
        $this->r=array();
        $this->rc=0;
        while($row = mysql_fetch_assoc($this->q)){
            $arr_row=array();
            $c=0;
            while ($c < mysql_num_fields($this->q)) {        
                $col = mysql_fetch_field($this->q, $c);    
                $arr_row[$col -> name] = $row[$col -> name];            
                $c++;
            }    
            $this->r[$this->rc] = $arr_row;
            $this->rc++;
        }
        $out = $this->r;

        // write results into cache table
        if ($cachetime != "0") {
            // not store cache values for now (too many locks)
            $this->query("INSERT INTO `cachesql` (`sql`,`data`,`expire`) VALUES ('".base64_encode($sql)."','".mysql_real_escape_string(serialize($out))."','".(time()+$cachetime)."')",$dba);       
        }
        return $out;

    }
    else { // use Cached data
        return unserialize($data); 
    }
}

【问题讨论】:

  • 因为 sql 是一个文本字段,它不能真正用作主键。内存表也是如此,SQL 的 VarChar 字段也不完全可能,因为查询可能比 256 字节长得多。
  • 我的想法是将校验和存储在 varchar 字段中而不是完整的查询中,但我不知道这是否是一个聪明的主意……有什么想法吗?
  • 校验和是指MD5还是其他类似的哈希码? MD5 存在加密问题,但对您的目的应该没问题。
  • 改用memcached怎么样?
  • @nullx8:自 5.0.3 以来,varchar 最长可达 64k。但是,这么大的 varchar 上的索引会很慢。

标签: php mysql query-cache


【解决方案1】:

我认为主要的减速点是,您使用 InnoDB 作为缓存表。

我发现,除了读取繁重的缓存表之外,您应该将 InnoDB 用于所有内容;)

MyISAM 特别适用于读取密集型(选择)表。

【讨论】:

  • MyISAM 速度很快,因为它是一个非事务性存储引擎。但是,如果您需要事务和完全符合 ACID 的要求,则不应使用它。
  • 同意! MyISAM 在锁定问题上有很大的缺点。由于表是由临时数据填充的并且清理是在同一个过程中完成的,使用 MyISAM 将基本上永远锁定表,数据被读取大约 50-100 次直到过期,所以 DELETE 命令仍然超过 1% 的事务MyISAM -> 糟糕的选择!
【解决方案2】:

感谢@HeatfanJohn - 他指出了一些非常简单和高效的东西。

由于原始查询不用于任何事情(缓存条目的匹配除外), 只需存储校验和即可唯一地标识相关查询。

新结构仅存储原始查询的 MD5 哈希(16 字节)、expireUnixTime 和序列化的行集

新结构:

CREATE TABLE `cachesql` (
`sql` varchar(32) NOT NULL,
`expire` int(11) NOT NULL,
`data` text NOT NULL,
PRIMARY KEY (`sql`),
UNIQUE KEY `sql` (`sql`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='cache for db->fetchRows'

sql 列上的主索引非常短,因此反应非常快,可以更好地为搜索建立索引。

在对数据集使用 BLOB 或 TEXT 字段时,我没有得到不同的速度结果。

【讨论】:

  • 您的问题得到解答了吗?如果是,请用答案左侧的支票将其标记为已回答。如果不是,请编辑您的问题并添加附加信息,然后删除答案。谢谢。
  • 我已经尝试过使用二进制字段,但匹配需要更长的时间。使用 varchar(32) 似乎是存储 md5 哈希的最佳和最快的解决方案。
【解决方案3】:

让我们试试 inmemory 表以使其更快。

【讨论】:

  • 内存表不支持文本文件,对于大行结果,64K varchar 大小不够
猜你喜欢
  • 2011-12-30
  • 1970-01-01
  • 1970-01-01
  • 2018-06-08
  • 1970-01-01
  • 1970-01-01
  • 2011-09-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多