Hateburo: kazeburo hatenablog

SRE / 運用系小姑 / Goを書くPerl Monger

データベースのmasterとslaveの使い分けの話。2014年版

社内で少し話題になったので。

運用上の話はfujiwaraさんの

MySQLをmaster:slave=1:1構成にして参照をslaveに向けるのがなぜ良くないか - 酒日記 はてな支店
MySQLで参照の負荷分散を行うslaveは3台から構成するのがよいのでは - 酒日記 はてな支店

をみてください。

最近、新しくサービスができたり、新規機能でデータベースを追加する際には必ず全ての参照をmasterに向けてもらっています。理由は上記のエントリを読んでください。このような構成が取れるのはもちろん性能的にそれで問題ないからです。

新しいハードウェアに、設定されたMySQL、問題のないように書かれたSQLであれば、数千QPSは余裕に、また少し頑張れば数万QPSを一台で賄えます。なので大体のサービスはmaster一台で十分です。

さらにこの考え方を進めて、Webアプリケーションの中で

sub dbh {
    DBI->connect("MASTER");
}

sub dbh_slave {
    my $dbh;
    for my $dsn ([MASTER]) { #masterを使ってる
        $dbh = DBI->connect($dsn);
        last if $dbh;
    }
}

sub controller_post {
    $c->dbh->do("insert ..")
}

sub controller_get {
    $c->dbh_slave->selectrow..("select ..")
}

のようにしているところのdbs_slaveが要らないんじゃないかと考えています。dbmとdbs_slaveの使い分けを考えてコード書くの、ちゃんとやると面倒ですよね。

sub dbh {
    DBI->connect("MASTER");
}

sub controller_post {
    $c->dbh->do("insert ..")
}

sub controller_get {
    $c->dbh->selectrow..("select ..")
}

シンプルになるのでサービスの構築も速くなるでしょう。

んで、サービスがかなりヒットして、master一台ではつらいという嬉しい悲鳴があがった時に、初めてdbs_slaveを足して、「スイートスポットだけ」をslave参照にします。

sub dbh {
    DBI->connect("MASTER");
}

sub dbh_slave {
    my $dbh;
    for my $dsn ([SLAVES]) {
        $dbh = DBI->connect($dsn);
        last if $dbh;
    }
}

sub controller_post {
    $c->dbh->do("insert ..")
}

sub controller_get {
    # ここアクセスが多いからslaveを参照する!
    $c->dbh_slave->selectrow..("select ..")
}

実際どこをslave参照にするかは、ちゃんと計測して行うといいですね。

また、こんな感じでシステムを作る事で、参照をslaveに向けた時に起きるであろう、レプリケーション遅延による様々な問題も回避しやすいんじゃないかなと思います。