但是读写分离必然引发主从复制,试想一共有 10 张票,买了 1 张票,读的时候如果还是读到 10 张余票就不对了,因此需要主从复制,再读的时候,就只能读到 9 张余票了;本文实例分析了Yii实现MySQL多数据库和读写分离的方法。分享给大家供大家参考。具体分析如下: Yii Framework是一个基于组件、用于开发大型 Web 应用的高性能 PHP 框架。Yii提供了今日Web 20应用开发所需要的几乎一切功能,也是最强大的框架之一,下文我们来介绍Yii实现MySQL多库和读写分离的方法 前段时间为SNS产品做了架构设计,在程序框架方面做了不少相关的压力测试,最终选定了YiiFramework,至于为什么没选用公司内部的 PHP框架,其实理由很充分,公司的框架虽然是"前辈"们辛苦的积累,但毕竟不够成熟,没有大型项目的历练,犹如一个涉世未深的年轻小伙。Yii作为一个 颇有名气开源产品,必定有很多人在使用,意味着有一批人在维护,而且在这之前,我也使用Yii开发过大型项目,Yii的设计模式和它的易扩展特性足以堪当重任。 SNS同一般的社交产品不同的就是它最终要承受大并发和大数据量的考验,架构设计时就要考虑这些问题, web分布式、负载均衡、分布式文件存储、MySQL分布式或读写分离、NoSQL以及各种缓存,这些都是必不可少的应用方案,本文所讲的就是MySQL 分库和主从读写分离在Yii的配置和使用。 Yii默认是不支持读写分离的,我们可以利用Yii的事件驱动模式来实现MySQL的读写分离。 Yii提供了一个强大的CActiveRecord数据库 *** 作类,通过重写getDbConnection方法来实现数据库的切换,然后通过事件 beforeSave、beforeDelete、beforeFind 来实现读写服务器的切换,还需要两个配置文件dbconfig和modelconfig分别配置数据库主从服务器和model所对应的数据库名称,附代码 DBConfigphp文件如下: 复制代码 代码如下:<php return array( 'passport' => array( 'write' => array( 'class' => 'CDbConnection', 'connectionString' => 'mysql:host=101392;dbname=db1′, 'emulatePrepare' => true, //'enableParamLogging' => true, 'enableProfiling' => true, 'username' => 'root', 'password' => '', 'charset' => 'utf8′, 'schemaCachingDuration'=>3600, ), 'read' => array( array( 'class' => 'CDbConnection', 'connectionString' => 'mysql:host=101393;dbname=db1, 'emulatePrepare' => true, //'enableParamLogging' => true, 'enableProfiling' => true, 'username' => 'root', 'password' => '', 'charset' => 'utf8′, 'schemaCachingDuration'=>3600, ), array( 'class' => 'CDbConnection', 'connectionString' => 'mysql:host=101394;dbname=db3′, 'emulatePrepare' => true, //'enableParamLogging' => true, 'enableProfiling' => true, 'username' => 'root', 'password' => '', 'charset' => 'utf8′, 'schemaCachingDuration'=>3600, ), ), ), ); ModelConfigphp如下: 复制代码 代码如下:<php return array( //key为数据库名称,value为Model 'passport' => array('User','Post'), 'microblog' => array('…'), ); > ActiveRecordphp如下: 复制代码 代码如下:/ 基于CActiveRecord类的封装,实现多库和主从读写分离 所有Model都必须继承些类 / class ActiveRecord extends CActiveRecord { //model配置 public $modelConfig = ''; //数据库配置 public $dbConfig = ''; //定义一个多数据库集合 static $dataBase = array(); //当前数据库名称 public $dbName = ''; //定义库类型(读或写) public $dbType = 'read'; //'read' or 'write' / 在原有基础上添加了一个dbname参数 @param string $scenario Model的应用场景 @param string $dbname 数据库名称 / public function __construct($scenario='insert', $dbname = '') { if (!empty($dbname)) $this->dbName = $dbname; parent::__construct($scenario); } / 重写父类的getDbConnection方法 多库和主从都在这里切换 / public function getDbConnection() { //如果指定的数据库对象存在则直接返回 if (self::$dataBase[$this->dbName]!==null) return self::$dataBase[$this->dbName]; if ($this->dbName == 'db'){ self::$dataBase[$this->dbName] = Yii::app()->getDb(); }else{ $this->changeConn($this->dbType); } if(self::$dataBase[$this->dbName] instanceof CDbConnection){ self::$dataBase[$this->dbName]->setActive(true); return self::$dataBase[$this->dbName]; } else throw new CDbException(Yii::t('yii','Model requires a "db" CDbConnection application component')); } / 获取配置文件 @param unknown_type $type @param unknown_type $key / private function getConfig($type="modelConfig",$key="){ $config = Yii::app()->params[$type]; if($key) $config = $config[$key]; return $config; } / 获取数据库名称 / private function getDbName(){ if($this->dbName) return $this->dbName; $modelName = get_class($this->model()); $this->modelConfig = $this->getConfig('modelConfig'); //获取model所对应的数据库名 if($this->modelConfig)foreach($this->modelConfig as $key=>$val){ if(in_array($modelName,$val)){ $dbName = $key; break; } } return $dbName; } / 切换数据库连接 @param unknown_type $dbtype / protected function changeConn($dbtype = 'read'){ if($this->dbType == $dbtype && self::$dataBase[$this->dbName] !== null) return self::$dataBase[$this->dbName]; $this->dbName = $this->getDbName(); if(Yii::app()->getComponent($this->dbName'_'$dbtype) !== null){ self::$dataBase[$this->dbName] = Yii::app()->getComponent($this->dbName'_'$dbtype); return self::$dataBase[$this->dbName]; } $this->dbConfig = $this->getConfig('dbConfig',$this->dbName); //跟据类型取对应的配置(从库是随机值) if($dbtype == 'write'){ $config = $this->dbConfig[$dbtype]; }else{ $slavekey = array_rand($this->dbConfig[$dbtype]); $config = $this->dbConfig[$dbtype][$slavekey]; } //将数据库配置加到component中 if($dbComponent = Yii::createComponent($config)){ Yii::app()->setComponent($this->dbName'_'$dbtype,$dbComponent); self::$dataBase[$this->dbName] = Yii::app()->getComponent($this->dbName'_'$dbtype); $this->dbType = $dbtype; return self::$dataBase[$this->dbName]; } else throw new CDbException(Yii::t('yii','Model requires a "changeConn" CDbConnection application component')); } / 保存数据前选择 主 数据库 / protected function beforeSave(){ parent::beforeSave(); $this->changeConn('write'); return true; } / 删除数据前选择 主 数据库 / protected function beforeDelete(){ parent::beforeDelete(); $this->changeConn('write'); return true; } / 读取数据选择 从 数据库 / protected function beforeFind(){ parent::beforeFind(); $this->changeConn('read'); return true; } / 获取master库对象 / public function dbWrite(){ return $this->changeConn('write'); } / 获取slave库对象 / public function dbRead(){ return $this->changeConn('read'); } } 这是我写好的类,放在components文件夹里,然后所有的Model都继承ActiveRecord类就可以实现多库和主从读写分离了,至于如何支持原生的SQL也同时使用读写分离,此类都已经实现。 希望本文所述对大家基于Yii框架的PHP程序设计有所帮助。
你要保持数据的一致性,需要一个共享存贮。共享存贮包括网页,脚本,数据库等数据。这样,当用户连接到不同的服务器时,可以看到相同的内容。也有利于管理员的维护。其实,没有共享存贮也可能实现LVS,但是,你要手工进行数据复制同步,管理负担会很重。
共享存贮,可以通过配置安装有FREENAS的服务器来实现,也可购买支持ISCSI等协议的的网络存贮设备来实现。
至于要不要双网卡,也是看你的性能要求了,理论上,只要通讯正常,单网卡也是可以的。
以下复制章文嵩的LVS原理,请见谅。
一般来说,LVS集群采用三层结构,其体系结构如图1所示,三层主要组成部分为:
•负载调度器(load balancer),它是整个集群对外面的前端机,负责将客户的请求发送到一组服务器上执行,而客户认为服务是来自一个IP地址(我们可称之为虚拟IP地址)上的。
•服务器池(server pool),是一组真正执行客户请求的服务器,执行的服务有WEB、MAIL、FTP和DNS等。
•共享存储(shared storage),它为服务器池提供一个共享的存储区,这样很容易使得服务器池拥有相同的内容,提供相同的服务。
调度器是服务器集群系统的唯一入口点(Single Entry Point),它可以采用IP负载均衡技术、基于内容请求分发技术或者两者相结合。在IP负载均衡技术中,需要服务器池拥有相同的内容提供相同的服务。当客户请求到达时,调度器只根据服务器负载情况和设定的调度算法从服务器池中选出一个服务器,将该请求转发到选出的服务器,并记录这个调度;当这个请求的其他报文到达,也会被转发到前面选出的服务器。在基于内容请求分发技术中,服务器可以提供不同的服务,当客户请求到达时,调度器可根据请求的内容选择服务器执行请求。因为所有的 *** 作都是在Linux *** 作系统核心空间中将完成的,它的调度开销很小,所以它具有很高的吞吐率。
服务器池的结点数目是可变的。当整个系统收到的负载超过目前所有结点的处理能力时,可以在服务器池中增加服务器来满足不断增长的请求负载。对大多数网络服务来说,请求间不存在很强的相关性,请求可以在不同的结点上并行执行,所以整个系统的性能基本上可以随着服务器池的结点数目增加而线性增长。
共享存储通常是数据库、网络文件系统或者分布式文件系统。服务器结点需要动态更新的数据一般存储在数据库系统中,同时数据库会保证并发访问时数据的一致性。静态的数据可以存储在网络文件系统(如NFS/CIFS)中,但网络文件系统的伸缩能力有限,一般来说,NFS/CIFS服务器只能支持3~6个繁忙的服务器结点。对于规模较大的集群系统,可以考虑用分布式文件系统,如AFS[1]、GFS[23]、Coda[4]和 Intermezzo[5]等。分布式文件系统可为各服务器提供共享的存储区,它们访问分布式文件系统就像访问本地文件系统一样,同时分布式文件系统可提供良好的伸缩性和可用性。此外,当不同服务器上的应用程序同时读写访问分布式文件系统上同一资源时,应用程序的访问冲突需要消解才能使得资源处于一致状态。这需要一个分布式锁管理器(Distributed Lock Manager),它可能是分布式文件系统内部提供的,也可能是外部的。开发者在写应用程序时,可以使用分布式锁管理器来保证应用程序在不同结点上并发访问的一致性。
负载调度器、服务器池和共享存储系统通过高速网络相连接,如100Mbps交换网络、Myrinet和Gigabit网络等。使用高速的网络,主要为避免当系统规模扩大时互联网络成为整个系统的瓶颈。
Graphic Monitor是为系统管理员提供整个集群系统的监视器,它可以监视系统的状态。Graphic Monitor是基于浏览器的,所以无论管理员在本地还是异地都可以监测系统的状况。为了安全的原因,浏览器要通过>
参考:
>
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)