ThinkPHP框架分布式数据库连接方法详解


Posted in PHP onMarch 14, 2017

本文实例分析了ThinkPHP框架分布式数据库连接方法。分享给大家供大家参考,具体如下:

Thinkphp作为国内的一款流行框架,相信使用的人一定不在少数。本篇我们来分析一下Thinkphp中比较重要的一部分——分布式数据库的连接。

当然了,我们在这里不是去将如何使用模型去对数据库进行增删改查。我们是对其底层的连接代码做一个分析,可以帮助大家更好的理解thinkphp对数据库的操作。方便我们以后的使用。

一、单一数据库的连接

在使用的时候,单一数据库的连接配置非常简单。我们只需要在配置文件中配置一下的信息即可。

'DB_TYPE' => 'mysql',
'DB_HOST' => '192.168.5.102',
'DB_NAME' => 'databasename',
'DB_USER' => 'user',
'DB_PWD' => 'password',
'DB_PORT' => '3306',
'DB_PREFIX' => 'onmpw_',

设置完成以后就可以使用了。默认情况下就是单一的数据库连接。

二、分布式数据库的连接

单一数据库的连接很简单,我们着重分析一下分布式数据库的连接。

'DB_TYPE' => 'mysql',
'DB_HOST' => '192.168.5.191,192.168.5.88,192.168.5.103',
'DB_NAME' => 'test,test,test',
'DB_USER' => 'masteruser,slaveuser,slaveuser',
'DB_PWD' => 'masterpass,slavepass,slavepass',
'DB_PORT' => '3306',
'DB_PREFIX' => '',
'DB_DEPLOY_TYPE' => 1, // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器)
'DB_RW_SEPARATE' => true, // 数据库读写是否分离 主从式有效
'DB_MASTER_NUM' => 1, // 读写分离后 主服务器数量
'DB_SLAVE_NO' => '', // 指定从服务器序号

按照以上配置就可以连接分布式数据库了。

下面我们看下面几个选项

'DB_HOST'

分布式数据库,有几台服务器就要填写几个服务器地址,每个地址之间用逗号隔开。如果是主从式分布的话,前面的地址要是主数据库的地址。

对于下面的用户名和密码还有监听端口之类的,当然是有几个就写几个。如果各个数据库的用户名和密码都一样的话,可以只写一个。

对于这些选项的解析的代码如下

$_config['username'] =  explode(',',$this->config['username']);
$_config['password'] =  explode(',',$this->config['password']);
$_config['hostname'] =  explode(',',$this->config['hostname']);
$_config['hostport']  =  explode(',',$this->config['hostport']);
$_config['database'] =  explode(',',$this->config['database']);
$_config['dsn']   =  explode(',',$this->config['dsn']);
$_config['charset']  =  explode(',',$this->config['charset']);

'DB_DEPLOY_TYPE'=>1

1 表示是分布式, 0 表示的是集中式(也就是单一服务器)。

该选项的实现是在类 Think\Db\Dirver中

protected function initConnect($master=true) {
  if(!empty($this->config['deploy']))
    // 采用分布式数据库
    $this->_linkID = $this->multiConnect($master);
  else
    // 默认单数据库
    if ( !$this->_linkID ) $this->_linkID = $this->connect();
}

$this->config['deploy']表示的就是'DB_DEPLOY_TYPE'这个配置选项,上面的配置在使用之前都已经经过解析了,配置项都在$this->config数组中。至于是如何解析配置文件的,这里我们不做介绍,感兴趣的可以参考Think\Db类。

$this->multiConnect()函数就是用来进行分布式连接的,如果'DB_DEPLOY_TYPE'选项设置为1,该函数就会执行。否则直接执行$this->connect()函数。

'DB_RW_SEPARATE'=>true

true 表示读写分离;false表示读写不分离。

这里需要注意的是,读写分离是以主从式数据库系统为前提的。该选项设置为true的时候主数据库写,从数据库读。

if($this->config['rw_separate']){
   // 主从式采用读写分离
   if($master)
     // 主服务器写入
     $r =  $m;
   else{
     if(is_numeric($this->config['slave_no'])) {// 指定服务器读
       $r = $this->config['slave_no'];
     }else{
        // 读操作连接从服务器
       $r = floor(mt_rand($this->config['master_num'],count($_config['hostname'])-1));  // 每次随机连接的数据库
     }
      }
}else{
   // 读写操作不区分服务器
   $r = floor(mt_rand(0,count($_config['hostname'])-1));  // 每次随机连接的数据库
}

