在用phalcon框架开发了一段时间项目之后,才发现需要做数据库读写分离,这时候就有一个问题了。
由于开发的时候没有安照phalcon官方推荐方式写程序,这时候有个问题,就是在使用到事务的时候,当事务中写操作没有提交,就直接读取刚才生成数据的时候,就发生了问题,写没有提交自然就读不到。
这时候:采用强制读操作在写库中进行,这样就能解决问题。
保证所有model继承一个自定义的baseModel ,并且全部继承parant
public function initialize()
{
parent::initialize();
}
baseModel需要进行两个方法的实现:
public function initialize()
{
$this->setWriteConnectionService('db');
$this->setReadConnectionService('dbRead');
}
//refresh需要重写下,否则有问题得不到未提交的数据
public function refresh()
{
if ($this->getWriteConnection()->isUnderTransaction()) {
$this->setReadConnectionService('db');
}
return parent::refresh();}
在services中DI注入db和dbRead两个服务,db主库(其实dbWrite也可以,但是为了保证$this->db的一些方法兼容继续使用),dbRead从库
//默认库(为了兼容现有$this->db->操作)
$di->setShared('db', function () use ($di, $configEnv) {
$connection = new DbAdapter([
"host" => $configEnv->database->host,
"username" => $configEnv->database->username,
"password" => $configEnv->database->password,
"dbname" => $configEnv->database->dbname,
"charset" => "utf8"
]);
return $connection;
});
// 读(从)库
$di->setShared('dbRead', function () use ($di, $configEnv) {
$connection = new DbAdapter([
"host" => $configEnv->r_database->host,
"username" => $configEnv->r_database->username,
"password" => $configEnv->r_database->password,
"dbname" => $configEnv->r_database->dbname,
"charset" => "utf8"
]);
return $connection;
});
通过以上步骤,基本完成读写分离,但是由于前期可能不是官方读写分离写法,尤其是用到事务的时候,事务包含过程过大的时候:
如果此时刚创建完一个数据,但是事务还没有提交,下方有某个方法中已经使用了find及findFrist方法,此时是肯定读不到数据的,
所有需要将这部分的读强制的置为在主(写)库中进行读,这样事务还未提交的数据就能被查到了。
做法如下:
1:
$order = \Phalcon\Di::getDefault()->getShared('db')->fetchOne("select * from order_freight where id={$order_id} limit 1");
数字结果,强制转对象:$order = (object)$order;
2:
此方法更优,对框架原生支持比较好
$history = new AddressHistory();
$history = $history->setReadConnectionService('db');
$obj = $history->findFirst([
'conditions' => "address_id = ?1 and is_latest = 1",
'bind' => [1 => 15794],
]);
上述为后期弥补做法,官方推荐事务写法如下:
<?php
use Phalcon\Mvc\Model\Transaction\Failed as TxFailed;
use Phalcon\Mvc\Model\Transaction\Manager as TxManager;
try {
// Create a transaction manager
$manager = new TxManager();
// Request a transaction
$transaction = $manager->get();
$robot = new Robots();
$robot->setTransaction($transaction);
$robot->name = 'WALL·E';
$robot->created_at = date('Y-m-d');
if ($robot->save() === false) {
$transaction->rollback(
'Cannot save robot'
);
}
$robotPart = new RobotParts();
$robotPart->setTransaction($transaction);
$robotPart->robots_id = $robot->id;
$robotPart->type = 'head';
if ($robotPart->save() === false) {
$transaction->rollback(
'Cannot save robot part'
);
}
// Everything's gone fine, let's commit the transaction
$transaction->commit();
} catch (TxFailed $e) {
echo 'Failed, reason: ', $e->getMessage();
}