Чтение / запись разделений с использованием Zend_Db - PullRequest
2 голосов
/ 30 мая 2011

У меня есть PHP-приложение, которое выросло в размере. Раньше база данных была на одном ведущем устройстве, но мы намерены изменить это с помощью довольно стандартной репликации ведущий / ведомый для производительности и высокой доступности.

Поскольку это приложение предназначено для чтения, я бы хотел, чтобы операции чтения были делегированы подчиненным репликам, а записи выполнялись ведущему.

Приложение основано на Zend Framework 1.1.10 и использует Zend_Db.

Какова была бы моя лучшая стратегия для того, чтобы приложение делило чтение и запись в БД без чрезмерного рефакторинга кода? (Я понимаю, что, возможно, здесь будет какой-то рефакторинг).

P.S:

Я смотрел на MySQL Proxy и, похоже, он может прозрачно разделять операции чтения и записи, сидя между сервером БД и приложением, но я не уверен в проблемах производительности, использующих это в производственная среда. У кого-нибудь есть опыт с этим?

Ответы [ 2 ]

3 голосов
/ 30 мая 2011

Как вы сказали, MySQlProxy может быть решением, но я лично никогда не тестировал его на производстве.

Я использую соединения 2 дБ в своем коде для разделения запросов на запись и чтение.80% обычных задач выполняются с подключением чтения.Вы можете использовать Zend_Application_Resource_Multidb , чтобы справиться с этим (для меня я сделал эту часть задолго до этого и просто храню второе соединение с БД в реестре).

  • Сначала ограничьте вашправа пользователя только на операцию чтения и создайте другого пользователя БД с авторизацией на запись.
  • , затем отслеживайте каждый запрос на запись в вашем коде («обновление», «вставка», «удаление» - хорошее начало) и попытайтесь сделатьвсе эти вызовы с выделенным помощником.
  • запустите ваше приложение и посмотрите, как оно падает, а затем исправьте проблемы: -)

Это проще, когда вы думаете об этой проблеме в начале.Например:

  • У меня обычно есть фабрика Zend_Db_Table, принимающая параметр 'read' или 'write' и дающая мне синглтон нужной Zend_Db_Table (двойной синглтон, если я могу прочитатьэкземпляр и экземпляр записи).Тогда мне нужно только убедиться, что я использую правильно инициализированный Zend_Db_Table, когда я использую запросы / операции доступа для записи.Обратите внимание, что использование памяти намного лучше при использовании Zend_Db_Table в качестве синглетонов.
  • Я пытаюсь получить все операции записи в TransactionHandler.Я там могу проверить, я использую только объекты, связанные с правильным соединением.Затем транзакциями управляют на контроллерах, я никогда не пытаюсь управлять транзакциями на уровнях базы данных, все мысли о запуске / фиксации / откате выполняются на контроллерах (или другом концептуальном уровне, но не на уровне DAO).

Этот последний пункт, транзакции, важен.Если вы хотите управлять транзакцией, важно сделать запросом READ ВНУТРИ транзакции с подключением WRITE .Поскольку все операции чтения, выполненные до транзакции, следует рассматривать как устаревшие, и если ваша база данных выполняет блокировки, вам придется выполнить запрос на чтение, чтобы получить блокировки.Если ваша база данных не выполняет неявное чтение, вам придется также выполнять блокировку строк в транзакции. И это означает, что вы не должны полагаться на ключевое слово SELECT, чтобы выдвинуть этот запрос на соединение только для чтения.

Если у вас есть хорошее использование уровня БД в вашем приложении, изменение неочень трудно сделать.Если вы сделали хаотичные вещи со своим слоем базы данных / DAO, тогда ... это может быть сложнее.

2 голосов
/ 02 декабря 2012

h2.Zend

Я только что установил 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 .

Patch:

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());
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...