让CodeIgniter支持数据库读、写分离

前言

CodeIgniter默认是不支持读、写分离的,网上流传的一般做法是在CI_Model层修改,但这有几个问题:

  1. 首先使用CodeIgniter的用户都是用过之后才发现不支持的,然后要修改大量的旧代码,产生的影响较多;
  2. 其次,在Model层修改,如果有代码在Controller操作数据库,将不能支持读、写分离(虽然在Controller直接操作数据库不是好方法);
  3. 最后,在CI_Model层的修改都要让用户去使用不同的数据库实例,如写用$this->write_db->query(),读用$this->read_db->query(),这样旧代码基本无法修改,因为之前的代码都是调用$this->db->query()方法的,要修改大量的旧代码。

所以,在CI_Model层修改不是很好的方案。因CI_Model和CI_Controller都使用了Controller类的db实例,所以我选择的方法是在Controller引入两个数据库连接,SQL查询时动态分析是否为SELECT语句,如果是则使用只读连接,其它使用写连接,来实现读、写分离,坏处是需要修改核心代码,对系统升级有一定副作用。

本次修改的环境是:

  • CodeIgniter 2.1.4
  • MySQL 5.1+
  • PHP 5.5.9
  • nginx 1.6.0

步骤一:修改application/config/database.php

数据库读、写连接参数的配置。