$this->config['rw_separate'] 为true的时候采用读写分离,为false的时候读写不分离。读写分离为什么必须是主从式的呢?因为从服务器不能写只能读,如果向从服务器写入数据的话,数据是没法同步的。这样就会造成数据不一致的情况。所以说,如果我们的系统是主从式的话,我们必须采用读写分离。也就是说DB_RW_SEPARATE选项必须配置为true。

'DB_MASTER_NUM'=>1

该选项后面的数字表示读写分离后,主服务器的数量。因此该选项也是用于主从式数据库系统。

下面的代码是选择主服务器。

$m = floor(mt_rand(0,$this->config['master_num']-1));

主从式数据库读取的时候选择从服务器读的核心代码

$r = floor(mt_rand($this->config['master_num'],count($_config['hostname'])-1));   // 每次随机连接的数据库

其中$this->config['master_num']表示主服务器的数量。

'DB_SLAVE_NO'=> ''

指定从服务器的序号,用于读取数据。如果不设置,则根据主服务器的数量计算书从服务器的数量,然后从中随机选取一台进行读取。

if(is_numeric($this->config['slave_no'])) {// 指定服务器读
  $r = $this->config['slave_no'];
}else{
  // 读操作连接从服务器
  $r = floor(mt_rand($this->config['master_num'],count($_config['hostname'])-1));  // 每次随机连接的数据库
}

以上是对每个选项的作用的实现代码进行了一个简单的说明。

下面我们来看其连接的部分

if($m != $r ){
  $db_master = array(
   'username' => isset($_config['username'][$m])?$_config['username'][$m]:$_config['username'][0],
   'password' => isset($_config['password'][$m])?$_config['password'][$m]:$_config['password'][0],
   'hostname' => isset($_config['hostname'][$m])?$_config['hostname'][$m]:$_config['hostname'][0],
   'hostport' => isset($_config['hostport'][$m])?$_config['hostport'][$m]:$_config['hostport'][0],
   'database' => isset($_config['database'][$m])?$_config['database'][$m]:$_config['database'][0],
   'dsn' => isset($_config['dsn'][$m])?$_config['dsn'][$m]:$_config['dsn'][0],
   'charset' => isset($_config['charset'][$m])?$_config['charset'][$m]:$_config['charset'][0],
  );
}
$db_config = array(
  'username' => isset($_config['username'][$r])?$_config['username'][$r]:$_config['username'][0],
  'password' => isset($_config['password'][$r])?$_config['password'][$r]:$_config['password'][0],
  'hostname' => isset($_config['hostname'][$r])?$_config['hostname'][$r]:$_config['hostname'][0],
  'hostport' => isset($_config['hostport'][$r])?$_config['hostport'][$r]:$_config['hostport'][0],
   'database' => isset($_config['database'][$r])?$_config['database'][$r]:$_config['database'][0],
   'dsn' => isset($_config['dsn'][$r])?$_config['dsn'][$r]:$_config['dsn'][0],
   'charset'  => isset($_config['charset'][$r])?$_config['charset'][$r]:$_config['charset'][0],
);
return $this->connect($db_config,$r,$r == $m ? false : $db_master);

看到这,我觉得大家应该对上面在介绍各个配置选项的代码的时候其中的$r和$m是什么作用了。

现在我们来看 $r == $m ? false : $db_master ,如果数据库读写不分离的情况下,读写是一台服务器的话 传给connect函数的值为false。或者是如果是主从分离的写的情况下传给connect的值也为false。通过上面代码我们看到,如果$r和$m不相等的情况下,会设置$db_master。其实也就是相当于一台备用的,如果选择的$r服务器出现故障不能连接,将会去连接$db_master。

connect()函数的第三个参数其实是表示当$db_config这台服务器连接故障时是否选择备用的连接。false表示不重连,其它值即表示重新连接。

其核心代码如下

try{
  if(empty($config['dsn'])) {
   $config['dsn'] =  $this->parseDsn($config);
  }
  if(version_compare(PHP_VERSION,'5.3.6','<=')){
    // 禁用模拟预处理语句
    $this->options[PDO::ATTR_EMULATE_PREPARES] =  false;
  }
  $this->linkID[$linkNum] = new PDO( $config['dsn'], $config['username'], $config['password'],$this->options);
}catch (\PDOException $e) {
  if($autoConnection){ //$autoConnection不为false,而是默认的主服务器
    trace($e->getMessage(),'','ERR');
      return $this->connect($autoConnection,$linkNum); //出现异常,使用递归函数重新连接
    }elseif($config['debug']){
      E($e->getMessage());
  }
}

