【问题标题】:Read/Write splits using Zend_Db使用 Zend_Db 读/写拆分
【发布时间】:2011-09-04 18:16:37
【问题描述】:

我有一个变大的 PHP 应用程序。数据库曾经位于单个主服务器上,但我们打算通过相当标准的主/从复制来改变它,以提高性能和 HA。

由于这个应用程序的读取量很大,我希望将读取委托给从属副本,并将写入分配给主副本。

该应用程序基于 Zend Framework 1.1.10 并使用 Zend_Db。

在不过多重构代码的情况下,让这个应用程序拆分对数据库的读取和写入的最佳策略是什么? (我意识到这里可能会涉及一些重构)。

PS:

我查看了MySQL Proxy,它似乎可以通过坐在数据库服务器和应用程序之间透明地拆分读取和写入,但我不确定在生产环境中使用它会出现性能问题。有人有这方面的经验吗?

【问题讨论】:

    标签: php mysql zend-framework replication


    【解决方案1】:

    正如你所说,MySQlProxy 可以是一个解决方案,但我个人从未在生产中对其进行过测试。

    我在代码中使用 2 个 Db 连接来拆分写入和读取请求。 80% 的日常任务都是通过读连接完成的。您可以使用Zend_Application_Resource_Multidb 来处理它(对我来说,这部分我很久以前就完成了,我只是在注册表中存储了第二个 Db 连接)。

    • 首先将您的用户权限限制在 读取操作并创建另一个数据库 具有写入权限的用户。
    • 然后跟踪每个写入请求 您的代码(“更新”、“插入”、 “删除”是一个好的开始)并尝试 用专门的电话拨打所有这些电话 帮手。
    • 运行您的应用并观察它崩溃,然后修复问题 :-)

    一开始就想到这个问题会更容易。例如:

    • 我通常有一个 Zend_Db_Table 工厂,接受一个“读”或“写”参数,并给我一个正确的 Zend_Db_Table 的单例(一个双单例,我可以有一个读实例和一个写实例)。然后我只需要确保在使用写访问查询/操作时使用正确的初始化 Zend_Db_Table。请注意,将 Zend_Db_Table 用作单例时,内存使用情况要好得多。
    • 我尝试在 TransactionHandler 中获取所有写操作。我在那里我可以检查我只使用与正确连接链接的对象。然后在控制器上管理事务,我从不尝试在数据库层中管理事务,所有启动/提交/回滚的想法都是在控制器(或另一个概念层,但不是 DAO 层)上完成的。

    最后一点,交易,很重要。如果您想管理事务,请务必使用启用写入的连接在事务内部发出 READ 请求。由于在事务之前完成的所有读取都应该被认为是过时的,并且如果您的数据库后端正在执行隐式锁,则您必须发出读取请求以获取锁。如果您的数据库后端没有进行隐式读取,那么您还必须在事务中执行行锁。 这意味着您不应该依赖 SELECT 关键字在只读连接上推送该请求。

    如果您的应用程序中使用了良好的数据库层,那么更改并不难。如果你用你的数据库/DAO 层做了一些混乱的事情,那么......它可能会更难。

    【讨论】:

    • 感谢您的提示...您是对的,如果我从一开始就考虑到这个问题会更容易。
    【解决方案2】:

    h2。禅德

    我刚刚修补了 Zend PDO_MYSQL 以分离读写连接。 为此,您只需在应用程序配置中指定其他参数:

    'databases' => array (
        'gtf' => array(
            'adapter' => 'PDO_MYSQL',
            'params' => array(
                'host' => 'read.com',
                'host_write' => 'write-database-host.com',
                'dbname' => 'database',
                'username' => 'reader',
                'password' => 'reader',
                'username_write' => 'writer',
                'password_write' => 'writer',
                'charset' => 'utf8'
            )
        ),
    

    这里所有的“SELECT ...”查询都将使用host。 所有其他查询将使用 *host_write*。 如果未指定 host_write,则所有查询都使用 host

    补丁:

    diff --git a/Modules/Tools/Externals/Zend/Db/Adapter/Abstract.php b/Modules/Tools/Externals/Zend/Db/Adapter/Abstract.php
    index 5ed3283..d6fccd6 100644
    --- a/Modules/Tools/Externals/Zend/Db/Adapter/Abstract.php
    +++ b/Modules/Tools/Externals/Zend/Db/Adapter/Abstract.php
    @@ -85,6 +85,14 @@ abstract class Zend_Db_Adapter_Abstract
          * @var object|resource|null
          */
         protected $_connection = null;
    +    
    +    
    +    /**
    +     * Database connection
    +     *
    +     * @var object|resource|null
    +     */
    +    protected $_connection_write = null;
    
         /**
          * Specifies the case of column names retrieved in queries
    @@ -299,10 +307,13 @@ abstract class Zend_Db_Adapter_Abstract
          *
          * @return object|resource|null
          */
    -    public function getConnection()
    +    public function getConnection($read_only_connection = true)
         {
             $this->_connect();
    -        return $this->_connection;
    +        if (!$read_only_connection && $this->_connection_write)
    +            return $this->_connection_write;
    +        else
    +            return $this->_connection;
         }
    
         /**
    diff --git a/Modules/Tools/Externals/Zend/Db/Adapter/Pdo/Abstract.php b/Modules/Tools/Externals/Zend/Db/Adapter/Pdo/Abstract.php
    index d7f6d8a..ee63c59 100644
    --- a/Modules/Tools/Externals/Zend/Db/Adapter/Pdo/Abstract.php
    +++ b/Modules/Tools/Externals/Zend/Db/Adapter/Pdo/Abstract.php
    @@ -57,7 +57,7 @@ abstract class Zend_Db_Adapter_Pdo_Abstract extends Zend_Db_Adapter_Abstract
          *
          * @return string
          */
    -    protected function _dsn()
    +    protected function _dsn($write_mode = false)
         {
             // baseline of DSN parts
             $dsn = $this->_config;
    @@ -65,10 +65,15 @@ abstract class Zend_Db_Adapter_Pdo_Abstract extends Zend_Db_Adapter_Abstract
             // don't pass the username, password, charset, persistent and driver_options in the DSN
             unset($dsn['username']);
             unset($dsn['password']);
    +        unset($dsn['username_write']);
    +        unset($dsn['password_write']);
             unset($dsn['options']);
             unset($dsn['charset']);
             unset($dsn['persistent']);
             unset($dsn['driver_options']);
    +        
    +        if ($write_mode) $dsn['host'] = $dsn['host_write'];
    +        unset($dsn['host_write']);
    
             // use all remaining parts in the DSN
             foreach ($dsn as $key => $val) {
    @@ -91,9 +96,6 @@ abstract class Zend_Db_Adapter_Pdo_Abstract extends Zend_Db_Adapter_Abstract
                 return;
             }
    
             // get the dsn first, because some adapters alter the $_pdoType
             $dsn = $this->_dsn();
    +        if ($this->_config['host_write'])
    +            $dsn_write = $this->_dsn(true);
    
             // check for PDO extension
             if (!extension_loaded('pdo')) {
                 /**
    @@ -120,14 +122,28 @@ abstract class Zend_Db_Adapter_Pdo_Abstract extends Zend_Db_Adapter_Abstract
                 $this->_config['driver_options'][PDO::ATTR_PERSISTENT] = true;
             }
    
             try {
                 $this->_connection = new PDO(
    -                $dsn,
    +                $dsn_read,
                     $this->_config['username'],
                     $this->_config['password'],
                     $this->_config['driver_options']
                 );
    
    +            if ($this->_config['host_write']) {
    +                $this->_connection_write = new PDO(
    +                    $dsn_write,
    +                    $this->_config['username_write'],
    +                    $this->_config['password_write'],
    +                    $this->_config['driver_options']
    +                );
    +            }
    +            
                 $this->_profiler->queryEnd($q);
    
                 // set the PDO connection to perform case-folding on array keys, or not
    diff --git a/Modules/Tools/Externals/Zend/Db/Statement/Pdo.php b/Modules/Tools/Externals/Zend/Db/Statement/Pdo.php
    index 8bd9f98..4ab81bf 100644
    --- a/Modules/Tools/Externals/Zend/Db/Statement/Pdo.php
    +++ b/Modules/Tools/Externals/Zend/Db/Statement/Pdo.php
    @@ -61,8 +61,11 @@ class Zend_Db_Statement_Pdo extends Zend_Db_Statement implements IteratorAggrega
          */
         protected function _prepare($sql)
         {
    +        
    +        $read_only_connection = preg_match("/^select/i", $sql);
    +        
             try {
    -            $this->_stmt = $this->_adapter->getConnection()->prepare($sql);
    +            $this->_stmt = $this->_adapter->getConnection($read_only_connection)->prepare($sql);
             } catch (PDOException $e) {
                 require_once 'Zend/Db/Statement/Exception.php';
                 throw new Zend_Db_Statement_Exception($e->getMessage());
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-10-20
      • 2014-11-12
      • 2017-09-01
      • 1970-01-01
      • 2014-12-13
      • 2014-10-22
      相关资源
      最近更新 更多