Yiiフレームワークデータベース書き込み分離設計

5961 ワード

Yiiフレームワークはphpで流行しているフレームワークであり,現在多くの開発者が使用している.現在、プロジェクトでmysqlデータベースの主従分離を使用する方法は常態となっている.Yiiではどのように実現しますか?
1、まず自分でデータベースの主従接続クラスを書く必要があります.
2、このクラスをconfigディレクトリのmainファイルに登録します.
3、実現構想はSELECT、SHOWなどの文に出会った時に自動的に従庫に切り替える.
 
データベース接続クラス:
<?php

class DbConnection extends CDbConnection
{
	/**
	 * @var array $slaves.Slave database connection(Read) config array.
	 * The array value's format is the same as CDbConnection.
	 * <code>
	 * 'components'=>array(
	 * 	'db'=>array(
	 * 		'class' => 'DbConnection',
	 * 		'connectionString'=>'mysql://<master>',
	 * 		'slaves'=>array(
	 * 			array('connectionString'=>'mysql://<slave01>'),
	 * 			array('connectionString'=>'mysql://<slave02>'),
	 * 		)
	 * 	)
	 * )
	 * </code>
	 */
	public $slaves = array();
	
	/**
	 * Whether enable the slave database connection.
	 * Defaut is true.Set this property to false for the purpose of only use the master database.
	 * 
	 * @var bool $enableSlave 
	 */
	public $enableSlave = true;
	
	/**
	 * @override
	 * @var bool $autoConnect Whether connect while init
	 */
	//public $autoConnect=false;
	

	/**
	 * @var CDbConnection
	 */
	private $_slave;

	/**
	 * Creates a CDbCommand object for excuting sql statement.
	 * It will detect the sql statement's behavior.
	 * While the sql is a simple read operation.
	 * It will use a slave database connection to contruct a CDbCommand object.
	 * Default it use current connection(master database).
	 * 
	 * @override 
	 * @param string $sql
	 * @return CDbCommand
	 */
	public function createCommand($query = null)
	{
		if ($this->enableSlave && !$this->getCurrentTransaction() && self::isReadOperation($query)) {
			return $this->getSlave()->createCommand($query);
		} else {
			return parent::createCommand($query);
		}
	}

	/**
	 * Construct a slave connection CDbConnection for read operation.
	 * 
	 * @return CDbConnection
	 */
	public function getSlave()
	{
		if (!isset($this->_slave)) {
			foreach ($this->slaves as $slaveConfig) {
				if (!isset($slaveConfig['class']))
					$slaveConfig['class'] = 'CDbConnection';
				try {
					if ($slave = Yii::createComponent($slaveConfig)) {
						Yii::app()->setComponent('dbslave', $slave);
						$this->_slave = $slave;
						break;
					}
				} catch (Exception $e) {
					Yii::log('Create slave database connection failed!', 'warn');
					continue;
				}
			}
			if (!$this->_slave) {
				$this->_slave = clone $this;
				$this->_slave->enableSlave = false;
			}
		}
		return $this->_slave;
	}

	/**
	 * Detect whether the sql statement is just a simple read operation.
	 * Read Operation means this sql will not change any thing ang aspect of the database.
	 * Such as SELECT,DECRIBE,SHOW etc.
	 * On the other hand:UPDATE,INSERT,DELETE is write operation.
	 * 
	 * @return bool
	 */
	public function isReadOperation($sql)
	{
		return !!preg_match('/^\s*(SELECT|SHOW|DESCRIBE|PRAGMA)/i', $sql);
	}

	/**
	 * check Master | slave db is connection 
	 *
	 * @params bool $isSlave
	 * @return void
	 */
	public function ping($isSlave = true)
	{
		if ($isSlave) {
			if ($this->getActive()) {
				$connect = $this->getSlave();
				try {
					$connect->getPdoInstance()->query('SELECT 1');
				} catch (PDOException $e) {
					$connect->setActive(false);
					$connect->setActive(true);
				}
			}
		} else {
			if ($this->getActive()) {
				try {
					$this->getPdoInstance()->query('SELECT 1');
				} catch (PDOException $e) {
					$this->setActive(false);
					$this->setActive(true);
				}
			}
		}
	}
}

 このクラスをmain.phpに登録します.
'public_db_common' => array(
	            'class' => 'DbConnection', //       DbConnection 
	            'connectionString' => "mysql:host=;dbname=;port=",
	            'emulatePrepare' => true,
	            'username' => ,
	            'password' => ,
	            'tablePrefix' => 't_',
	             'charset' => 'utf8',
	            'enableProfiling' => true, //
	            'enableParamLogging' => true, //
	            'slaves' => array(
	                array(
	                    'connectionString' => "mysql:host=;dbname=;port=",
	                    'emulatePrepare' => true,
	                    'username' => ,
	                     'password' => ,
	                    'charset' => 'UTF8',
	                    'tablePrefix' => 't_',
	                    'enableParamLogging' => YII_DEBUG,
	                    'schemaCacheID' => 'cache',
	                    'schemaCachingDuration' => 0,
	                )
	            ),
        ),

 
プログラム全体の呼び出しといえば、何の違いもありません.