$active_record = TRUE;

	if (defined(‘ENVIRONMENT‘))
	{
		switch (ENVIRONMENT)
		{
			case ‘development‘:

				$db[‘default‘][‘hostname‘] = ‘127.0.0.1‘;
				$db[‘default‘][‘username‘] = ‘‘;
				$db[‘default‘][‘password‘] = ‘‘;
				$db[‘default‘][‘database‘] = ‘‘;
				$db[‘default‘][‘dbdriver‘] = ‘mysqli‘;
				$db[‘default‘][‘dbprefix‘] = ‘‘;
				$db[‘default‘][‘pconnect‘] = FALSE;
				$db[‘default‘][‘db_debug‘] = FALSE;
				$db[‘default‘][‘cache_on‘] = FALSE;
				$db[‘default‘][‘cachedir‘] = $_SERVER[‘DOCUMENT_ROOT‘].‘/cache/‘;
				$db[‘default‘][‘char_set‘] = ‘utf8‘;
				$db[‘default‘][‘dbcollat‘] = ‘utf8_general_ci‘;
				$db[‘default‘][‘swap_pre‘] = ‘‘;
				$db[‘default‘][‘autoinit‘] = TRUE;
				$db[‘default‘][‘stricton‘] = FALSE;

				$db[‘write‘][‘hostname‘] = ‘127.0.0.1‘;
				$db[‘write‘][‘username‘] = ‘‘;
				$db[‘write‘][‘password‘] = ‘‘;
				$db[‘write‘][‘database‘] = ‘‘;
				$db[‘write‘][‘dbdriver‘] = ‘mysqli‘;
				$db[‘write‘][‘dbprefix‘] = ‘‘;
				$db[‘write‘][‘pconnect‘] = FALSE;
				$db[‘write‘][‘db_debug‘] = FALSE;
				$db[‘write‘][‘cache_on‘] = FALSE;
				$db[‘write‘][‘cachedir‘] = $_SERVER[‘DOCUMENT_ROOT‘].‘/cache/‘;
				$db[‘write‘][‘char_set‘] = ‘utf8‘;
				$db[‘write‘][‘dbcollat‘] = ‘utf8_general_ci‘;
				$db[‘write‘][‘swap_pre‘] = ‘‘;
				$db[‘write‘][‘autoinit‘] = TRUE;
				$db[‘write‘][‘stricton‘] = FALSE;
				$db[‘crawl‘][‘stricton‘] = FALSE;

				break;

			case ‘testing‘:

				$db[‘default‘][‘hostname‘] = ‘127.0.0.1‘;
				$db[‘default‘][‘port‘] = ‘3306‘;
				$db[‘default‘][‘username‘] = ‘‘;
				$db[‘default‘][‘password‘] = ‘‘;
				$db[‘default‘][‘database‘] = ‘‘;
				$db[‘default‘][‘dbdriver‘] = ‘mysqli‘;
				$db[‘default‘][‘dbprefix‘] = ‘‘;
				$db[‘default‘][‘pconnect‘] = FALSE;
				$db[‘default‘][‘db_debug‘] = FALSE;
				$db[‘default‘][‘cache_on‘] = FALSE;
				$db[‘default‘][‘cachedir‘] = $_SERVER[‘DOCUMENT_ROOT‘].‘/cache/‘;
				$db[‘default‘][‘char_set‘] = ‘utf8‘;
				$db[‘default‘][‘dbcollat‘] = ‘utf8_general_ci‘;
				$db[‘default‘][‘swap_pre‘] = ‘‘;
				$db[‘default‘][‘autoinit‘] = TRUE;
				$db[‘default‘][‘stricton‘] = FALSE;

				$db[‘write‘][‘hostname‘] = ‘‘;
				$db[‘write‘][‘port‘] = ‘3306‘;
				$db[‘write‘][‘username‘] = ‘‘;
				$db[‘write‘][‘password‘] = ‘‘;
				$db[‘write‘][‘database‘] = ‘caishenquan‘;
				$db[‘write‘][‘dbdriver‘] = ‘mysqli‘;
				$db[‘write‘][‘dbprefix‘] = ‘‘;
				$db[‘write‘][‘pconnect‘] = FALSE;
				$db[‘write‘][‘db_debug‘] = FALSE;
				$db[‘write‘][‘cache_on‘] = FALSE;
				$db[‘write‘][‘cachedir‘] = $_SERVER[‘DOCUMENT_ROOT‘].‘/cache/‘;
				$db[‘write‘][‘char_set‘] = ‘utf8‘;
				$db[‘write‘][‘dbcollat‘] = ‘utf8_general_ci‘;
				$db[‘write‘][‘swap_pre‘] = ‘‘;
				$db[‘write‘][‘autoinit‘] = TRUE;
				$db[‘write‘][‘stricton‘] = FALSE;

				break;

			case ‘production‘:

				$db[‘default‘][‘hostname‘] = ‘‘;
				$db[‘default‘][‘username‘] = ‘‘;
				$db[‘default‘][‘password‘] = ‘‘;
				$db[‘default‘][‘database‘] = ‘‘;
				$db[‘default‘][‘dbdriver‘] = ‘mysqli‘;
				$db[‘default‘][‘dbprefix‘] = ‘‘;
				$db[‘default‘][‘pconnect‘] = FALSE;
				$db[‘default‘][‘db_debug‘] = FALSE;
				$db[‘default‘][‘cache_on‘] = FALSE;
				$db[‘default‘][‘cachedir‘] = $_SERVER[‘DOCUMENT_ROOT‘].‘/cache/‘;
				$db[‘default‘][‘char_set‘] = ‘utf8‘;
				$db[‘default‘][‘dbcollat‘] = ‘utf8_general_ci‘;
				$db[‘default‘][‘swap_pre‘] = ‘‘;
				$db[‘default‘][‘autoinit‘] = TRUE;
				$db[‘default‘][‘stricton‘] = FALSE;

				$db[‘write‘][‘hostname‘] = ‘‘;
				$db[‘write‘][‘username‘] = ‘‘;
				$db[‘write‘][‘password‘] = ‘‘;
				$db[‘write‘][‘database‘] = ‘‘;
				$db[‘write‘][‘dbdriver‘] = ‘mysqli‘;
				$db[‘write‘][‘dbprefix‘] = ‘‘;
				$db[‘write‘][‘pconnect‘] = FALSE;
				$db[‘write‘][‘db_debug‘] = FALSE;
				$db[‘write‘][‘cache_on‘] = FALSE;
				$db[‘write‘][‘cachedir‘] = $_SERVER[‘DOCUMENT_ROOT‘].‘/cache/‘;
				$db[‘write‘][‘char_set‘] = ‘utf8‘;
				$db[‘write‘][‘dbcollat‘] = ‘utf8_general_ci‘;
				$db[‘write‘][‘swap_pre‘] = ‘‘;
				$db[‘write‘][‘autoinit‘] = TRUE;
				$db[‘write‘][‘stricton‘] = FALSE;
				$db[‘crawl‘][‘stricton‘] = FALSE;

				break;

			default:
				exit(‘The application environment is not set correctly.‘);
		}
	}

$active_group = ‘default‘;

步骤二、修改 system/core/Controller.php

添加两数据库实例变量

var $db_write = null;
var $db_read = null;

步骤三、修改system/core/Load.php

修改数据库实例加载方法