这种方式,对于主从式来说,$r和$m肯定不会相同。因此如果说在读取数据的时候,选择的那台从服务器出现故障的话,那主服务器即是备用的,最后会去主服务器读取。能保证数据读取的时效性。

但是,总感觉现在还不太完善。如果说有多台从服务器,在读取的时候选择的那台从服务器和主服务器都出现了故障,那数据岂不是就读取失败了。这时候如果能再次读取其它的从服务器的话那应该是更有保障。当然了,目前的thinkphp的功能已经相当完善,足够我们使用了。但是还是希望thinkphp以后越来越完善。

希望本文所述对大家基于ThinkPHP框架的PHP程序设计有所帮助。

PHP 相关文章推荐
基于php设计模式中单例模式的应用分析
May 15 PHP
解析php防止form重复提交的方法
Jul 01 PHP
Yii框架获取当前controlle和action对应id的方法
Dec 03 PHP
分享常见的几种页面静态化的方法
Jan 08 PHP
PHP开发框架laravel安装与配置教程
Mar 13 PHP
Yii获取当前url和域名的方法
Jun 08 PHP
php使用APC实现实时上传进度条功能
Oct 26 PHP
linux下php上传文件注意事项
Jun 11 PHP
PHP微信开发之根据用户回复关键词\位置返回附近信息
Jun 24 PHP
深入解析Laravel5.5中的包自动发现Package Auto Discovery
Sep 13 PHP
PDO::getAttribute讲解
Jan 28 PHP
php-fpm中max_children的配置
Mar 15 PHP
让ThinkPHP的模板引擎达到最佳效率的方法详解
Mar 14 #PHP
thinkphp3.2实现跨控制器调用其他模块的方法
Mar 14 #PHP
Yii2下点击验证码的切换实例代码
Mar 14 #PHP
php分页查询的简单实现代码
Mar 14 #PHP
php实现购物车产品删除功能(2)
Jul 23 #PHP
php实现产品加入购物车功能(1)
Jul 23 #PHP
浅谈PHP的排列组合(如输入a,b,c 输出他们的全部组合)
Mar 14 #PHP
You might like
PHP中的按位与和按位或操作示例
2014/01/27 PHP
php查找字符串出现次数的方法
2014/12/01 PHP
php版微信公众平台接口开发之智能回复开发教程
2016/09/22 PHP
PHP异常处理定义与使用方法分析
2017/07/25 PHP
优化javascript的执行速度
2010/01/23 Javascript
基于jquery的图片懒加载js
2010/06/30 Javascript
nodejs入门详解(多篇文章结合)
2012/03/07 NodeJs
JS实现随机数生成算法示例代码
2013/08/08 Javascript
Angularjs实现多个页面共享数据的方式
2016/03/29 Javascript
第七章之菜单按钮图标组件
2016/04/25 Javascript
Javascript基于jQuery UI实现选中区域拖拽效果
2016/11/25 Javascript
用原生js做单页应用
2017/01/17 Javascript
JS简单实现点击跳转登陆邮箱功能的方法
2017/10/31 Javascript
js实现图片上传并预览功能
2018/08/06 Javascript
灵活使用console让js调试更简单的方法步骤
2019/04/23 Javascript
Vue中key的作用示例代码详解
2020/06/10 Javascript
vue基于Echarts的拖拽数据可视化功能实现
2020/12/04 Vue.js
原生js实现下拉框选择组件
2021/01/20 Javascript
Python中的index()方法使用教程
2015/05/18 Python
Python实现的手机号归属地相关信息查询功能示例
2017/06/08 Python
Python3中列表list合并的四种方法
2019/04/19 Python
NumPy排序的实现
2020/01/21 Python
PyQt5 界面显示无响应的实现
2020/03/26 Python
详解python UDP 编程
2020/08/24 Python
Html5 Canvas 实现一个“刮刮乐”游戏
2019/09/05 HTML / CSS
什么是TCP/IP
2014/07/27 面试题
学校办公室主任职责
2013/12/27 职场文书
校园十大歌手策划书
2014/02/01 职场文书
房屋买卖委托公证书
2014/04/08 职场文书
劳资协议书范本
2014/04/23 职场文书
员工安全生产责任书
2014/07/22 职场文书
社区学习党的群众路线教育实践活动心得体会
2014/11/03 职场文书
先进工作者个人总结
2015/02/15 职场文书
演讲开场白和结束语
2015/05/29 职场文书
初婚初育证明范本
2015/06/18 职场文书
开网店计划分析
2019/07/30 职场文书