为SilverStripe网站添加只读副本

时间:2017-03-13 04:40:38

标签: php postgresql load-balancing silverstripe

我设法获得了一个稳定的负载平衡前端服务器,可以水平扩展,但下一个瓶颈将是数据库。有一个blog post在水平方向上讨论dbs,但是它的细节很少。我目前正在使用PostgreSQL,因此我发现的唯一plugin无效。

我唯一的选择是创建自己的HAProxy还是重写PostgreSQL插件以允许与只读副本连接?

我使用AWS进行所有托管

1 个答案:

答案 0 :(得分:1)

首先 - 我很乐意纠正这个问题!

只快速查看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),但这可能会影响该类的其他子实现的查询转换,并且你赢了#39在没有(A)修复框架修复或(B)重载所有其他SQL *类的情况下,能够做很多事情。

此时,您应该拥有将选择查询路由到default_read连接所需的一切。

基础设施

在基础结构方面,您应该能够通过RDS控制台设置只读副本。执行此操作时,它将为您的副本节点提供DNS端点,您可以在_config.php中使用该端点配置与只读副本数据库的连接。

如果这对您有用,您应该为它创建一个模块并将其放在GitHub上 - 这对其他人来说肯定会有用!

您还可以考虑向框架发出拉取请求,以便为DB::prepared_query等方法添加其他参数以接受连接名称。

另外值得注意的是,如果您正在使用mysqlnd数据库适配器,您可以利用read/write splitting,通过某种注入器重载实现,但所有处理都低于应用程序层