Работа с MySQL кластером в Doctrine

// Июль 14th, 2010 // Без рубрики

В этой статье я хочу рассказать, как настроить Doctrine таким образом, чтобы использовать master-сервера для записи, а slave-сервера для чтения данных. Эта стратегия позволит добиться распределения нагрузки между серверами БД. Речь идет именно о нескольких серверах, на которых реплицируются базы, а не о ndbcluster, как некоторые могли бы подумать.

Итак, сначала мы должны сконфигурировать все дсотупные соединения в Doctrine.

$connections = array(
    'master'  => 'mysql://root:@master/dbname',
    'slave_1' => 'mysql://root:@slave1/dbname',
    'slave_2' => 'mysql://root:@slave2/dbname',
    'slave_3' => 'mysql://root:@slave3/dbname',
    'slave_4' => 'mysql://root:@slave4/dbname'
);

foreach ($connections as $name => $dsn) {
    Doctrine_Manager::connection($dsn, $name);
}

Теперь, когда у нас есть один master-сервер, и 4 slave-сервера мы можем переписать классы Doctrine_Record и Doctrine_Query для добавления логики по разделению запросов для чтения и для записи. Все запросы на запись мы будем отправлять на master-сервер, а все запросы на чтение на случайно выбранный slave-сервер. См. статью про мульти-мастер репликацию

class MyQuery extends Doctrine_Query
{
    // Since php doesn't support late static binding in 5.2 we need to override
    // this method to instantiate a new MyQuery instead of Doctrine_Query
    public static function create($conn = null)
    {
        return new MyQuery($conn);
    }

    public function preQuery()
    {
        // If this is a select query then set connection to one of the slaves
        if ($this->getType() == Doctrine_Query::SELECT) {
            $this->_conn = Doctrine_Manager::getInstance()->getConnection('slave_' . rand(1, 4));
        // All other queries are writes so they need to go to the master
        } else {
            $this->_conn = Doctrine_Manager::getInstance()->getConnection('master');
        }
    }
}

Теперь DQL-запросы будут распределятсья между серверами, а что делать с сохранением записи? Мы сможем указать соединение дял записи, переписав класс Doctrine_Record, и используя его как базовый для наших моделей.

abstract class MyRecord extends Doctrine_Record
{
    public function save(Doctrine_Connection $conn = null)
    {
        // If specific connection is not provided then lets force the connection
        // to be the master
        if ($conn === null) {
            $conn = Doctrine_Manager::getInstance()->getConnection('master');
        }
        parent::save($conn);
    }
}

Отлично! Теперь запросы на чтение буду траспределены между slave-серверами, а на запись пойдут на master-сервер. Вот несколько примеров работы запросоов и моделей:

class User extends MyRecord
{
    public function setTableDefinition()
    {
      $this->setTableName('user');
      $this->hasColumn('username', 'string', 255, array('type' => 'string', 'length' => '255'));
      $this->hasColumn('password', 'string', 255, array('type' => 'string', 'length' => '255'));
    }
}

// The save() method will happen on the master connection because it is a write
$user = new User();
$user->username = 'jwage';
$user->password = 'changeme';
$user->save();

// This query goes to one of the slaves because it is a read
$q = new MyQuery();
$q->from('User u');
$users = $q->execute();

print_r($users->toArray(true));

// This query goes to the master connection because it is a write
$q = new MyQuery();
$q->delete('User')
  ->from('User u')
  ->execute();
Share

Спасибо!


Если вам помогла статья, или вы хотите поддержать мои исследования и блог - вот лучший способ сделать это:


Комментировать