簡體   English   中英

使用 Zend_Db 讀/寫拆分

[英]Read/Write splits using Zend_Db

我有一個 PHP 應用程序的大小已經增加。 數據庫曾經位於單個主服務器上,但我們打算通過相當標准的主/從復制來改變它,以提高性能和 HA。

由於這個應用程序是讀取繁重的,我希望將讀取委托給從屬副本並將寫入分配給主副本。

該應用程序基於 Zend Framework 1.1.10 並使用 Zend_Db。

讓這個應用程序在不過多重構代碼的情況下拆分對數據庫的讀取和寫入的最佳策略是什么? (我意識到這里可能會涉及一些重構)。

PS:

我查看了MySQL 代理,它似乎可以通過坐在數據庫服務器和應用程序之間透明地拆分讀取和寫入,但我不確定在生產環境中使用它會出現性能問題。 這個事情誰有經驗?

正如您所說,MySQlProxy 可以是一個解決方案,但我個人從未在生產中對其進行過測試。

我在代碼中使用 2 個 Db 連接來拆分寫入和讀取請求。 80% 的日常任務都是通過讀連接完成的。 您可以使用Zend_Application_Resource_Multidb來處理它(對我來說,我很久以前就完成了這部分,我只是在注冊表中存儲了第二個 Db 連接)。

  • 首先將您的用戶權限限制在讀取操作上,然后創建另一個具有寫入權限的數據庫用戶。
  • 然后跟蹤代碼中的每個寫入請求(“更新”、“插入”、“刪除”是一個好的開始)並嘗試使用專用幫助程序進行所有這些調用。
  • 運行您的應用程序並觀察它崩潰,然后修復問題:-)

當您一開始就想到這個問題時,它會更容易。 例如:

  • 我通常有一個 Zend_Db_Table 工廠,采用“讀”或“寫”參數,並給我一個正確的 Zend_Db_Table 的 Singleton(一個雙 singleton,我可以有一個讀實例和一個寫實例)。 然后我只需要確保在使用寫訪問查詢/操作時使用正確的初始化 Zend_Db_Table。 請注意,當使用 Zend_Db_Table 作為單例時,memory 的使用要好得多。
  • 我嘗試在 TransactionHandler 中獲取所有寫操作。 我在那里我可以檢查我只使用與正確連接鏈接的對象。 然后在控制器上管理事務,我從不嘗試在數據庫層中管理事務,所有啟動/提交/回滾的想法都是在控制器(或另一個概念層,但不是 DAO 層)上完成的。

最后一點,交易,很重要。 如果您想管理事務,重要的是在事務內部發出 READ 請求,並使用啟用 WRITE 的連接 由於在事務之前完成的所有讀取都應該被認為是過時的,並且如果您的數據庫后端正在執行隱式鎖,則您必須發出讀取請求以獲取鎖。 如果您的數據庫后端沒有進行隱式讀取,那么您還必須在事務中執行行鎖。 這意味着您不應該依賴 SELECT 關鍵字在只讀連接上推送該請求。

如果您的應用程序中有一個很好的 db 層使用,那么更改並不難。 如果你用你的數據庫/DAO 層做了一些混亂的事情,那么......它可能會更難。

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());

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM