Comment obtenir du noyau pour tirer parti d'une configuration maître / esclave MySQL?

21

J'ai lu cette question La réplication maître / esclave MySQL ne fonctionne pas et sa réponse:

L'utilisation de bases de données esclaves est à peine implémentée dans le noyau Drupal. Si vous développez vos propres modules, les appels à db_query doivent spécifier qu'ils souhaitent utiliser la base de données esclave à l'aide du tableau $ options. Voir DatabaseConnection :: defaultOptions pour savoir comment définir ce tableau.

Existe-t-il un moyen sans tuer les chatons piratant le noyau pour obtenir db_query()et db_select()faire plus de requêtes SELECT esclaves?

Par défaut, ces fonctions interrogeront le maître, sauf indication contraire d'interroger l'esclave (voir leur API). Vous devez écrire db_query($query, $args, array('target' => 'slave'))afin d'interroger l'esclave et le noyau (et tous les modules) ne sont pas écrits pour y parvenir.

Seuls la recherche (voir la partie esclave) et l'agrégateur semblent en tirer parti.

Edit: 25 octobre
J'ai vu que le pressflow 7 est sorti mais je ne sais pas si cela aide beaucoup en ce moment.
Je n'ai pas trouvé quelque chose de pertinent, alors essayons un peu de générosité pour aider à obtenir une réponse.

Edit: 31 octobre.
Je suis principalement préoccupé par les commentaires de Crell à ce sujet: Que faire des esclaves? .
Surtout, y a-t-il des problèmes si j'envoie des SELECTrequêtes à l'esclave, ce qui se passe avec des retards dans la réplication et le fait que je puisse vouloir faire node_load()juste après avoir enregistré un nouveau nœud.

tostinni
la source

Réponses:

17

Voici comment j'implémente actuellement ceci.

Vous devez d'abord configurer une classe SelectQueryExtender comme ceci:

class SlaveTarget extends SelectQueryExtender {
  public function __construct(SelectQueryInterface $query, DatabaseConnection $connection) {
    if ($connection->getTarget() != 'slave') {
      $connection = Database::getConnection('slave', $connection->getKey());
    }
    parent::__construct($query, $connection);
    $this->addTag('SlaveTarget');
  }
}

Une fois que vous avez cela, alors tout ce que vous avez à faire est d'obtenir toutes les autres requêtes pour étendre l'extension. :) si ça a du sens. Voici l'extrait.

/**
 * Implements hook_query_alter().
 */
function example_query_alter(QueryAlterableInterface $query) { 
  if (is_a($query, 'SelectQuery') && !$query->hasTag('SlaveTarget')) {
    $query->extend('SlaveTarget');
  }
}

Et maintenant, tous vos SelectQuery ont atteint l'esclave ;-) C'est la seule façon dont j'ai pu y arriver. Quoi qu'il en soit, cela fonctionne très bien.

De plus, si vous l'avez sur un module personnalisé, vous pouvez configurer le SlaveTarget pour qu'il soit dans le fichier SlaveTarget.inc et ajouter un fichier [] = SlaveTarget.inc à votre fichier d'informations sur le module.

ericduran
la source
Salut Eric, merci pour ta réponse, ce qui m'inquiète principalement c'est ce fil: Que faire des esclaves? et le commentaire de Crell concernant l'esclave . Votre solution est-elle sûre dans tous les cas? Restreignez-vous certaines SELECTrequêtes? Comment gérez-vous les retards dans la réplication et le fait que le chargement d'un nœud juste après l'avoir enregistré peut causer des problèmes?
tostinni
Cela modifie la base de données en esclave uniquement sur les requêtes Select. Cela ne se produit que lorsque la requête a été écrite avec SelectQuery et non db_query, donc pas besoin de s'inquiéter et d'insérer ou de mettre à jour le ciblage de l'esclave. Nous exécutons cela sur 3 énormes environnements de production sans aucun problème. Je ne me suis pas beaucoup inquiété de la réplication mysql car elle est presque instantanée (dans mon cas) mais je peux voir comment cela pourrait être un petit problème dans certains environnements.
ericduran
Merci pour vos réponses, c'est une excellente solution, je vais voir si c'est viable sur notre environnement.
tostinni
Eric, ce code existe-t-il quelque part en tant que module contrib ou sandbox?
paul-m
@ paul-m: voir drupal.org/project/autoslave .
smokris
5

