首先 - 我很乐意在这方面得到纠正!
仅快速浏览了 SilverStripe 3.5 站点中的一些 ORM 类,看起来 ORM 确实支持多个数据库连接(参见 DB::get_conn 名称参数)专为特定用例而设计。也就是说,您可能有一个模块需要写入特定的数据库,所以这将允许它。
您想要的是在框架内对此提供本机和自动支持,以便所有读取都转到您的从属服务器,而写入转到您的主服务器。不幸的是,它看起来并不是开箱即用的。您可以通过使用注入器重载几个核心 SQL 类来实现它。
如果您要尝试它,this answer 概述了如何将 select 语句与其他语句分开并通过不同的数据库连接器运行它们。
作为一个快速示例,说明如何使用 SQLSelect 实现此目的,您会注意到它是可注入的,这意味着您可以轻松地对其进行重载。
文件:mysite/_config/injector.yml
Injector:
SQLSelect:
class: ReadOnlySQLSelect
您需要向 DB 类注册一个新的数据库连接:
文件:mysite/_config.php
$readDatabaseConfig = array(/** define your DB credentials here, as with the default $databaseConfig **/);
if (!DB::connect($readDatabaseConfig, 'default_read')) {
user_error('Failed to connect to read replica DB!', E_USER_ERROR);
}
现在,重载 SQLSelect 类并替换它调用 DB 类方法的部分。该类继承自 SQLExpression,该类包含您在此实例中实际关心的方法:
文件:mysite/code/ReadOnlySQLSelect.php
class ReadOnlySQLSelect extends SQLSelect
{
public function sql(&$parameters = array())
{
// Changed from SQLExpression: third parameter passed as connection name
$sql = DB::build_sql($this, $parameters, 'default_read');
if (empty($sql)) {
return null;
}
if ($this->replacementsOld) {
$sql = str_replace($this->replacementsOld, $this->replacementsNew, $sql);
}
return $sql;
}
public function execute()
{
$sql = $this->sql($parameters);
// Changed from SQLExpression: skip DB::prepared_query since it doesn't allow
// you to provide the connection name - replace it with its contents instead.
$conn = DB::get_conn('default_read');
return $conn->preparedQuery($sql, $parameters);
}
}
注意: SQLSelect::unlimitedRowCount 在技术上应该在它调用DB::prepared_query 的地方被替换,因为准备好的查询方法调用DB::get_conn 没有参数,所以总是返回默认连接。您可以将DB::prepared_query 替换为与上面相同的行:
$conn = DB::get_conn('default_read');
$result = $conn->preparedQuery($sql, $innerParameters);
如果你实现了上面的方法,还要把new SQLSelect()改成SQLSelect::create(),否则你最终会得到一些仍然访问主服务器的查询,因为它会通过不使用注入器绕过你的类。
SQLConditionalExpression 中还有一个实例,您也应该替换它 (::toSelect),但这可能会影响来自该类的其他子实现的查询转换,如果没有它,您将无法做很多事情(A) PRing 修复框架或 (B) 重载所有其他 SQL* 类。
此时,您应该拥有将选择查询路由到default_read 连接所需的一切。
基础设施
在基础架构方面,您应该能够通过 RDS 控制台设置只读副本。当您这样做时,它将为您的副本节点提供一个 DNS 端点,您可以在 _config.php 中使用它来配置与只读副本数据库的连接。
如果这对你有用,你应该为它创建一个模块并将它放在 GitHub 上——这对未来的其他人肯定有用!
您也可以考虑向框架发出拉取请求,以向 DB::prepared_query 等方法添加其他参数以接受连接名称。
另外值得注意的是,如果您使用的是 mysqlnd 数据库适配器,您可能能够利用 read/write splitting,它通过某种注入器重载实现,但所有处理都在低于应用程序层的级别。