public function database($params = ‘‘, $return = FALSE, $active_record = NULL)
{
   // Grab the super object
   $CI =& get_instance();

   // Do we even need to load the database class?
   if (class_exists(‘CI_DB‘) AND $return == FALSE AND $active_record == NULL AND isset($CI->db) AND is_object($CI->db))
   {
      return FALSE;
   }

   require_once(BASEPATH.‘database/DB.php‘);

   if ($return === TRUE)
   {
      return DB($params, $active_record);
   }

   // Initialize the db variable.  Needed to prevent
   // reference errors with some configurations
   $CI->db = ‘‘;

   // Load the DB class
   $CI->db =& DB($params, $active_record);
   //读、写分离 by [email protected]
   $CI->db_read = $CI->db;
   $CI->db_write = & DB($this->get_write_dsn(),$active_record);
}

   /**
    * 获取数据库连接(写模式)dsn
    * driver://username:[email protected]/database
    * @author [email protected]
    */
   function get_write_dsn()
   {
       if ( ! defined(‘ENVIRONMENT‘) OR ! file_exists($file_path = APPPATH.‘config/‘.ENVIRONMENT.‘/database.php‘))
       {
           if ( ! file_exists($file_path = APPPATH.‘config/database.php‘))
           {
               show_error(‘[get_write_dsn]The configuration file database.php does not exist.‘);
           }
       }

       include($file_path);

       if ( ! isset($db) OR count($db) == 0)
       {
           show_error(‘[get_write_dsn]No database connection settings were found in the database config file.‘);
       }

       $connection_arguments = array();
       if(isset($db[‘write‘]))
       {
           $connection_arguments = $db[‘write‘];
       }
       else
       {
           $connection_arguments = $db[‘default‘];
       }

       $dsn_base =  $connection_arguments[‘dbdriver‘].‘://‘.$connection_arguments[‘username‘].‘:‘.$connection_arguments[‘password‘].‘@‘.$connection_arguments[‘hostname‘].‘/‘.$connection_arguments[‘database‘];
       unset($connection_arguments[‘dbdriver‘]);
       unset($connection_arguments[‘username‘]);
       unset($connection_arguments[‘password‘]);
       unset($connection_arguments[‘hostname‘]);
       unset($connection_arguments[‘database‘]);
       $dsn_query = $this->my_build_str($connection_arguments);
       return $dsn_base.‘?‘.$dsn_query;
   }

   function my_build_str($arr)
   {
       $str=‘‘;
       foreach($arr as $key=>$val)
       {
           $str.=($key.‘=‘.strval($val).‘&‘);
       }
       $str = preg_replace(‘/&$/‘,‘‘,$str);
       return $str;
   }

步骤四、修改 system/database/DB_driver.php

添加对SQL的数据库连接选择路由功能。

function simple_query($sql)
   {
      //depend on database.php setting $db[‘local‘][‘autoinit‘]
      if ( ! $this->conn_id)
      {
         $this->initialize($sql);
      }
      //added by [email protected]
      if( $this->db_debug ){
         echo $sql."<br/><br/>\r\n\r\n";
      }
//    return $this->_execute($sql);

        //读写分离
        $CI =& get_instance();
        if(strstr(strtolower($sql),"select"))
        {
            return $CI->db_read->_execute($sql);
        }
        else
        {
            return $CI->db_write->_execute($sql);
        }
   }

步骤五、修改 system/core/CodeIngiter.php

添加关闭数据库连接功能

if (class_exists(‘CI_DB‘) AND isset($CI->db))
{
   $CI->db->close();
   //读写分离 by [email protected]
   $CI->db_write->close();
   $CI->db_read->close();
}

测试

读者可以写一个controller,观察$this->db_read 和 $this->db_write 实例的连接信息(具体php的调试方法参考笔者之前的博客介绍);或对一个主、从数据进行操作实际检验效果。

总结

此方案在Controller层修改,适应了在Model层和Controller层的数据库操作的读、写分离,旧代码基本不用修改,加入适当的调整,对连接池或多主多从连接的支持也是可以的,缺点是代码要修改核心功能,影响升级。

时间: 2024-10-08 12:07:45

让CodeIgniter支持数据库读、写分离的相关文章

CodeIgniter 3.0支持数据库读写分离方式

