ThinkPHP关于数据库Cache的一些注意事项

http://img.ex666.cn/gid89/cover.png

在做一个比较简单的数据接口,虽然数据量并不大就几万条,但是为了增加响应速度以及减少资源占用,使用了其中的数据库缓存技术。

笔者大概对比了一下日志,直接访问数据库和使用缓存的耗时相差达到了3-5倍。

不过这个数据跟很多方面有关系,首先是数据库缓存方式,我选择了File,因此本地磁盘以及服务器的I/O性能对耗时影响应该是最大的了,所以数据并不官方,只是想说尽量使用缓存能给你的项目带来很大的帮助。

ThinkPHP对缓存方式的支持比较多,有file、memcache、wincache、sqlite、redis和xcache,所以我尝试了一下使用sqlite,结果却以失败告终。

http://img.ex666.cn/gid89/1.png

尝试过修改官方 think\Cache\Sqlite 驱动,然后配置了PHP对sqlite的支持,但还是报了各种错误,所以还是暂时放弃了,毕竟主要的目的还是正在做的项目接口嘛。

首先是一个获取随机数据的需求,ORDER BY RAND()就不讲了,效率超差+官方不推荐+有更好的解决方案,不用说直接忽略了。

最终使用SQL确定下来就是通过聚合函数 MAX() MIN() 以及 RAND() 来实现。

但是结合到ThinkPHP感觉还是有点复杂,而且每次查询仍然得访问数据库,加上SQL略多,所以效率上还是值得再优化的。

想了一下解决方案,最终结合ThinkPHP的缓存完成了需求,数据缓存后查询数据甚至不用连接数据库。

其实过程还是比较蛋疼了,因为总共有4个接口,每个接口得单独的写代码,因为新加的这个需求,代码简单重构了一下,不论limit多少数据都会缓存全部符合条件的数据,然后根据返回的数据二次加工返回指定数量。

惯性思维的锅,如果要获取1条数据,但却取出100条数据,任何人哪怕不是写代码的都知道这个肯定会花费很多时间。

但有了缓存,就得换个思维了,因为接口还支持设置指定数量的数据,所以倒不如将接口统一,先将所有符合条件的数据缓存,然后根据数量进行返回。

于是将多个接口最终统一到了一个接口上,效率也得到了明显的提高。代码如下:

1
2
3
4
5
6
7
8
private function data($date, $start = 0, $limit = 10, $field = 'id, y AS type')
{
$data = Users::where('i', $date)
->field($field)
->cache('tag_date' . $date, 3600)
->select();
return array_slice($data, $start, $limit);
}

同时,需要注意在链式查询当中,cache() 方法只能用一次,官方文档中并没有说明这个。

错误的写法是这样的:

1
2
3
4
5
6
7
8
9
public function test()
{
return Users::where()
->field('id, un, pw')
->cache('test')
->limit(10)
->cache('test_limit_10')
->select();
}

在接口还没改动的时候,我是打算查到所有数据后将所有数据缓存,然后查询前10条的时候缓存一次,这样别的接口可以复用这个查询所有的缓存。

但很显然这是个失败的尝试,反而导致了接口获取的数据不正确,所以要注意一下:

cache方法在一链条式查询当中只会产生一次,以最后一次为准。

另外就是缓存标识在不同的查询间一定要用不同的标识区分,否则新缓存覆盖同名标识的旧缓存,会导致数据查询为别的同名接口查出的数据。

注意我使用的版本是ThinkPHP5.0.4版本,其他版本可能有差异,详情请参考官方历史版本更新日志中关于数据库缓存的修改说明。

随便说说,可能有点啰嗦……