数据库主从和读写分离的配置和使用方法
数据库主从和读写分离的配置和使用方法
TP:
ThinkPHP是一个开源的PHP框架,是为了简化企业级应用开发和敏捷WEB应用开发而诞生的。ThinkPHP可以支持windows/Unix/Liunx等服务器环境,正式版需要PHP5.0以上版本支持,支持MySql、PgSQL、Sqlite以及PDO等多种数据库
ThinkPHP内置了分布式数据库的支持,包括主从式数据库的读写分离,但是分布式数据库必须是相同的数据库类型。
配置DB_DEPLOY_TYPE
为1 可以采用分布式数据库支持。如果采用分布式数据库,定义数据库配置信息的方式如下:
在读写分离的情况下,默认第一个数据库配置是主服务器的配置信息,负责写入数据,如果设置了DB_MASTER_NUM
参数,则可以支持多个主服务器写入。其它的都是从数据库的配置信息,负责读取数据,数量不限制。每次连接从服务器并且进行读取操作的时候,系统会随机进行在从服务器中选择。
还可以设置DB_SLAVE_NO
指定某个服务器进行读操作。
小结:
TP框架内置的分布式数据库处理,在默认的情况下,如果没有设置DB_MASTER_NUM
参数,就会是默认第一个数据库作为写操作,其他是读数据库;如果设置了DB_MASTER_NUM
参数,例如上面的数据库配置信息里面把DB_MASTER_NUM
参数设置为2,在没有并发的情况下,写操作一直会是192.168.33.111这个数据库
YII2
主从和读写分离
许多数据库支持数据库复制来获得更好的数据库可用性,以及更快的服务器响应时间。 通过数据库复制功能, 数据从所谓的主服务器被复制到从服务器。 所有的写和更新必须发生在主服务器上, 而读可以发生在从服务器上。
为了利用数据库复制并且完成读写分离,你可以按照下面的方法来配置 yii\db\Connection 组件:
上述的配置指定了一主多从的设置。这些从库其中之一将被建立起连接并执行读操作, 而主库将被用来执行写操作。 这样的读写分离将通过上述配置自动地完成。 比如,
Info: 通过调用 yii\db\Command::execute() 来执行的语句都被视为写操作, 而其他所有通过调用 yii\db\Command 中任一 "query" 方法来执行的语句都被视为读操作。你可以通过 Yii::$app->db->slave
来获取当前有效的从库连接
Connection
组件支持从库间的负载均衡和失效备援, 当第一次执行读操作时, Connection
组件将随机地挑选出一个从库并尝试与之建立连接, 如果这个从库被发现为”挂掉的“, 将尝试连接另一个从库。如果没有一个从库是连接得上的, 那么将试着连接到主库上。 通过配置 server statuscache,一个“挂掉的”服务器将会被记住, 因此,在一个 yii\db\Connection::serverRetryInterval内将不再试着连接该服务器。
Info: 在上面的配置中, 每个从库都共同地指定了 10 秒的连接超时时间, 这意味着,如果一个从库在 10 秒内不能被连接上, 它将被视为“挂掉的”。 你可以根据你的实际环境来调整该参数。
你也可以配置多主多从。例如,
上述配置指定了两个主库和两个从库。 Connection
组件在主库之间,也支持如从库间般的负载均衡和失效备援。 唯一的差别是, 如果没有主库可用,将抛出一个异常
注意: 当你使用 masters 属性来配置一个或多个主库时, 所有其他指定数据库连接的属性 (例如 dsn
, username
, password
) 与Connection
对象本身将被忽略。
默认情况下, 事务使用主库连接,一个事务内, 所有的数据库操作都将使用主库连接, 例如,
如果你想在从库上开启事务,你应该明确地像下面这样做:
有时,你或许想要强制使用主库来执行读查询。 这可以通过 useMaster()
方法来完成:
Laravel5
Laravel5读写分离配置比较简单,只需修改config/database.PHP,下面以MySQL数据库为例 内容如下 'mysql' => [
]
设置完毕之后,Laravel5默认将select的语句让read指定的数据库执行,insert/update/delete则交给write指定的数据库,达到读写分离的作用。 这些设置对原始查询raw queries,查询生成器query builder,以及对象映射 Eloquent 都生效。 官网解释如下:
Sometimes you maywish to use one database connection for SELECT statements, and another forINSERT, UPDATE, and DELETE statements. Laravel makes this a breeze, and theproper connections will always be used whether you are using raw queries, thequery builder, or the Eloquent ORM
二,实现原理
Laravel5读写分离主要有两个过程: 第一步,根据database.php配置,创建写库和读库的链接connection 第二步,调用select时先判断使用读库还是写库,而insert/update/delete统一使用写库
三,源码分析:根据database.php配置,创建写库和读库的链接connection
主要文件:Illuminate/Database/Connectors/ConnectionFactory.php来看看几个重要的函数:
1,判断database.php是否配置了读写分离数据库
2,看看如何创建读库和写库的链接
3,多个读库会选择哪个呢
4,写库也是随机选择的
总结:
1,可以设置多个读库和多个写库,或者不同组合,比如一个写库两个读库
2,每次只创建一个读库链接和一个写库链接,从多个库中随机选择一个;
四,源码分析:调用select时先判断使用读库还是写库,而insert/update/delete统一使用写库
主要文件:Illuminate/Database/Connection.php看看几个重要的函数
1,select函数根据第三个输入参数判断使用读库还是写库
2, insert/update/delete统一使用写库
总结:
1,getReadPdo()获得读库链接,getPdo()获得写库链接;
2,select()函数根据第三个参数判断使用读库还是写库;
五,强制使用写库
有时候,我们需要读写实时一致,写完数据库后,想马上读出来,那么读写都指定一个数据库即可。虽然Laravel5配置了读写分离,但也提供了另外的方法强制读写使用同一个数据库。
实现原理:上面$this->select()时指定使用写库的链接,即第三个参数useReadPdo设置为false即可
有几个方法可实现 1,调用方法 DB::table('posts')->selectFromWriteConnection('*')->where('id',$id);
源码解释:通过selectFromWriteConnection()函数主要文件:Illuminate/Database/Connection.php
2,调用方法
User::onWriteConnection()->find($id);
源码解释:通过onWriteConnection()函数主要文件:Illuminate/Database/Eloquent/Model
看看querybuilder如何指定使用写库 主要文件:Illuminate/Database/Query/Builder
CI
Codeigniter怎么实现读写分离,并且需要满足以下两点:
1、读写分离对开发应该透明。
网上有方案通过手动load多个DB来实现读写分离,这样的分离跟业务关联太紧,增加了开发难度也不利于维护,我们要做的是默认读重库,写则写主库,读写分离对开发者透明
2、配置简单。
保留现有的配置方式,通过增加一个数组来配置读写分离,不影响原有使用方式。
思路
1、要实现读写分离最简单的思路就是在最终执行查询的地方根据查询语句判断是插入主库还是读取从库,所以需要找到该函数。
2、应该只连接一次数据库,下次操作该链接应当可复用。也就是连一次重库后所有的读操作都可用,不需再次连接,主库同理。所以我们可以将链接放在CI超级对象中。
3、主从的判断是根据最终执行的SQL语句来判断的,所以数据库配置中的自动链接autoinit参数就不用设置为true了,如果默认连接了而又不需要操作该库就浪费资源了。
4、模型中可以使用$this->db来直接操作查询,不需要其他调整。
5、不直接修改system下的文件
实现读写分离
CI的DB类固定为读取system下的文件,我们可以通过适当的重写来实现。首先是Loader.php,其中的database方法用来加载数据库对象,固定引用了system/database/DB.php文件,我们判断下是否存在自定义DB.php文件,存在则引入。
重写Loader.php
接着我们在application/core下创建database/DB.php,该文件只有一个DB方法,用来读取配置文件并进行初始化工作。同样有两处地方需要重写下:
整个DB.php调整的也基本上是文件的引入,group name的引入是为了方便后面的判断,不引入则可以通过主机、数据库名称这些来配置。如果想强制关闭autoint,可以在DB.php中删掉下面这段:
接下来就是最核心的地方。根据查询语句实现读写分离。
DB_driver.php中的simple_query方法可以理解为最后执行SQL语句的方法,我们可以在这里进行数据库链接的判断。
重写DB_driver.php
到这里读写分离即基本实现了,但做事情得善始善终,链接的数据库对象需要关闭,可以在公用控制器中执行完毕后关掉连接。
DB_driver.php中也有close方法,可以考虑下是否可以在该方法中关闭?这里认为是不行的。
关闭数据库链接
模型中的使用,为了使每个model中都可使用$this->db,以及不多次连接数据库,这里也是将链接放在CI超级对象中。这里就算不读写分离也可以这么处理,可以很方便的连接多个DB,具体的model要使用其他库只需要在构造函数中传入group name即可。
模型调整
最后的数据库配置方式,只需要在原有的基础上配置一个数组即可。是使用双主还是一主多从就看这里的配置方式。最开始想到直接在原配置上加键名来处理,但主与从的对应关系还是没有这样子明了,这里的定义方式决定了load_db_proxy_setting的实现方式。
database.php配置
最开始的数据库链接并未放到CI超级对象中,发现load多个模型时每次都会打开链接,所以完成读写分离之后一定要测试,可以在数据库链接打开和关闭的地方查看是否按预期执行(方法对应application/core/database/drivers/mysql/mysql_driver.php中的db_connect和_close)。整个调整过程最重要的两点就是simple_query方法以及构造函数中关闭数据库链接。模型中的调整是为了更方便的链接多个库,未实现读写分离时也是这么调整的,常用的方法独立成一个文件,MY_Model去继承。