mysql读写分离原理是什么要如何 *** 作

mysql读写分离原理是什么要如何 *** 作,第1张

在老版本的MySQL 322中,MySQL的单表限大小为4GB,当时的MySQL的存储引擎还是ISAM存储引擎。但是,当出现MyISAM存储引擎之后,也就是从MySQL 323开始,MySQL单表最大限制就已经扩大到了64PB了(官方文档显示)。也就是说,从目前的技术环境来看,MySQL数据库的MyISAM存储 引擎单表大小限制已经不是有MySQL数据库本身来决定,而是由所在主机的OS上面的文件系统来决定了。\x0d\\x0d\而MySQL另外一个最流行的存储引擎之一Innodb存储数据的策略是分为两种的,一种是共享表空间存储方式,还有一种是独享表空间存储方式。\x0d\当使用共享表空间存储方式的时候,Innodb的所有数据保存在一个单独的表空间里面,而这个表空间可以由很多个文件组成,一个表可以跨多个文件存在,所 以其大小限制不再是文件大小的限制,而是其自身的限制。从Innodb的官方文档中可以看到,其表空间的最大限制为64TB,也就是说,Innodb的单 表限制基本上也在64TB左右了,当然这个大小是包括这个表的所有索引等其他相关数据。\x0d\而当使用独享表空间来存放Innodb的表的时候,每个表的数据以一个单独的文件来存放,这个时候的单表限制,又变成文件系统的大小限制了。

通常读 *** 作要比写 *** 作的数量多得多,而且数据库在数据写入时是会锁表的,这个过程不能读取数据,必须等待写入完成,所以一旦请求量大那么执行效率就降低了。读写分离就是一个库只负责读取不做写入,这样就提高了读的效率。

oracle数据库本身就是读写分离的。如果是防止脏读,那么直接用一个库就可以了。

如果你是想把大查询放到从库,实时业务放主库,防止查询影响主库效率,那最简单的就是把你的查询链接数据库连到从库上,甚至可以配一个server,先尝试连从库,从库连不上连主库。

两种方案

方案1:当只有读 *** 作的时候,直接 *** 作读库(从库);

当在写事务(即写主库)中读时,也是读主库(即参与到主库 *** 作),这样的优势是可以防止写完后可能读不到刚才写的数据;

此方案其实是使用事务传播行为为:SUPPORTS解决的。

方案2:当只有读 *** 作的时候,直接 *** 作读库(从库);

当在写事务(即写主库)中读时,强制走从库,即先暂停写事务,开启读(读从库),然后恢复写事务。

此方案其实是使用事务传播行为为:NOT_SUPPORTS解决的。

核心组件

cnjavasscommondatasourceReadWriteDataSource:读写分离的动态数据源,类似于AbstractRoutingDataSource,具体参考javadoc;

cnjavasscommondatasourceReadWriteDataSourceDecision:读写库选择的决策者,具体参考javadoc;

cnjavasscommondatasourceReadWriteDataSourceProcessor:此类实现了两个职责(为了减少类的数量将两个功能合并到一起了):读/写动态数据库选择处理器、通过AOP切面实现读/写选择,具体参考javadoc。

具体配置

1、数据源配置

11、写库配置

Java代码

<bean id="writeDataSource" class="orglogicalcobwebsproxoolProxoolDataSource">

<property name="alias" value="writeDataSource"/>

<property name="driver" value="${writeconnectiondriver_class}" />

<property name="driverUrl" value="${writeconnectionurl}" />

<property name="user" value="${writeconnectionusername}" />

<property name="password" value="${writeconnectionpassword}" />

<property name="maximumConnectionCount" value="${writeproxoolmaximumconnectioncount}"/>

<property name="minimumConnectionCount" value="${writeproxoolminimumconnectioncount}" />

<property name="statistics" value="${writeproxoolstatistics}" />

<property name="simultaneousBuildThrottle" value="${writeproxoolsimultaneousbuildthrottle}"/>

</bean>

12、读库配置

Java代码

<bean id="readDataSource1" class="orglogicalcobwebsproxoolProxoolDataSource">

<property name="alias" value="readDataSource"/>

<property name="driver" value="${readconnectiondriver_class}" />

<property name="driverUrl" value="${readconnectionurl}" />

<property name="user" value="${readconnectionusername}" />

<property name="password" value="${readconnectionpassword}" />

<property name="maximumConnectionCount" value="${readproxoolmaximumconnectioncount}"/>

<property name="minimumConnectionCount" value="${readproxoolminimumconnectioncount}" />

<property name="statistics" value="${readproxoolstatistics}" />

<property name="simultaneousBuildThrottle" value="${readproxoolsimultaneousbuildthrottle}"/>

