GENIEEエンジニアブログ

株式会社ジーニーのエンジニアがアドテクノロジー・マーケティングテクノロジーなどについて情報発信するブログです。

MySQL接続数の削減

はじめに

こんにちは、R&D本部アドプラットフォーム開発部の村岡です。

私は、ジーニーでGenieeSSPの開発を主に行っています。

今回、私が入社前のインターンで行った開発内容について紹介したいと思います。

GenieeSSP

SSPは、ブラウザなどからのリクエストを処理し、レスポンスとして広告を返します。 当然ですが、メディアごとにサーバを用意するわけではないため、複数のメディアから来る膨大な広告リクエストを、短時間のうちに処理し広告を返す必要があります。

リクエストがきてから、レスポンスを返せるまでの時間は短ければ短いほどよいのですが、膨大なリクエスト数となるため、システムのパフォーマンスが非常に重要になってきます。

GenieeSSPはそういったSSP特有のパフォーマンス要求のため、C++で開発されており、現在16台のサーバで全リクエストを処理しています。

GenieeSSPでは、広告枠の情報などはMySQLに保存されていますが、広告リクエストごとにDBに枠情報を問い合わせていたのでは、広告を返すまでのレスポンスタイムが長くなってしまうため、全ての枠情報はオンメモリで保持されています。 これらの広告枠情報は、更新のあった枠に対してだけ一定時間ごとにDBにクエリを投げ、メモリ上の枠情報を更新するようになっています。

また、GenieeSSPはlibmysqlclientを直接使用してクエリを投げています。

課題

私がインターンとして入ったとき、GenieeSSPは1プロセスで使用するMySQLコネクション数が多すぎるという問題を抱えていました。

システムの機能拡張を繰り返した結果、MySQLからデータをフェッチする順序が複雑に絡まり、一本のMySQLコネクションでクエリを投げることが困難な状態にありました。

GenieeSSPでは、プリペアドステートメントを使用して全てのクエリを投げています。

この場合、MySQLでデータをフェッチするためには下図の順序でライブラリ関数を呼び出す必要があります。 この順序を守らないと、CR_COMMANDS_OUT_OF_SYNCエラーとなってしまいます。

f:id:tech-geniee:20170808175326p:plain

GenieeSSPにおいて、プロセスが大量のMySQLコネクションを使用する原因は、mysql_stmt_fetch関数を呼び出した後、別のMySQLコネクションとプリペアドステートメントmysql_stmt_execute関数を呼び出すことがあるためです。

一本のMySQLコネクションで、CR_COMMANDS_OUT_OF_SYNCエラーを回避するためには、上図のようにmysql_stmt_execute関数を呼び出したあとはMYSQL_NO_DATAとなるまでmysql_stmt_fetch関数を繰り返し呼び出す必要があります。

調査

ライブラリ関数の呼び出し順序を調査するため、下記のコードを使います。

static int
mysql_stmt_execute0(MYSQL_STMT *stmt, const char *func, int line)
{
    std::cerr << func <<  ":" << line << " execute" << std::endl;
    return mysql_stmt_execute(stmt);
}
#define mysql_stmt_execute(stmt) mysql_stmt_execute0((stmt), __func__, __LINE__)

static int
mysql_stmt_fetch0(MYSQL_STMT *stmt, const char *func, int line)
{
    std::cerr << func <<  ":" << line << " fetch";
    int ret = mysql_stmt_fetch(stmt);
    if (ret == MYSQL_NO_DATA)
    {
        std::cerr << " done";
    }
    std::cerr << std::endl;
    return ret;
}
#define mysql_stmt_fetch(stmt) mysql_stmt_fetch0((stmt), __func__, __LINE__)

このコードを挿入し、テスト環境で実行すると標準エラー出力mysql_stmt_execute関数とmysql_stmt_fetch関数を呼び出した関数名と 呼び出し箇所のソースコードの行数が出力されます。 この出力を元に、呼び出し順序が正しいか調査します、が、呼び出し順が間違っていれば実行途中でエラーとなるため、直前の出力を調べれば原因は大体わかります。

原因がわかった後は、正しいライブラリ関数の呼び出し順序となるようにソースコードを修正するだけです。

修正後

修正後、副次的な効果として全ての広告枠情報を取得する時間が2割減となりました。

また、コネクション数が減ったため、MySQLサーバも安定して動作するようになったような気がします。

f:id:tech-geniee:20170817135838p:plain

おわりに

今回、色々あってMySQL接続数を減らすことになりましたが、日々機能拡張が続くソフトウェアをメンテナンスしていくのは難しく、時間が取れず改善課題は溜まる一方です。

当記事を読んで、弊社とSSP開発に興味を持って頂ければ幸いです。

ありがとうございました。