Managing Tech

Managing Technology, from the trenches

Infos

Im privaten Blog von Jan Miczaika geht es um die Arbeit mit Technologien, in Teams, und das insbesondere im Startup-Umfeld. Kontakt?
    jan -at- hitflip.de oder bei XING

Wie in MySQL Skalierung Teil 1 versprochen hier noch etwas Code sowie eine tiefere Einführung.

Am Anfang werden zwei Datenbank-Verbindungen eingerichtet (aber noch nicht aufgebaut, das macht der lazy loader vom Zend Framework):

$DB_WRITE=Zend_Db::factory('Pdo_Mysql',$CFG_SYS_DB_WRITE);
$DB_READ=Hits_Db::chooseReadDb($CFG_SYS_DB_READ);

Die Funktion Hits_Db::chooseReadDb sucht aus einem Array einen zufälligen Slave aus. Das array sieht etwa so aus:

$CFG_SYS_DB_READ = array(
 array(
   'id' => 'a',
   'conn_settings' =>
    array(
     'host' => 'h',
     'username' => 'u',
     'password' => 'p',
     'port' => '1',
     'dbname' => 'd',
    ),
    'weight' => 50),
   'id' => 'a',
   'conn_settings' =>
    array(
     'host' => 'h',
     'username' => 'u',
     'password' => 'p',
     'port' => '1',
     'dbname' => 'd',
    ),
    'weight' => 50),
);

Daraus sucht die Funktion einen Slave raus, gewichtet nach weight. Wenn der erste Slave nicht antwortet wird der nächste probiert usw.


$dCountServers = count($aDbs);
if($dCountServers > 0)
{
  $dMaxRand = 0;
  for($i = 0; $i < $dCountServers; $i++)
  {
   $dMaxRand += $aDbs[$i]['weight'];
  }
  $dTarget = mt_rand(1, $dMaxRand);
  $dSumWeight = 0;
  $dTargetIndex = 0;
  for($i = 0; $i < $dCountServers; $i++)
  {
   $dSumWeight += $aDbs[$i]['weight'];
   if ($dTarget <= $dSumWeight)
   {
    $dTargetIndex = $i;
    break;
   }
  }
  try
  {
   return Zend_Db::factory('Pdo_Mysql', $aDbs[$dTargetIndex]['conn_settings']);
  }
  catch(Zend_Db_Exception $e)
  {
   Hits_API::log($e->getMessage(),Zend_Log::ALERT);
   unset($aDbs[$dTargetIndex]);
   while($dCountServers > 1)
   {
    return self::chooseReadDb($aDbs);
   }
   throw $e;
  }
}
else
{
  Hits_API::log(’empty db-config array’, Zend_Log::ALERT);
  throw new Zend_Db_Exception(’empty db-config array’);
}

So, jetzt können wir eine Verbindung zum Master und eine zum Slave aufbauen.

Kurz zum Applikationsdesign: Alle unsere SQL-Abfragen sind in CRUD Services gekapselt. Die orientieren sich typischerweise an den Entitäten, also GetUnit, AddUnit, DeleteUnit usw.

Diese Services extenden jetzt zwei Standard-Klassen: Hits_Abstract_Service_Db_Reader oder Hits_Abstract_Service_Db_Writer. Diese extenden wiederum die allgemeine Db Klasse (die ganze API ist übrigens nicht mein Werk sondern von meinem Kollegen Youssef).

Wenn eine Query ausgeführt ist, landet die entweder beim Db_Writer oder Db_Reader. Über die Db_Writer landen natürlich alle Queries auf dem Master.

Im Db_Reader ist etwas mehr Logik. Wenn die Query „normal“ ist, landet die auf dem slave. Es gibt aber Sonderfälle.

Wenn die Query im Rahmen einer Datenbank-Transaktion ist ($bIsInTransaction) landet die immer auf dem master, auch wenn es eine Read Query ist. Transaktionen werden bei uns nicht „per Hand“ angestoßen, sondern über eine Funktion. Und genauso auch beendet. Die Applikation behält dann, ob gerade eine Transaktion offen ist.

Zusätzlich kann der Programmierer, der einen Read Service aufruft, den Datenbankhandler beeinflussen. Man kann natürlich keine Write Services auf den Slave umbiegen. Aber beim Aufruf des Services kann in den Params übergeben werden, ob dieser Service ausnahmsweise auf dem Master ausgeführt werden soll. Das machen wir bei manchen kritischen Abfragen, z.B. im Checkout.

So, ich hoffe das hilft dem einen oder anderen weiter. Die versprochenen Code-Beispiele konnte ich nicht wirklich unterbringen, das sind bei uns wirklich dutzende Klassen, der Blog-Post wäre unlesbar. Aber ich hoffe es hilft trotzdem. Und Fragen beantworte ich natürlich auch gerne, wie schon in Teil 1!

One Response to “Skalierung von mySQL-Datenbanken Teil 1b – Code Beispiele zur Verteilung von reads und writes”

  1. Hi,
    sehr interessante Infos, die man hier findet. Besonders der Artikel “HTML Optimierung für Mails – welche Programme werden benutzt?” hat mir gefallen.

    Freundliche Grüße
    Bernhard Babbe

    Bernhard Babbe

Leave a Reply