</bean>

13、读写动态库配置

通过writeDataSource指定写库,通过readDataSourceMap指定从库列表,从库列表默认通过顺序轮询来使用读库,具体参考javadoc;

Java代码

<bean id="readWriteDataSource" class="cnjavasscommondatasourceReadWriteDataSource">

<property name="writeDataSource" ref="writeDataSource"/>

<property name="readDataSourceMap">

<map>

<entry key="readDataSource1" value-ref="readDataSource1"/>

<entry key="readDataSource2" value-ref="readDataSource1"/>

<entry key="readDataSource3" value-ref="readDataSource1"/>

<entry key="readDataSource4" value-ref="readDataSource1"/>

</map>

</property>

</bean>

2、XML事务属性配置

所以读方法必须是read-only(必须,以此来判断是否是读方法)。

Java代码

<tx:advice id="txAdvice" transaction-manager="txManager">

<tx:attributes>

<tx:method name="save" propagation="REQUIRED" />

<tx:method name="add" propagation="REQUIRED" />

<tx:method name="create" propagation="REQUIRED" />

<tx:method name="insert" propagation="REQUIRED" />

<tx:method name="update" propagation="REQUIRED" />

<tx:method name="merge" propagation="REQUIRED" />

<tx:method name="del" propagation="REQUIRED" />

<tx:method name="remove" propagation="REQUIRED" />

<tx:method name="put" read-only="true"/>

<tx:method name="query" read-only="true"/>

<tx:method name="use" read-only="true"/>

<tx:method name="get" read-only="true" />

<tx:method name="count" read-only="true" />

<tx:method name="find" read-only="true" />

<tx:method name="list" read-only="true" />

<tx:method name="" propagation="REQUIRED"/>

</tx:attributes>

</tx:advice>

3、事务管理器

事务管理器管理的是readWriteDataSource

Java代码

<bean id="txManager" class="orgspringframeworkjdbcdatasourceDataSourceTransactionManager">

<property name="dataSource" ref="readWriteDataSource"/>

</bean>

4、读/写动态数据库选择处理器

根据之前的txAdvice配置的事务属性决定是读/写,具体参考javadoc;

forceChoiceReadWhenWrite:用于确定在如果目前是写(即开启了事务),下一步如果是读,是直接参与到写库进行读,还是强制从读库读,具体参考javadoc;

Java代码

<bean id="readWriteDataSourceTransactionProcessor" class="cnjavasscommondatasourceReadWriteDataSourceProcessor">

<property name="forceChoiceReadWhenWrite" value="false"/>

</bean>

5、事务切面和读/写库选择切面

Java代码

<aop:config expose-proxy="true">

<!-- 只对业务逻辑层实施事务 -->

<aop:pointcut id="txPointcut" expression="execution( cnjavassservice())" />

<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>

<!-- 通过AOP切面实现读/写库选择 -->

<aop:aspect order="-2147483648" ref="readWriteDataSourceTransactionProcessor">

<aop:around pointcut-ref="txPointcut" method="determineReadOrWriteDB"/>

</aop:aspect>

</aop:config>

1、事务切面一般横切业务逻辑层;

2、此处我们使用readWriteDataSourceTransactionProcessor的通过AOP切面实现读/写库选择功能,order=IntegerMIN_VALUE(即最高的优先级),从而保证在 *** 作事务之前已经决定了使用读/写库。

6、测试用例

只要配置好事务属性(通过read-only=true指定读方法)即可,其他选择读/写库的 *** 作都交给readWriteDataSourceTransactionProcessor完成。

可以参考附件的:

cnjavassreadwriteReadWriteDBTestWithForceChoiceReadOnWriteFalse

cnjavassreadwriteReadWriteDBTestWithNoForceChoiceReadOnWriteTrue

可以下载附件的代码进行测试,具体选择主/从可以参考日志输出。

读写分离一般都是结合Master/Slave模式使用,Master处理写请求,Slave处理读请求,这样做的好处是:

1、提高数据库的并发处理能力;

2、避免写请求锁表阻塞读请求;

3、避免单点,提高数据库的可用性;

而使用Redis作为DB前面的缓存,是为了减少对MySQL的压力,提高系统的处理效率。

二者解决的问题域不同,不存在谁替代谁。

一般高并发应用都是结合二者使用。

本文实例分析了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程序设计有所帮助。

以上就是关于mysql读写分离原理是什么要如何 *** 作全部的内容,包括:mysql读写分离原理是什么要如何 *** 作、为什么数据库读写分离可以提高性能、Oracle的读写分离在应用程序层面上应该怎么做等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/sjk/9768695.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-01
下一篇 2023-05-01

发表评论

登录后才能评论

评论列表(0条)

保存