问题:为 SilverStripe 网站添加只读副本

我已经设法获得了一个稳定的负载平衡前端服务器,它可以很好地水平扩展,但是下一个瓶颈将是 db。有一篇博客文章讨论了水平扩展 dbs,但是关于它的细节很少。我目前正在使用 PostgreSQL,所以我发现的唯一插件不起作用。

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

我使用 AWS 进行所有托管

解答

首先-我很想对此进行纠正!

仅快速浏览了 SilverStripe 3.5 站点中的一些 ORM 类,看起来虽然 ORM _确实支持_多个数据库连接(请参阅带有名称参数的DB::get_conn),但它是为特定用例而设计的。也就是说,您可能有一个模块需要写入特定的数据库,所以这将允许它。

您想要的是在框架内对此进行本机和自动支持,以便所有读取都转到您的从属服务器,而写入转到您的主服务器。不幸的是,它看起来并不是开箱即用的。您可以通过使用注入器重载几个核心 SQL 类来实现它。

如果您要尝试_它,这个答案概述了如何将选择语句与其他语句分开并通过不同的数据库连接器运行它们。

作为如何使用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 数据库适配器,您可能能够利用读/写拆分,通过某种注入器重载实现,但所有处理都在比应用程序层更低的级别。

Logo

PostgreSQL社区为您提供最前沿的新闻资讯和知识内容

更多推荐