让我纠结的phpredis

在最近的项目中,须要用php访问redis,咱们选择了phpredis,下面是让人纠结的一些问题。 php


redis持久链接不靠谱。

能够说这是php的通病了,无论是mysql、memcache仍是redis,期望由php自己(包含php扩展)来实现持久链接都是行不通的。 前端

为何这么说呢?

首先,所谓的持久链接的实现不外乎在进程(php-fpm)内建一个链接池,当php须要链接时,先以ip+port等信息为key在池中查找,找到则直接返回已有链接没有则新建链接。而当一个请求执行结束时,不关闭链接,而是把链接归还到池中。 mysql

这样当php须要用到多个redis实例时(分库),由于一个php-fpm进程会持有每一个redis实例的一个链接,因此须要“php-fpm进程数“*“redis实例数"个redis链接,而对于每一个redis服务器则有“php-fpm进程数“个客户端链接。 web

举个例子:一个web应用开了1000个php-fpm进程,有10个redis实例,那么保持的redis链接数就为1000*10也就是10000,每一个redis实例有1000个客户端链接。若是前端或redis再扩容所须要的链接就会以乘积方式增长。一个redis实例有php-fpm进程数个链接的状况下表现如何呢,这就要好好测一测了,反正是每链接一线程的mysql是直接堵死了。 redis

RedisArray不靠谱。

RedisArray实现了一致性hash分布式,可是它在初始化的时候就会链接上每一个实例,这在web应用中简直是胡闹,它对一致性hash实现得比较完善,结点失效、动态添加结点时从新hash都有处理,在万不得已进行水平扩容时,可能会用得上。 sql

须要自已关闭redis链接。

Redis的析构函数没有关闭redis链接,这会致使redis网络负载太高,要确保脚本结束时关闭链接,最好是可以封装一下Redis类再使用。 安全

示例封装 /// 分布式Redis. class RedisShard {     /// 构造函数.     public function __construct($shards) {         $this->reinit($shards);     }     /// 析构函数.     /// 脚本结束时,phpredis不会自动关闭redis链接,这里添加自动关闭链接支持.     /// 能够经过手动unset本类对象快速释放资源.     public function __destruct() {         if(isset($this->shard)){             $this->shard['redis']->close();         }     }     /// 从新初始化.     public function reinit($shards){         $index = 0;         $this->shards = array();         foreach($shards as $shard){             $this->shards[$index] = explode(':', $shard); //格式:host:port:db             $this->shards[$index]['index'] = $index;             ++$index;         }             }     /// 转发方法调用到真正的redis对象.     public function __call($name, $arguments) {         $result = call_user_func_array(array($this->redis($arguments[0]), $name), $arguments);         if($result === false and in_array($name, array('set', 'setex', 'incr'))) {             trigger_error("redis error: " . $this->shard[0] . ':' . $this->shard[1] . ':' .$this->shard[2] . " $name " . implode(' ', $arguments), E_USER_NOTICE);         }         return $result;     }     /// 获取1至max间的惟一序号name,达到max后会从1开始.     /// redis的递增到最大值后会返回错误,本方法实现安全的递增。     /// 失败返回false,最要确保已用redis()方法连到生成序号的某个redis对象.     public function id($name, $max) {         if(isset($this->shard)){             $id = $this->shard['redis']->incr('_id_' . $name);             if($id){                 $max = intval($max/count($this->shards));                 if($id % $max == 0){                     while($this->shard['redis']->decrBy('_id_' . $name, $max) >= $max){                     }                     $id = $max;                 }                 else if($id > $max){                     $id %= $max;                 }                 return ($id - 1)*count($this->shards) + ($this->shard['index'] + 1);             }         }         return false;     }     /// 链接并返回key对应的redis对象.     public function redis($key){         //TODO: crc32在32位系统下会返回负数,因咱们是部署在64位系统上,暂时忽略.         assert(PHP_INT_SIZE === 8);         $index = crc32($key) % count($this->shards);         $shard = $this->shards[$index];         if(isset($this->shard)){             //尝试重用已有链接.             if($this->shard[0] == $shard[0] and $this->shard[1] == $shard[1]){                 if($this->shard[2] != $shard[2]){                     if(! $this->shard['redis']->select($shard[2])){                         trigger_error('redis error: select ' . $shard[0] . ':' . $shard[1] . ':' .$shard[2], E_USER_ERROR);                         return false;                     }                     $this->shard[2] = $shard[2];                 }                 return $this->shard['redis'];             }             $this->shard['redis']->close();             unset($this->shard);         }         //新建链接.         $shard['redis'] = new Redis();         if(! $shard['redis']->connect($shard[0], $shard[1])){             trigger_error('redis error: connect ' . $shard[0] . ':' . $shard[1], E_USER_ERROR);             return false;         }         $db = intval($shard[2]);         if($db != 0 and !$shard['redis']->select($db)){             trigger_error('redis error: select ' . $shard[0] . ':' . $shard[1] . ':' .$shard[2], E_USER_ERROR);             $shard['redis']->close();             return false;         }         if(ENABLE_DEVELOP){             trigger_error('redis connect success. ' . $shard[0] . ':' . $shard[1] . ':' . $shard[2], E_USER_NOTICE);         }                 $this->shard = $shard;         return $this->shard['redis'];     } }
相关文章
相关标签/搜索