解决两个小细节造成的数据库压力

之前在另一篇博客里说过了,本人的webservice ,db都是跨地区的,web在杭州机房,db都在北京机房,所以每次连库都是100多ms,为了加快速度,本人用了memcache,将整个网站的数据都缓存起来了,然后发现访问速度还算理想。但是今天闲来无事,用siege进行了一次压测,模拟了1000个用户,1分钟的持续时间,最后发现结果并不理想,所以今天决定探本索源一次。

我首先去查看了mysql的日志,发现日志里显示mysql有连接也有查询,既然全站缓存了,为何还有连接呢?我开启了xdebug追踪了下运行过程,发现确实连了数据库,原来我在写框架的时候,为了省事,在model的基类里用了一个构造函数,如下:

    protected $db = null;
    public function __construct() {
        $this->db =  _Drive\pdo::getInstance(self::DB_HOST,self::DB_USER,self::DB_PASS,self::DB_NAME,self::DB_PORT);
    }

也就是说,我每次实例化model的时候,都会连接数据库,虽然我使用了单例模式,一次请求只连一次,但是如果访问量高了,也是会造成数据库压力的。可是如果我不把它放在基类的构造函数里,那么我岂不是得每次缓存不命中的时候需要手动的实例化一次db类?那我得改多少处代码。这时我突然想到了php的魔术方法,本人写php很久了,貌似魔术方法用的还真不多,既然大多数情况下不会实例化db类,但是当缓存不命中的时候还是需要查库的,所以去掉了上面的代码,加上代码如下:

public function __get($key) {
      //echo $key;exit();
      if($key === 'db') {
        $this->db =  _Drive\pdo::getInstance(self::DB_HOST,self::DB_USER,self::DB_PASS,self::DB_NAME,self::DB_PORT);
        return $this->db;
      }
      return ;
   }

修改好了以后,再用siege压测了一下,奇怪的是发现mysql依然有连接,有查询,再用xdebug追踪一下,发现查询走的已经是魔术方法了,也就是说,还有其他问题造成了查库。经观察发现,原来在读取数据的时候,会先去缓存查询,如果有数据则直接返回,如果没有数据则读库,把从数据库读出的数据写入缓存,并返回。乍然一看貌似没有问题,可是仔细推敲就发现,如果从数据库里读出的数据本身就是空,那岂不是从缓存读取的数据也为空,那么就会一直查库了。原来的代码大抵如下:

$cache_key = 'XXXXXXX';
$cache_result = $this->cache->get($cache_key);
if(!empty($cache_result)) {
return $cache_result;
}

修改如下

$cache_key = 'XXXXXXX';
$result = $this->cache->exits($cache_key);
if($result) {
return $this->cache->get($cache_key);
}

这两处修改后,再压测,发现就正常了,其实这两个小问题都算不上什么很大的问题,而且在访问量不大的时候都看不出啥差别,所以平时很容易忽略,看来压力测试还是很有必要的。