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 相关文章推荐
多文件上载系统完整版
Oct 09 PHP
PHP中$_SERVER的详细参数与说明
Jul 29 PHP
php 将excel导入mysql
Nov 09 PHP
PHP中用hash实现的数组
Jul 17 PHP
php将数组转换成csv格式文件输出的方法
Mar 14 PHP
Nginx环境下PHP flush失效的解决方法
Oct 19 PHP
PHP面向对象程序设计高级特性详解(接口,继承,抽象类,析构,克隆等)
Dec 02 PHP
PHP实现绘制二叉树图形显示功能详解【包括二叉搜索树、平衡树及红黑树】
Nov 16 PHP
PHP实现合并两个排序链表的方法
Jan 19 PHP
php+redis实现商城秒杀功能
Nov 19 PHP
微信公众平台开发教程③ PHP实现微信公众号支付功能图文详解
Apr 10 PHP
php日志函数error_log用法实例分析
Sep 23 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
收音机发烧友应当熟知的100条知识
2021/03/02 无线电
怎么使 Mysql 数据同步
2006/10/09 PHP
使用PHPExcel操作Excel用法实例分析
2015/03/26 PHP
php打包压缩文件之ZipArchive方法用法分析
2016/04/30 PHP
Laravel框架运行出错提示RuntimeException No application encryption key has been specified.解决方法
2019/04/02 PHP
javascript 密码强弱度检测万能插件
2009/02/25 Javascript
jquery 与NVelocity 产生冲突的解决方法
2011/06/13 Javascript
js自动查找select下拉的菜单并选择(示例代码)
2014/02/26 Javascript
Javascript动画的实现原理浅析
2015/03/02 Javascript
ubuntu下安装nodejs以及升级的办法
2015/05/08 NodeJs
Express框架之connect-flash详解
2017/05/31 Javascript
微信小程序 上传头像的实例详解
2017/10/27 Javascript
基于Datatables跳转到指定页的简单实例
2017/11/09 Javascript
详解如何用模块化的方式写vuejs
2017/12/16 Javascript
详解key在Vue列表渲染时究竟起到了什么作用
2019/04/20 Javascript
微信小程序Flex布局用法深入浅出分析
2019/04/25 Javascript
微信小程序实现上传word、txt、Excel、PPT等文件功能
2019/05/23 Javascript
Python文件读写保存操作的示例代码
2018/09/14 Python
python 判断参数为Nonetype类型或空的实例
2018/10/30 Python
Python selenium的基本使用方法分析
2019/12/21 Python
python软件都是免费的吗
2020/06/18 Python
Python pysnmp使用方法及代码实例
2020/08/24 Python
python通过函数名调用函数的几种场景
2020/09/23 Python
五分钟学会怎么用Pygame做一个简单的贪吃蛇
2021/01/06 Python
CSS中越界问题的经典解决方案【推荐】
2016/04/19 HTML / CSS
HTML5的文档结构和新增标签完全解析
2017/04/21 HTML / CSS
美国知名的在线旅游服务网站:Priceline
2016/07/23 全球购物
尤妮佳moony海外旗舰店:日本殿堂级纸尿裤品牌
2018/02/23 全球购物
学生发电厂实习自我鉴定
2013/09/22 职场文书
汽车专业大学生职业生涯规划范文
2014/01/07 职场文书
团代会主持词
2014/04/02 职场文书
项目合作协议书范本
2014/04/16 职场文书
小学运动会前导词
2015/07/20 职场文书
总结python多进程multiprocessing的相关知识
2021/06/29 Python
Python 数据科学 Matplotlib图库详解
2021/07/07 Python
MIME类型中application/xml与text/xml的区别介绍
2022/01/18 HTML / CSS