Le module AutoSlave redirige les SELECTrequêtes vers des bases de données réplicantes en lecture seule, et il prend en compte le retard de réplication.

Selon les documents du module, il utilise uniquement le réplicateur en lecture seule lorsque toutes les conditions suivantes sont remplies:

  1. La requête est une requête de sélection
  2. Les tables de la requête de sélection n'ont pas été écrites pendant la demande et dans le délai de réplication supposé
  3. Une transaction n'a pas été démarrée
  4. Les tables de la requête de sélection ne sont pas spécifiées dans l'option «tables» des paramètres du pilote
  5. Un verrou n'a pas été démarré (core db-lock et memcache-lock pris en charge)
smokris
la source
1

de ce que j'ai entendu lors du récent Drupal BADcamp Pressflow est la voie à suivre si vous voulez des configurations maître / esclave. Vous serez limité à Mysql en tant que DB. Consultez également le " groupe haute performance " sur do

uwe
la source
1
Actuellement, Pressflow 7 = D7, il n'y a rien (encore) que Pressflow fasse que D7 ne fasse pas :(
tostinni
1

Malgré tout le travail incroyable accompli sur la couche d'abstraction de la base de données dans Drupal 7, cela reste étonnamment difficile à faire avec Drupal core prêt à l'emploi. Comme d'autres l'ont mentionné, AutoSlave est une option, bien que je n'en ai pas essayé en raison de mon refus obstiné de croire qu'il devrait être si difficile de le faire.

Une solution plus simple que j'ai trouvée est la suivante. Pour acheminer tous les SELECT s vers le serveur esclave, vous créez un fichier intitulé à l' select.incintérieur du includes/database/mysqlrépertoire principal avec le contenu suivant:

<?php

/**
 * @file
 * Select builder for MySQL database engine, routing all SELECTs to the slave.
 */

/**
 * @addtogroup database
 * @{
 */

class SelectQuery_mysql extends SelectQuery {
  public function __construct($table, $alias = NULL, DatabaseConnection $connection, $options = array()) {
    $key = $connection->getKey();
    $connection = Database::getConnection('slave', $key);
    $options['target'] = 'slave';
    parent::__construct($table, $alias, $connection, $options);
  }
}

/**
 * @} End of "addtogroup database".
 */

Il y a des risques avec cette méthode:

  1. Cette méthode détournera tous les SELECT s et les dirigera vers l'esclave, ce qui causera sans aucun doute des problèmes si vous avez un retard dans la réplication. Relisez cette phrase.
  2. Lorsque vous mettez à niveau Drupal core, il est possible que ce fichier soit supprimé.
  3. Si le noyau Drupal devait commencer à être livré avec le sien includes/database/mysql/select.inc, votre fichier serait écrasé lors de la mise à niveau, et vous devriez commencer à maintenir votre propre version corrigée du select.inc fourni avec le noyau Drupal.

Si vous n'avez aucun serveur esclave spécifié dans settings.php, le code ci-dessus ne posera pas de problème. Il se dégradera toujours avec grâce en utilisant le serveur maître .

q0rban
la source
Oui, il apparaît même si la connexion peut être définie sur "esclave", si la requête elle-même n'a pas l' target => 'slave'option définie, elle s'exécutera toujours sur la connexion par défaut. Il est difficile de définir plus facilement la cible de connexion au query_alterniveau.
David Thomas