问题背景在我们的项目中,我们使用了大量的属性函数来提高我们面向业务的调用便利性和代码级的优雅,但同时也带来了一定的问题,会产生大量的重复计算和SQL调用,对应用的性能造成打击。让我们演示一下这个过程。首先定义一个属性,这样我们就可以很方便的通过$user->posts_count来得到用户发布的帖子总数,比原来的写法更加语义化和优雅。publicfunctiongetPostsCountAttribute(){return$this->posts()->count();}但在下面的情况下,会差很多,会产生多个SQL查询导致服务器响应慢if($user->posts_count>30){//过程1}if($user->posts_count>50){//过程2}if($user->posts_count>100){//过程3}if($user->posts_count>200){//流程4}通过上面的写法,我们可能会造成4次SQL聚合操作,影响我们的响应速度。当然,我们也可以尝试完善代码规范来解决这个问题,但是还是不能保证这个属性被连续调用到第二、第三次,所以方便快捷的方法是加一层属性缓存,将获取到的值缓存到模型属性中。属性缓存(attributecache)使用属性缓存的好处是简单快速,不需要第三方扩展,利用了面向对象语言的原生优势。下面来实现一下:在我们的Larvel框架项目中,模型都是继承自Eloquent\Model是一个基类。重写基类对我们来说很麻烦,所以我们可以用php实现多重继承traits。首先实现一个特性AttributeCacheHelpercachedAttributes)){$this->setCachedAttribute($key,call_user_func($callable));}返回$this->cachedAttributes[$key];}publicfunctionsetCachedAttribute(string$key,$value){return$this->cachedAttributes[$key]=$value;}publicfunctionrefresh(){unset($this->cachedAttributes);返回父级::刷新();}}主要实现了三个函数,get和set用于获取和设置属性Cache,refresh重写了父类的刷新函数,每次刷新后都会清理对象缓存。然后我们开始重新修改我们的属性来添加对象缓存。首先,我们需要在模型上使用特征公共函数AttributeCacheHelpergetPostsCountAttribute(){$method='postsCount';$callable=[$this,$method];return$this->getCachedAttribute($method,$callable);}publicfunctionpostsCount(){return$this->posts()->count();}修改完成后,我们重新应用到场景,即使执行10000次,也只会生成一条SQL查询,大大提高了查询效率和响应速度。for($i=0;$i<10000;$i++){//只生成一个真正的查询,并读取所有其他对象缓存数据dump($user->posts_count);}最后快乐编码。