网上有部分方法,支持读写分离,但过于复制,而且有的只支持2.0版本的,现在改善一个,支持3.0版本的读写分离 本次修改的环境是: CodeIgniter 3.0.3 MySQL 5.5+ PHP 5.5.9 nginx 1.1.8 步骤一:修改application/config/database.php 数据库读.写连接参数的配置. $active_group = 'default'; $query_builder = TRUE; $db['default'] = array( 'dsn' =

sql server 本地复制订阅 实现数据库服务器 读写分离(转载)

转载地址:http://www.cnblogs.com/echosong/p/3603270.html 再前段echosong 写了一遍关于mysql 数据同步实现业务读写分离的文章,今天咱们来看下SQL Server的复制订阅实现数据的读写分离 比起mysql的复制,SQL server 复制相对强大 一. 名词解释 1.复制的 机构组成(类比报纸流通): 1).发布服务器(报社出版) 生产维护数据源,审阅所有出版数据的更改 发送给 分发服务器(邮局) 2).分发服务器 (邮局) 分发服务器包

MySQL数据库之读写分离

一.概述: MySQL数据库主从结构配置以后,正常情况下数据库的所有读写操作全部都在主数据库上面,从数据库仅仅作为数据备份使用,显然无法有效的使用服务器资源,那么实现读写分离的需求就不可避免. 二.拓扑图说明: 如上图所示,本文要实现的是读MySQL数据库的写入操作(增删改)等在Master服务器(192.168.4.10)上面实现,而对MySQL数据库的读取操作(查询)等在Slave服务器(192.168.4.20)上面完成. 如果在程序员编程时创建两个数据库连接Connection,在程序中

sql server 本地复制订阅 实现数据库服务器 读写分离

原文:sql server 本地复制订阅 实现数据库服务器 读写分离 再前段echosong 写了一遍关于mysql 数据同步实现业务读写分离的文章,今天咱们来看下SQL Server的复制订阅实现数据的读写分离 比起mysql的复制,SQL server 复制相对强大 一. 名词解释 1.复制的 机构组成(类比报纸流通): 1).发布服务器(报社出版) 生产维护数据源,审阅所有出版数据的更改 发送给 分发服务器(邮局) 2).分发服务器 (邮局) 分发服务器包括分发数据库,并且存储元数据.历史

Mycat - 实现数据库的读写分离与高可用【转】

文章地址:https://www.cnblogs.com/youzhibing/p/9553766.html 前言 开心一刻 上语文课,不小心睡着了,坐在边上的同桌突然叫醒了我,并小声说道:“读课文第三段”.我立马起身大声读了起来.正在黑板写字的老师吓了一跳,老师郁闷的看着我,问道:“同学有什么问题吗?”,我貌似知道了什么,蛋定的说了一句:“这段写的真好!我给大伙念念!”,老师还较真了:“你说说看,好在哪里?”,顿时我就无语了,脸黑着望向了同桌了,心想着:“这是个畜生啊!” 路漫漫其修远兮,吾将

数据库表读写分离,分区

数据库分区分表以及读写分离 谈谈怎么实现Oracle数据库分区表 Oracle数据库分区是作为Oracle数据库性能优化的一种重要的手段和方法,做手头的项目以前,只聆听过分区的大名,感觉特神秘,看见某某高手在讨论会上夸夸其谈时,真是骂自己学艺不精,最近作GPS方面的项目,处理的数据量达到了几十GB,为了满足系统的实时性要求,必须提高数据的查询效率,这样就必须通过分区,以解燃眉之急! 先说说分区的好处吧! 1) 增强可用性:如果表的某个分区出现故障,表在其他分区的数据仍然可用: 2) 维护方便:如

数据库的主从分离

Linux安装及配套使用软件 第一步:去下载VMware软件. 第二步:去下载Linux系统的映像文件,我使用的是CentOS-6.5-x86_64-bin-DVD1.iso的系统. 第三步:开始安装 第四步:安装成功后去关掉Linux的防火墙 关闭防火墙:# service iptables stop: 查看防火墙状态:# service iptables status: (如果要开启防火墙:使用# service iptables start:) 这样就把Linux虚拟机安装好了: 第五步:

数据库的读写分离技术

读写分离,基本的原理是让主数据库处理事务性增.改.删操作(INSERT.UPDATE.DELETE)和实时数据查询,而从数据库处理SELECT查询操作.数据库复制被用来把事务性操作导致的变更同步到集群中的从数据库. 背景: 随着一个网站的业务不断扩展,数据不断增加,数据库的压力也会越来越大,对数据库或者SQL的基本优化可能达不到最终的效果,我们可以采用读写分离的策 略来改变现状.读写分离现在被大量应用于很多大型网站,这个技术也不足为奇了. 读写分离简单的说是把对数据库读和写的操作分开对应不同的数

怎样加快master数据库的写操作?分表原则!将表水平划分!

1.怎样加快master数据库的写操作?分表原则!将表水平划分!减少表的锁定时间!!! 2.既然分表了,就一定要注意分表的规则!要在代码层,代码层知道将数据insert/select到哪张表上. 3.要在代码层知道将数据操作到哪张表上!分表规则, 所以分表规则要用代码设计出来,与数据库中的分表规则对应!!!一样!!!一样的分表规则!! 4.那么按什么规则分表呢?有不同的策略,常见的有按照01234556789,账号尾数0对应0表,1对应1表. 还可以根据业务来分表.如按照用户或者商品. 具体分表